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