1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * linux/arch/arm/mach-ebsa110/core.c 4 * 5 * Copyright (C) 1998-2001 Russell King 6 * 7 * Extra MM routines for the EBSA-110 architecture 8 */ 9 #include <linux/kernel.h> 10 #include <linux/mm.h> 11 #include <linux/interrupt.h> 12 #include <linux/serial_8250.h> 13 #include <linux/init.h> 14 #include <linux/io.h> 15 16 #include <mach/hardware.h> 17 #include <asm/irq.h> 18 #include <asm/setup.h> 19 #include <asm/mach-types.h> 20 #include <asm/pgtable.h> 21 #include <asm/page.h> 22 #include <asm/system_misc.h> 23 24 #include <asm/mach/arch.h> 25 #include <asm/mach/irq.h> 26 #include <asm/mach/map.h> 27 28 #include <asm/mach/time.h> 29 30 #include "core.h" 31 32 static void ebsa110_mask_irq(struct irq_data *d) 33 { 34 __raw_writeb(1 << d->irq, IRQ_MCLR); 35 } 36 37 static void ebsa110_unmask_irq(struct irq_data *d) 38 { 39 __raw_writeb(1 << d->irq, IRQ_MSET); 40 } 41 42 static struct irq_chip ebsa110_irq_chip = { 43 .irq_ack = ebsa110_mask_irq, 44 .irq_mask = ebsa110_mask_irq, 45 .irq_unmask = ebsa110_unmask_irq, 46 }; 47 48 static void __init ebsa110_init_irq(void) 49 { 50 unsigned long flags; 51 unsigned int irq; 52 53 local_irq_save(flags); 54 __raw_writeb(0xff, IRQ_MCLR); 55 __raw_writeb(0x55, IRQ_MSET); 56 __raw_writeb(0x00, IRQ_MSET); 57 if (__raw_readb(IRQ_MASK) != 0x55) 58 while (1); 59 __raw_writeb(0xff, IRQ_MCLR); /* clear all interrupt enables */ 60 local_irq_restore(flags); 61 62 for (irq = 0; irq < NR_IRQS; irq++) { 63 irq_set_chip_and_handler(irq, &ebsa110_irq_chip, 64 handle_level_irq); 65 irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); 66 } 67 } 68 69 static struct map_desc ebsa110_io_desc[] __initdata = { 70 /* 71 * sparse external-decode ISAIO space 72 */ 73 { /* IRQ_STAT/IRQ_MCLR */ 74 .virtual = (unsigned long)IRQ_STAT, 75 .pfn = __phys_to_pfn(TRICK4_PHYS), 76 .length = TRICK4_SIZE, 77 .type = MT_DEVICE 78 }, { /* IRQ_MASK/IRQ_MSET */ 79 .virtual = (unsigned long)IRQ_MASK, 80 .pfn = __phys_to_pfn(TRICK3_PHYS), 81 .length = TRICK3_SIZE, 82 .type = MT_DEVICE 83 }, { /* SOFT_BASE */ 84 .virtual = (unsigned long)SOFT_BASE, 85 .pfn = __phys_to_pfn(TRICK1_PHYS), 86 .length = TRICK1_SIZE, 87 .type = MT_DEVICE 88 }, { /* PIT_BASE */ 89 .virtual = (unsigned long)PIT_BASE, 90 .pfn = __phys_to_pfn(TRICK0_PHYS), 91 .length = TRICK0_SIZE, 92 .type = MT_DEVICE 93 }, 94 95 /* 96 * self-decode ISAIO space 97 */ 98 { 99 .virtual = ISAIO_BASE, 100 .pfn = __phys_to_pfn(ISAIO_PHYS), 101 .length = ISAIO_SIZE, 102 .type = MT_DEVICE 103 }, { 104 .virtual = ISAMEM_BASE, 105 .pfn = __phys_to_pfn(ISAMEM_PHYS), 106 .length = ISAMEM_SIZE, 107 .type = MT_DEVICE 108 } 109 }; 110 111 static void __init ebsa110_map_io(void) 112 { 113 iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc)); 114 } 115 116 static void __iomem *ebsa110_ioremap_caller(phys_addr_t cookie, size_t size, 117 unsigned int flags, void *caller) 118 { 119 return (void __iomem *)cookie; 120 } 121 122 static void ebsa110_iounmap(volatile void __iomem *io_addr) 123 {} 124 125 static void __init ebsa110_init_early(void) 126 { 127 arch_ioremap_caller = ebsa110_ioremap_caller; 128 arch_iounmap = ebsa110_iounmap; 129 } 130 131 #define PIT_CTRL (PIT_BASE + 0x0d) 132 #define PIT_T2 (PIT_BASE + 0x09) 133 #define PIT_T1 (PIT_BASE + 0x05) 134 #define PIT_T0 (PIT_BASE + 0x01) 135 136 /* 137 * This is the rate at which your MCLK signal toggles (in Hz) 138 * This was measured on a 10 digit frequency counter sampling 139 * over 1 second. 140 */ 141 #define MCLK 47894000 142 143 /* 144 * This is the rate at which the PIT timers get clocked 145 */ 146 #define CLKBY7 (MCLK / 7) 147 148 /* 149 * This is the counter value. We tick at 200Hz on this platform. 150 */ 151 #define COUNT ((CLKBY7 + (HZ / 2)) / HZ) 152 153 /* 154 * Get the time offset from the system PIT. Note that if we have missed an 155 * interrupt, then the PIT counter will roll over (ie, be negative). 156 * This actually works out to be convenient. 157 */ 158 static u32 ebsa110_gettimeoffset(void) 159 { 160 unsigned long offset, count; 161 162 __raw_writeb(0x40, PIT_CTRL); 163 count = __raw_readb(PIT_T1); 164 count |= __raw_readb(PIT_T1) << 8; 165 166 /* 167 * If count > COUNT, make the number negative. 168 */ 169 if (count > COUNT) 170 count |= 0xffff0000; 171 172 offset = COUNT; 173 offset -= count; 174 175 /* 176 * `offset' is in units of timer counts. Convert 177 * offset to units of microseconds. 178 */ 179 offset = offset * (1000000 / HZ) / COUNT; 180 181 return offset * 1000; 182 } 183 184 static irqreturn_t 185 ebsa110_timer_interrupt(int irq, void *dev_id) 186 { 187 u32 count; 188 189 /* latch and read timer 1 */ 190 __raw_writeb(0x40, PIT_CTRL); 191 count = __raw_readb(PIT_T1); 192 count |= __raw_readb(PIT_T1) << 8; 193 194 count += COUNT; 195 196 __raw_writeb(count & 0xff, PIT_T1); 197 __raw_writeb(count >> 8, PIT_T1); 198 199 timer_tick(); 200 201 return IRQ_HANDLED; 202 } 203 204 static struct irqaction ebsa110_timer_irq = { 205 .name = "EBSA110 Timer Tick", 206 .flags = IRQF_TIMER | IRQF_IRQPOLL, 207 .handler = ebsa110_timer_interrupt, 208 }; 209 210 /* 211 * Set up timer interrupt. 212 */ 213 void __init ebsa110_timer_init(void) 214 { 215 arch_gettimeoffset = ebsa110_gettimeoffset; 216 217 /* 218 * Timer 1, mode 2, LSB/MSB 219 */ 220 __raw_writeb(0x70, PIT_CTRL); 221 __raw_writeb(COUNT & 0xff, PIT_T1); 222 __raw_writeb(COUNT >> 8, PIT_T1); 223 224 setup_irq(IRQ_EBSA110_TIMER0, &ebsa110_timer_irq); 225 } 226 227 static struct plat_serial8250_port serial_platform_data[] = { 228 { 229 .iobase = 0x3f8, 230 .irq = 1, 231 .uartclk = 1843200, 232 .regshift = 0, 233 .iotype = UPIO_PORT, 234 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, 235 }, 236 { 237 .iobase = 0x2f8, 238 .irq = 2, 239 .uartclk = 1843200, 240 .regshift = 0, 241 .iotype = UPIO_PORT, 242 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, 243 }, 244 { }, 245 }; 246 247 static struct platform_device serial_device = { 248 .name = "serial8250", 249 .id = PLAT8250_DEV_PLATFORM, 250 .dev = { 251 .platform_data = serial_platform_data, 252 }, 253 }; 254 255 static struct resource am79c961_resources[] = { 256 { 257 .start = 0x220, 258 .end = 0x238, 259 .flags = IORESOURCE_IO, 260 }, { 261 .start = IRQ_EBSA110_ETHERNET, 262 .end = IRQ_EBSA110_ETHERNET, 263 .flags = IORESOURCE_IRQ, 264 }, 265 }; 266 267 static struct platform_device am79c961_device = { 268 .name = "am79c961", 269 .id = -1, 270 .num_resources = ARRAY_SIZE(am79c961_resources), 271 .resource = am79c961_resources, 272 }; 273 274 static struct platform_device *ebsa110_devices[] = { 275 &serial_device, 276 &am79c961_device, 277 }; 278 279 /* 280 * EBSA110 idling methodology: 281 * 282 * We can not execute the "wait for interrupt" instruction since that 283 * will stop our MCLK signal (which provides the clock for the glue 284 * logic, and therefore the timer interrupt). 285 * 286 * Instead, we spin, polling the IRQ_STAT register for the occurrence 287 * of any interrupt with core clock down to the memory clock. 288 */ 289 static void ebsa110_idle(void) 290 { 291 const char *irq_stat = (char *)0xff000000; 292 293 /* disable clock switching */ 294 asm volatile ("mcr p15, 0, ip, c15, c2, 2" : : : "cc"); 295 296 /* wait for an interrupt to occur */ 297 while (!*irq_stat); 298 299 /* enable clock switching */ 300 asm volatile ("mcr p15, 0, ip, c15, c1, 2" : : : "cc"); 301 } 302 303 static int __init ebsa110_init(void) 304 { 305 arm_pm_idle = ebsa110_idle; 306 return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices)); 307 } 308 309 arch_initcall(ebsa110_init); 310 311 static void ebsa110_restart(enum reboot_mode mode, const char *cmd) 312 { 313 soft_restart(0x80000000); 314 } 315 316 MACHINE_START(EBSA110, "EBSA110") 317 /* Maintainer: Russell King */ 318 .atag_offset = 0x400, 319 .reserve_lp0 = 1, 320 .reserve_lp2 = 1, 321 .map_io = ebsa110_map_io, 322 .init_early = ebsa110_init_early, 323 .init_irq = ebsa110_init_irq, 324 .init_time = ebsa110_timer_init, 325 .restart = ebsa110_restart, 326 MACHINE_END 327
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.