18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/arch/arm/mach-ebsa110/core.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1998-2001 Russell King
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Extra MM routines for the EBSA-110 architecture
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/mm.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/serial_8250.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <mach/hardware.h>
178c2ecf20Sopenharmony_ci#include <asm/irq.h>
188c2ecf20Sopenharmony_ci#include <asm/setup.h>
198c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
208c2ecf20Sopenharmony_ci#include <asm/page.h>
218c2ecf20Sopenharmony_ci#include <asm/system_misc.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <asm/mach/arch.h>
248c2ecf20Sopenharmony_ci#include <asm/mach/irq.h>
258c2ecf20Sopenharmony_ci#include <asm/mach/map.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <asm/mach/time.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "core.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void ebsa110_mask_irq(struct irq_data *d)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	__raw_writeb(1 << d->irq, IRQ_MCLR);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void ebsa110_unmask_irq(struct irq_data *d)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	__raw_writeb(1 << d->irq, IRQ_MSET);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic struct irq_chip ebsa110_irq_chip = {
428c2ecf20Sopenharmony_ci	.irq_ack	= ebsa110_mask_irq,
438c2ecf20Sopenharmony_ci	.irq_mask	= ebsa110_mask_irq,
448c2ecf20Sopenharmony_ci	.irq_unmask	= ebsa110_unmask_irq,
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void __init ebsa110_init_irq(void)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	unsigned long flags;
508c2ecf20Sopenharmony_ci	unsigned int irq;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	local_irq_save(flags);
538c2ecf20Sopenharmony_ci	__raw_writeb(0xff, IRQ_MCLR);
548c2ecf20Sopenharmony_ci	__raw_writeb(0x55, IRQ_MSET);
558c2ecf20Sopenharmony_ci	__raw_writeb(0x00, IRQ_MSET);
568c2ecf20Sopenharmony_ci	if (__raw_readb(IRQ_MASK) != 0x55)
578c2ecf20Sopenharmony_ci		while (1);
588c2ecf20Sopenharmony_ci	__raw_writeb(0xff, IRQ_MCLR);	/* clear all interrupt enables */
598c2ecf20Sopenharmony_ci	local_irq_restore(flags);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	for (irq = 0; irq < NR_IRQS; irq++) {
628c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(irq, &ebsa110_irq_chip,
638c2ecf20Sopenharmony_ci					 handle_level_irq);
648c2ecf20Sopenharmony_ci		irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic struct map_desc ebsa110_io_desc[] __initdata = {
698c2ecf20Sopenharmony_ci	/*
708c2ecf20Sopenharmony_ci	 * sparse external-decode ISAIO space
718c2ecf20Sopenharmony_ci	 */
728c2ecf20Sopenharmony_ci	{	/* IRQ_STAT/IRQ_MCLR */
738c2ecf20Sopenharmony_ci		.virtual	= (unsigned long)IRQ_STAT,
748c2ecf20Sopenharmony_ci		.pfn		= __phys_to_pfn(TRICK4_PHYS),
758c2ecf20Sopenharmony_ci		.length		= TRICK4_SIZE,
768c2ecf20Sopenharmony_ci		.type		= MT_DEVICE
778c2ecf20Sopenharmony_ci	}, {	/* IRQ_MASK/IRQ_MSET */
788c2ecf20Sopenharmony_ci		.virtual	= (unsigned long)IRQ_MASK,
798c2ecf20Sopenharmony_ci		.pfn		= __phys_to_pfn(TRICK3_PHYS),
808c2ecf20Sopenharmony_ci		.length		= TRICK3_SIZE,
818c2ecf20Sopenharmony_ci		.type		= MT_DEVICE
828c2ecf20Sopenharmony_ci	}, {	/* SOFT_BASE */
838c2ecf20Sopenharmony_ci		.virtual	= (unsigned long)SOFT_BASE,
848c2ecf20Sopenharmony_ci		.pfn		= __phys_to_pfn(TRICK1_PHYS),
858c2ecf20Sopenharmony_ci		.length		= TRICK1_SIZE,
868c2ecf20Sopenharmony_ci		.type		= MT_DEVICE
878c2ecf20Sopenharmony_ci	}, {	/* PIT_BASE */
888c2ecf20Sopenharmony_ci		.virtual	= (unsigned long)PIT_BASE,
898c2ecf20Sopenharmony_ci		.pfn		= __phys_to_pfn(TRICK0_PHYS),
908c2ecf20Sopenharmony_ci		.length		= TRICK0_SIZE,
918c2ecf20Sopenharmony_ci		.type		= MT_DEVICE
928c2ecf20Sopenharmony_ci	},
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/*
958c2ecf20Sopenharmony_ci	 * self-decode ISAIO space
968c2ecf20Sopenharmony_ci	 */
978c2ecf20Sopenharmony_ci	{
988c2ecf20Sopenharmony_ci		.virtual	= ISAIO_BASE,
998c2ecf20Sopenharmony_ci		.pfn		= __phys_to_pfn(ISAIO_PHYS),
1008c2ecf20Sopenharmony_ci		.length		= ISAIO_SIZE,
1018c2ecf20Sopenharmony_ci		.type		= MT_DEVICE
1028c2ecf20Sopenharmony_ci	}, {
1038c2ecf20Sopenharmony_ci		.virtual	= ISAMEM_BASE,
1048c2ecf20Sopenharmony_ci		.pfn		= __phys_to_pfn(ISAMEM_PHYS),
1058c2ecf20Sopenharmony_ci		.length		= ISAMEM_SIZE,
1068c2ecf20Sopenharmony_ci		.type		= MT_DEVICE
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic void __init ebsa110_map_io(void)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc));
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void __iomem *ebsa110_ioremap_caller(phys_addr_t cookie, size_t size,
1168c2ecf20Sopenharmony_ci					    unsigned int flags, void *caller)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	return (void __iomem *)cookie;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void ebsa110_iounmap(volatile void __iomem *io_addr)
1228c2ecf20Sopenharmony_ci{}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic void __init ebsa110_init_early(void)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	arch_ioremap_caller = ebsa110_ioremap_caller;
1278c2ecf20Sopenharmony_ci	arch_iounmap = ebsa110_iounmap;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci#define PIT_CTRL		(PIT_BASE + 0x0d)
1318c2ecf20Sopenharmony_ci#define PIT_T2			(PIT_BASE + 0x09)
1328c2ecf20Sopenharmony_ci#define PIT_T1			(PIT_BASE + 0x05)
1338c2ecf20Sopenharmony_ci#define PIT_T0			(PIT_BASE + 0x01)
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/*
1368c2ecf20Sopenharmony_ci * This is the rate at which your MCLK signal toggles (in Hz)
1378c2ecf20Sopenharmony_ci * This was measured on a 10 digit frequency counter sampling
1388c2ecf20Sopenharmony_ci * over 1 second.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_ci#define MCLK	47894000
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/*
1438c2ecf20Sopenharmony_ci * This is the rate at which the PIT timers get clocked
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_ci#define CLKBY7	(MCLK / 7)
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/*
1488c2ecf20Sopenharmony_ci * This is the counter value.  We tick at 200Hz on this platform.
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_ci#define COUNT	((CLKBY7 + (HZ / 2)) / HZ)
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci * Get the time offset from the system PIT.  Note that if we have missed an
1548c2ecf20Sopenharmony_ci * interrupt, then the PIT counter will roll over (ie, be negative).
1558c2ecf20Sopenharmony_ci * This actually works out to be convenient.
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_cistatic u32 ebsa110_gettimeoffset(void)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	unsigned long offset, count;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	__raw_writeb(0x40, PIT_CTRL);
1628c2ecf20Sopenharmony_ci	count = __raw_readb(PIT_T1);
1638c2ecf20Sopenharmony_ci	count |= __raw_readb(PIT_T1) << 8;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/*
1668c2ecf20Sopenharmony_ci	 * If count > COUNT, make the number negative.
1678c2ecf20Sopenharmony_ci	 */
1688c2ecf20Sopenharmony_ci	if (count > COUNT)
1698c2ecf20Sopenharmony_ci		count |= 0xffff0000;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	offset = COUNT;
1728c2ecf20Sopenharmony_ci	offset -= count;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/*
1758c2ecf20Sopenharmony_ci	 * `offset' is in units of timer counts.  Convert
1768c2ecf20Sopenharmony_ci	 * offset to units of microseconds.
1778c2ecf20Sopenharmony_ci	 */
1788c2ecf20Sopenharmony_ci	offset = offset * (1000000 / HZ) / COUNT;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return offset * 1000;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic irqreturn_t
1848c2ecf20Sopenharmony_ciebsa110_timer_interrupt(int irq, void *dev_id)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	u32 count;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* latch and read timer 1 */
1898c2ecf20Sopenharmony_ci	__raw_writeb(0x40, PIT_CTRL);
1908c2ecf20Sopenharmony_ci	count = __raw_readb(PIT_T1);
1918c2ecf20Sopenharmony_ci	count |= __raw_readb(PIT_T1) << 8;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	count += COUNT;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	__raw_writeb(count & 0xff, PIT_T1);
1968c2ecf20Sopenharmony_ci	__raw_writeb(count >> 8, PIT_T1);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	timer_tick();
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/*
2048c2ecf20Sopenharmony_ci * Set up timer interrupt.
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_civoid __init ebsa110_timer_init(void)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	int irq = IRQ_EBSA110_TIMER0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	arch_gettimeoffset = ebsa110_gettimeoffset;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Timer 1, mode 2, LSB/MSB
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	__raw_writeb(0x70, PIT_CTRL);
2168c2ecf20Sopenharmony_ci	__raw_writeb(COUNT & 0xff, PIT_T1);
2178c2ecf20Sopenharmony_ci	__raw_writeb(COUNT >> 8, PIT_T1);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (request_irq(irq, ebsa110_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
2208c2ecf20Sopenharmony_ci			"EBSA110 Timer Tick", NULL))
2218c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (EBSA110 Timer Tick)\n", irq);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic struct plat_serial8250_port serial_platform_data[] = {
2258c2ecf20Sopenharmony_ci	{
2268c2ecf20Sopenharmony_ci		.iobase		= 0x3f8,
2278c2ecf20Sopenharmony_ci		.irq		= 1,
2288c2ecf20Sopenharmony_ci		.uartclk	= 1843200,
2298c2ecf20Sopenharmony_ci		.regshift	= 0,
2308c2ecf20Sopenharmony_ci		.iotype		= UPIO_PORT,
2318c2ecf20Sopenharmony_ci		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
2328c2ecf20Sopenharmony_ci	},
2338c2ecf20Sopenharmony_ci	{
2348c2ecf20Sopenharmony_ci		.iobase		= 0x2f8,
2358c2ecf20Sopenharmony_ci		.irq		= 2,
2368c2ecf20Sopenharmony_ci		.uartclk	= 1843200,
2378c2ecf20Sopenharmony_ci		.regshift	= 0,
2388c2ecf20Sopenharmony_ci		.iotype		= UPIO_PORT,
2398c2ecf20Sopenharmony_ci		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
2408c2ecf20Sopenharmony_ci	},
2418c2ecf20Sopenharmony_ci	{ },
2428c2ecf20Sopenharmony_ci};
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic struct platform_device serial_device = {
2458c2ecf20Sopenharmony_ci	.name			= "serial8250",
2468c2ecf20Sopenharmony_ci	.id			= PLAT8250_DEV_PLATFORM,
2478c2ecf20Sopenharmony_ci	.dev			= {
2488c2ecf20Sopenharmony_ci		.platform_data	= serial_platform_data,
2498c2ecf20Sopenharmony_ci	},
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic struct resource am79c961_resources[] = {
2538c2ecf20Sopenharmony_ci	{
2548c2ecf20Sopenharmony_ci		.start		= 0x220,
2558c2ecf20Sopenharmony_ci		.end		= 0x238,
2568c2ecf20Sopenharmony_ci		.flags		= IORESOURCE_IO,
2578c2ecf20Sopenharmony_ci	}, {
2588c2ecf20Sopenharmony_ci		.start		= IRQ_EBSA110_ETHERNET,
2598c2ecf20Sopenharmony_ci		.end		= IRQ_EBSA110_ETHERNET,
2608c2ecf20Sopenharmony_ci		.flags		= IORESOURCE_IRQ,
2618c2ecf20Sopenharmony_ci	},
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic struct platform_device am79c961_device = {
2658c2ecf20Sopenharmony_ci	.name			= "am79c961",
2668c2ecf20Sopenharmony_ci	.id			= -1,
2678c2ecf20Sopenharmony_ci	.num_resources		= ARRAY_SIZE(am79c961_resources),
2688c2ecf20Sopenharmony_ci	.resource		= am79c961_resources,
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic struct platform_device *ebsa110_devices[] = {
2728c2ecf20Sopenharmony_ci	&serial_device,
2738c2ecf20Sopenharmony_ci	&am79c961_device,
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/*
2778c2ecf20Sopenharmony_ci * EBSA110 idling methodology:
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci * We can not execute the "wait for interrupt" instruction since that
2808c2ecf20Sopenharmony_ci * will stop our MCLK signal (which provides the clock for the glue
2818c2ecf20Sopenharmony_ci * logic, and therefore the timer interrupt).
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * Instead, we spin, polling the IRQ_STAT register for the occurrence
2848c2ecf20Sopenharmony_ci * of any interrupt with core clock down to the memory clock.
2858c2ecf20Sopenharmony_ci */
2868c2ecf20Sopenharmony_cistatic void ebsa110_idle(void)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	const char *irq_stat = (char *)0xff000000;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* disable clock switching */
2918c2ecf20Sopenharmony_ci	asm volatile ("mcr p15, 0, ip, c15, c2, 2" : : : "cc");
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* wait for an interrupt to occur */
2948c2ecf20Sopenharmony_ci	while (!*irq_stat);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* enable clock switching */
2978c2ecf20Sopenharmony_ci	asm volatile ("mcr p15, 0, ip, c15, c1, 2" : : : "cc");
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int __init ebsa110_init(void)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	arm_pm_idle = ebsa110_idle;
3038c2ecf20Sopenharmony_ci	return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices));
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ciarch_initcall(ebsa110_init);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic void ebsa110_restart(enum reboot_mode mode, const char *cmd)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	soft_restart(0x80000000);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ciMACHINE_START(EBSA110, "EBSA110")
3148c2ecf20Sopenharmony_ci	/* Maintainer: Russell King */
3158c2ecf20Sopenharmony_ci	.atag_offset	= 0x400,
3168c2ecf20Sopenharmony_ci	.reserve_lp0	= 1,
3178c2ecf20Sopenharmony_ci	.reserve_lp2	= 1,
3188c2ecf20Sopenharmony_ci	.map_io		= ebsa110_map_io,
3198c2ecf20Sopenharmony_ci	.init_early	= ebsa110_init_early,
3208c2ecf20Sopenharmony_ci	.init_irq	= ebsa110_init_irq,
3218c2ecf20Sopenharmony_ci	.init_time	= ebsa110_timer_init,
3228c2ecf20Sopenharmony_ci	.restart	= ebsa110_restart,
3238c2ecf20Sopenharmony_ciMACHINE_END
324