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