diff options
Diffstat (limited to 'arch/arm/mach-ebsa110')
-rw-r--r-- | arch/arm/mach-ebsa110/Makefile | 12 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/Makefile.boot | 4 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/core.c | 245 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/io.c | 378 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/leds.c | 51 |
5 files changed, 690 insertions, 0 deletions
diff --git a/arch/arm/mach-ebsa110/Makefile b/arch/arm/mach-ebsa110/Makefile new file mode 100644 index 000000000000..6520ac835802 --- /dev/null +++ b/arch/arm/mach-ebsa110/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the linux kernel. +# + +# Object file lists. + +obj-y := core.o io.o +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_LEDS) += leds.o diff --git a/arch/arm/mach-ebsa110/Makefile.boot b/arch/arm/mach-ebsa110/Makefile.boot new file mode 100644 index 000000000000..232126044935 --- /dev/null +++ b/arch/arm/mach-ebsa110/Makefile.boot @@ -0,0 +1,4 @@ + zreladdr-y := 0x00008000 +params_phys-y := 0x00000400 +initrd_phys-y := 0x00800000 + diff --git a/arch/arm/mach-ebsa110/core.c b/arch/arm/mach-ebsa110/core.c new file mode 100644 index 000000000000..ef362d44949d --- /dev/null +++ b/arch/arm/mach-ebsa110/core.c @@ -0,0 +1,245 @@ +/* + * linux/arch/arm/mach-ebsa110/core.c + * + * Copyright (C) 1998-2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Extra MM routines for the EBSA-110 architecture + */ +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/serial_8250.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/mach-types.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/system.h> + +#include <asm/mach/arch.h> +#include <asm/mach/irq.h> +#include <asm/mach/map.h> + +#include <asm/mach/time.h> + +#define IRQ_MASK 0xfe000000 /* read */ +#define IRQ_MSET 0xfe000000 /* write */ +#define IRQ_STAT 0xff000000 /* read */ +#define IRQ_MCLR 0xff000000 /* write */ + +static void ebsa110_mask_irq(unsigned int irq) +{ + __raw_writeb(1 << irq, IRQ_MCLR); +} + +static void ebsa110_unmask_irq(unsigned int irq) +{ + __raw_writeb(1 << irq, IRQ_MSET); +} + +static struct irqchip ebsa110_irq_chip = { + .ack = ebsa110_mask_irq, + .mask = ebsa110_mask_irq, + .unmask = ebsa110_unmask_irq, +}; + +static void __init ebsa110_init_irq(void) +{ + unsigned long flags; + unsigned int irq; + + local_irq_save(flags); + __raw_writeb(0xff, IRQ_MCLR); + __raw_writeb(0x55, IRQ_MSET); + __raw_writeb(0x00, IRQ_MSET); + if (__raw_readb(IRQ_MASK) != 0x55) + while (1); + __raw_writeb(0xff, IRQ_MCLR); /* clear all interrupt enables */ + local_irq_restore(flags); + + for (irq = 0; irq < NR_IRQS; irq++) { + set_irq_chip(irq, &ebsa110_irq_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } +} + +static struct map_desc ebsa110_io_desc[] __initdata = { + /* + * sparse external-decode ISAIO space + */ + { IRQ_STAT, TRICK4_PHYS, PGDIR_SIZE, MT_DEVICE }, /* IRQ_STAT/IRQ_MCLR */ + { IRQ_MASK, TRICK3_PHYS, PGDIR_SIZE, MT_DEVICE }, /* IRQ_MASK/IRQ_MSET */ + { SOFT_BASE, TRICK1_PHYS, PGDIR_SIZE, MT_DEVICE }, /* SOFT_BASE */ + { PIT_BASE, TRICK0_PHYS, PGDIR_SIZE, MT_DEVICE }, /* PIT_BASE */ + + /* + * self-decode ISAIO space + */ + { ISAIO_BASE, ISAIO_PHYS, ISAIO_SIZE, MT_DEVICE }, + { ISAMEM_BASE, ISAMEM_PHYS, ISAMEM_SIZE, MT_DEVICE } +}; + +static void __init ebsa110_map_io(void) +{ + iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc)); +} + + +#define PIT_CTRL (PIT_BASE + 0x0d) +#define PIT_T2 (PIT_BASE + 0x09) +#define PIT_T1 (PIT_BASE + 0x05) +#define PIT_T0 (PIT_BASE + 0x01) + +/* + * This is the rate at which your MCLK signal toggles (in Hz) + * This was measured on a 10 digit frequency counter sampling + * over 1 second. + */ +#define MCLK 47894000 + +/* + * This is the rate at which the PIT timers get clocked + */ +#define CLKBY7 (MCLK / 7) + +/* + * This is the counter value. We tick at 200Hz on this platform. + */ +#define COUNT ((CLKBY7 + (HZ / 2)) / HZ) + +/* + * Get the time offset from the system PIT. Note that if we have missed an + * interrupt, then the PIT counter will roll over (ie, be negative). + * This actually works out to be convenient. + */ +static unsigned long ebsa110_gettimeoffset(void) +{ + unsigned long offset, count; + + __raw_writeb(0x40, PIT_CTRL); + count = __raw_readb(PIT_T1); + count |= __raw_readb(PIT_T1) << 8; + + /* + * If count > COUNT, make the number negative. + */ + if (count > COUNT) + count |= 0xffff0000; + + offset = COUNT; + offset -= count; + + /* + * `offset' is in units of timer counts. Convert + * offset to units of microseconds. + */ + offset = offset * (1000000 / HZ) / COUNT; + + return offset; +} + +static irqreturn_t +ebsa110_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 count; + + write_seqlock(&xtime_lock); + + /* latch and read timer 1 */ + __raw_writeb(0x40, PIT_CTRL); + count = __raw_readb(PIT_T1); + count |= __raw_readb(PIT_T1) << 8; + + count += COUNT; + + __raw_writeb(count & 0xff, PIT_T1); + __raw_writeb(count >> 8, PIT_T1); + + timer_tick(regs); + + write_sequnlock(&xtime_lock); + + return IRQ_HANDLED; +} + +static struct irqaction ebsa110_timer_irq = { + .name = "EBSA110 Timer Tick", + .flags = SA_INTERRUPT, + .handler = ebsa110_timer_interrupt +}; + +/* + * Set up timer interrupt. + */ +static void __init ebsa110_timer_init(void) +{ + /* + * Timer 1, mode 2, LSB/MSB + */ + __raw_writeb(0x70, PIT_CTRL); + __raw_writeb(COUNT & 0xff, PIT_T1); + __raw_writeb(COUNT >> 8, PIT_T1); + + setup_irq(IRQ_EBSA110_TIMER0, &ebsa110_timer_irq); +} + +static struct sys_timer ebsa110_timer = { + .init = ebsa110_timer_init, + .offset = ebsa110_gettimeoffset, +}; + +static struct plat_serial8250_port serial_platform_data[] = { + { + .iobase = 0x3f8, + .irq = 1, + .uartclk = 1843200, + .regshift = 0, + .iotype = UPIO_PORT, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, + }, + { + .iobase = 0x2f8, + .irq = 2, + .uartclk = 1843200, + .regshift = 0, + .iotype = UPIO_PORT, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, + }, + { }, +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = serial_platform_data, + }, +}; + +static int __init ebsa110_init(void) +{ + return platform_device_register(&serial_device); +} + +arch_initcall(ebsa110_init); + +MACHINE_START(EBSA110, "EBSA110") + MAINTAINER("Russell King") + BOOT_MEM(0x00000000, 0xe0000000, 0xe0000000) + BOOT_PARAMS(0x00000400) + DISABLE_PARPORT(0) + DISABLE_PARPORT(2) + SOFT_REBOOT + MAPIO(ebsa110_map_io) + INITIRQ(ebsa110_init_irq) + .timer = &ebsa110_timer, +MACHINE_END diff --git a/arch/arm/mach-ebsa110/io.c b/arch/arm/mach-ebsa110/io.c new file mode 100644 index 000000000000..ef7eb5dc91bd --- /dev/null +++ b/arch/arm/mach-ebsa110/io.c @@ -0,0 +1,378 @@ +/* + * linux/arch/arm/mach-ebsa110/isamem.c + * + * Copyright (C) 2001 Russell King + * + * Perform "ISA" memory and IO accesses. The EBSA110 has some "peculiarities" + * in the way it handles accesses to odd IO ports on 16-bit devices. These + * devices have their D0-D15 lines connected to the processors D0-D15 lines. + * Since they expect all byte IO operations to be performed on D0-D7, and the + * StrongARM expects to transfer the byte to these odd addresses on D8-D15, + * we must use a trick to get the required behaviour. + * + * The trick employed here is to use long word stores to odd address -1. The + * glue logic picks this up as a "trick" access, and asserts the LSB of the + * peripherals address bus, thereby accessing the odd IO port. Meanwhile, the + * StrongARM transfers its data on D0-D7 as expected. + * + * Things get more interesting on the pass-1 EBSA110 - the PCMCIA controller + * wiring was screwed in such a way that it had limited memory space access. + * Luckily, the work-around for this is not too horrible. See + * __isamem_convert_addr for the details. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <asm/io.h> +#include <asm/page.h> + +static void __iomem *__isamem_convert_addr(void __iomem *addr) +{ + u32 ret, a = (u32 __force) addr; + + /* + * The PCMCIA controller is wired up as follows: + * +---------+---------+---------+---------+---------+---------+ + * PCMCIA | 2 2 2 2 | 1 1 1 1 | 1 1 1 1 | 1 1 | | | + * | 3 2 1 0 | 9 8 7 6 | 5 4 3 2 | 1 0 9 8 | 7 6 5 4 | 3 2 1 0 | + * +---------+---------+---------+---------+---------+---------+ + * CPU | 2 2 2 2 | 2 1 1 1 | 1 1 1 1 | 1 1 1 | | | + * | 4 3 2 1 | 0 9 9 8 | 7 6 5 4 | 3 2 0 9 | 8 7 6 5 | 4 3 2 x | + * +---------+---------+---------+---------+---------+---------+ + * + * This means that we can access PCMCIA regions as follows: + * 0x*10000 -> 0x*1ffff + * 0x*70000 -> 0x*7ffff + * 0x*90000 -> 0x*9ffff + * 0x*f0000 -> 0x*fffff + */ + ret = (a & 0xf803fe) << 1; + ret |= (a & 0x03fc00) << 2; + + ret += 0xe8000000; + + if ((a & 0x20000) == (a & 0x40000) >> 1) + return (void __iomem *)ret; + + BUG(); + return NULL; +} + +/* + * read[bwl] and write[bwl] + */ +u8 __readb(void __iomem *addr) +{ + void __iomem *a = __isamem_convert_addr(addr); + u32 ret; + + if ((unsigned long)addr & 1) + ret = __raw_readl(a); + else + ret = __raw_readb(a); + return ret; +} + +u16 __readw(void __iomem *addr) +{ + void __iomem *a = __isamem_convert_addr(addr); + + if ((unsigned long)addr & 1) + BUG(); + + return __raw_readw(a); +} + +u32 __readl(void __iomem *addr) +{ + void __iomem *a = __isamem_convert_addr(addr); + u32 ret; + + if ((unsigned long)addr & 3) + BUG(); + + ret = __raw_readw(a); + ret |= __raw_readw(a + 4) << 16; + return ret; +} + +EXPORT_SYMBOL(__readb); +EXPORT_SYMBOL(__readw); +EXPORT_SYMBOL(__readl); + +void __writeb(u8 val, void __iomem *addr) +{ + void __iomem *a = __isamem_convert_addr(addr); + + if ((unsigned long)addr & 1) + __raw_writel(val, a); + else + __raw_writeb(val, a); +} + +void __writew(u16 val, void __iomem *addr) +{ + void __iomem *a = __isamem_convert_addr(addr); + + if ((unsigned long)addr & 1) + BUG(); + + __raw_writew(val, a); +} + +void __writel(u32 val, void __iomem *addr) +{ + void __iomem *a = __isamem_convert_addr(addr); + + if ((unsigned long)addr & 3) + BUG(); + + __raw_writew(val, a); + __raw_writew(val >> 16, a + 4); +} + +EXPORT_SYMBOL(__writeb); +EXPORT_SYMBOL(__writew); +EXPORT_SYMBOL(__writel); + +#define SUPERIO_PORT(p) \ + (((p) >> 3) == (0x3f8 >> 3) || \ + ((p) >> 3) == (0x2f8 >> 3) || \ + ((p) >> 3) == (0x378 >> 3)) + +/* + * We're addressing an 8 or 16-bit peripheral which tranfers + * odd addresses on the low ISA byte lane. + */ +u8 __inb8(unsigned int port) +{ + u32 ret; + + /* + * The SuperIO registers use sane addressing techniques... + */ + if (SUPERIO_PORT(port)) + ret = __raw_readb((void __iomem *)ISAIO_BASE + (port << 2)); + else { + void __iomem *a = (void __iomem *)ISAIO_BASE + ((port & ~1) << 1); + + /* + * Shame nothing else does + */ + if (port & 1) + ret = __raw_readl(a); + else + ret = __raw_readb(a); + } + return ret; +} + +/* + * We're addressing a 16-bit peripheral which transfers odd + * addresses on the high ISA byte lane. + */ +u8 __inb16(unsigned int port) +{ + unsigned int offset; + + /* + * The SuperIO registers use sane addressing techniques... + */ + if (SUPERIO_PORT(port)) + offset = port << 2; + else + offset = (port & ~1) << 1 | (port & 1); + + return __raw_readb((void __iomem *)ISAIO_BASE + offset); +} + +u16 __inw(unsigned int port) +{ + unsigned int offset; + + /* + * The SuperIO registers use sane addressing techniques... + */ + if (SUPERIO_PORT(port)) + offset = port << 2; + else { + offset = port << 1; + BUG_ON(port & 1); + } + return __raw_readw((void __iomem *)ISAIO_BASE + offset); +} + +/* + * Fake a 32-bit read with two 16-bit reads. Needed for 3c589. + */ +u32 __inl(unsigned int port) +{ + void __iomem *a; + + if (SUPERIO_PORT(port) || port & 3) + BUG(); + + a = (void __iomem *)ISAIO_BASE + ((port & ~1) << 1); + + return __raw_readw(a) | __raw_readw(a + 4) << 16; +} + +EXPORT_SYMBOL(__inb8); +EXPORT_SYMBOL(__inb16); +EXPORT_SYMBOL(__inw); +EXPORT_SYMBOL(__inl); + +void __outb8(u8 val, unsigned int port) +{ + /* + * The SuperIO registers use sane addressing techniques... + */ + if (SUPERIO_PORT(port)) + __raw_writeb(val, (void __iomem *)ISAIO_BASE + (port << 2)); + else { + void __iomem *a = (void __iomem *)ISAIO_BASE + ((port & ~1) << 1); + + /* + * Shame nothing else does + */ + if (port & 1) + __raw_writel(val, a); + else + __raw_writeb(val, a); + } +} + +void __outb16(u8 val, unsigned int port) +{ + unsigned int offset; + + /* + * The SuperIO registers use sane addressing techniques... + */ + if (SUPERIO_PORT(port)) + offset = port << 2; + else + offset = (port & ~1) << 1 | (port & 1); + + __raw_writeb(val, (void __iomem *)ISAIO_BASE + offset); +} + +void __outw(u16 val, unsigned int port) +{ + unsigned int offset; + + /* + * The SuperIO registers use sane addressing techniques... + */ + if (SUPERIO_PORT(port)) + offset = port << 2; + else { + offset = port << 1; + BUG_ON(port & 1); + } + __raw_writew(val, (void __iomem *)ISAIO_BASE + offset); +} + +void __outl(u32 val, unsigned int port) +{ + BUG(); +} + +EXPORT_SYMBOL(__outb8); +EXPORT_SYMBOL(__outb16); +EXPORT_SYMBOL(__outw); +EXPORT_SYMBOL(__outl); + +void outsb(unsigned int port, const void *from, int len) +{ + u32 off; + + if (SUPERIO_PORT(port)) + off = port << 2; + else { + off = (port & ~1) << 1; + if (port & 1) + BUG(); + } + + __raw_writesb((void __iomem *)ISAIO_BASE + off, from, len); +} + +void insb(unsigned int port, void *from, int len) +{ + u32 off; + + if (SUPERIO_PORT(port)) + off = port << 2; + else { + off = (port & ~1) << 1; + if (port & 1) + BUG(); + } + + __raw_readsb((void __iomem *)ISAIO_BASE + off, from, len); +} + +EXPORT_SYMBOL(outsb); +EXPORT_SYMBOL(insb); + +void outsw(unsigned int port, const void *from, int len) +{ + u32 off; + + if (SUPERIO_PORT(port)) + off = port << 2; + else { + off = (port & ~1) << 1; + if (port & 1) + BUG(); + } + + __raw_writesw((void __iomem *)ISAIO_BASE + off, from, len); +} + +void insw(unsigned int port, void *from, int len) +{ + u32 off; + + if (SUPERIO_PORT(port)) + off = port << 2; + else { + off = (port & ~1) << 1; + if (port & 1) + BUG(); + } + + __raw_readsw((void __iomem *)ISAIO_BASE + off, from, len); +} + +EXPORT_SYMBOL(outsw); +EXPORT_SYMBOL(insw); + +/* + * We implement these as 16-bit insw/outsw, mainly for + * 3c589 cards. + */ +void outsl(unsigned int port, const void *from, int len) +{ + u32 off = port << 1; + + if (SUPERIO_PORT(port) || port & 3) + BUG(); + + __raw_writesw((void __iomem *)ISAIO_BASE + off, from, len << 1); +} + +void insl(unsigned int port, void *from, int len) +{ + u32 off = port << 1; + + if (SUPERIO_PORT(port) || port & 3) + BUG(); + + __raw_readsw((void __iomem *)ISAIO_BASE + off, from, len << 1); +} + +EXPORT_SYMBOL(outsl); +EXPORT_SYMBOL(insl); diff --git a/arch/arm/mach-ebsa110/leds.c b/arch/arm/mach-ebsa110/leds.c new file mode 100644 index 000000000000..3bc8c5e708e6 --- /dev/null +++ b/arch/arm/mach-ebsa110/leds.c @@ -0,0 +1,51 @@ +/* + * linux/arch/arm/mach-ebsa110/leds.c + * + * Copyright (C) 1998 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * EBSA-110 LED control routines. We use the led as follows: + * + * - Red - toggles state every 50 timer interrupts + */ +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.h> +#include <asm/mach-types.h> + +static spinlock_t leds_lock; + +static void ebsa110_leds_event(led_event_t ledevt) +{ + unsigned long flags; + + spin_lock_irqsave(&leds_lock, flags); + + switch(ledevt) { + case led_timer: + *(volatile unsigned char *)SOFT_BASE ^= 128; + break; + + default: + break; + } + + spin_unlock_irqrestore(&leds_lock, flags); +} + +static int __init leds_init(void) +{ + if (machine_is_ebsa110()) + leds_event = ebsa110_leds_event; + + return 0; +} + +__initcall(leds_init); |