diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /arch/arm/mach-ebsa110 | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/arm/mach-ebsa110')
-rw-r--r-- | arch/arm/mach-ebsa110/Makefile | 8 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/Makefile.boot | 5 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/core.c | 323 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/core.h | 38 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/include/mach/entry-macro.S | 33 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/include/mach/hardware.h | 21 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/include/mach/io.h | 89 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/include/mach/irqs.h | 17 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/include/mach/memory.h | 22 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/include/mach/uncompress.h | 41 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/io.c | 440 | ||||
-rw-r--r-- | arch/arm/mach-ebsa110/leds.c | 71 |
12 files changed, 1108 insertions, 0 deletions
diff --git a/arch/arm/mach-ebsa110/Makefile b/arch/arm/mach-ebsa110/Makefile new file mode 100644 index 000000000..296541315 --- /dev/null +++ b/arch/arm/mach-ebsa110/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the linux kernel. +# + +# Object file lists. + +obj-y := core.o io.o leds.o diff --git a/arch/arm/mach-ebsa110/Makefile.boot b/arch/arm/mach-ebsa110/Makefile.boot new file mode 100644 index 000000000..e7e98937c --- /dev/null +++ b/arch/arm/mach-ebsa110/Makefile.boot @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + 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 000000000..5960e3dfd --- /dev/null +++ b/arch/arm/mach-ebsa110/core.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-ebsa110/core.c + * + * Copyright (C) 1998-2001 Russell King + * + * 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 <linux/io.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <asm/setup.h> +#include <asm/mach-types.h> +#include <asm/page.h> +#include <asm/system_misc.h> + +#include <asm/mach/arch.h> +#include <asm/mach/irq.h> +#include <asm/mach/map.h> + +#include <asm/mach/time.h> + +#include "core.h" + +static void ebsa110_mask_irq(struct irq_data *d) +{ + __raw_writeb(1 << d->irq, IRQ_MCLR); +} + +static void ebsa110_unmask_irq(struct irq_data *d) +{ + __raw_writeb(1 << d->irq, IRQ_MSET); +} + +static struct irq_chip ebsa110_irq_chip = { + .irq_ack = ebsa110_mask_irq, + .irq_mask = ebsa110_mask_irq, + .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++) { + irq_set_chip_and_handler(irq, &ebsa110_irq_chip, + handle_level_irq); + irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); + } +} + +static struct map_desc ebsa110_io_desc[] __initdata = { + /* + * sparse external-decode ISAIO space + */ + { /* IRQ_STAT/IRQ_MCLR */ + .virtual = (unsigned long)IRQ_STAT, + .pfn = __phys_to_pfn(TRICK4_PHYS), + .length = TRICK4_SIZE, + .type = MT_DEVICE + }, { /* IRQ_MASK/IRQ_MSET */ + .virtual = (unsigned long)IRQ_MASK, + .pfn = __phys_to_pfn(TRICK3_PHYS), + .length = TRICK3_SIZE, + .type = MT_DEVICE + }, { /* SOFT_BASE */ + .virtual = (unsigned long)SOFT_BASE, + .pfn = __phys_to_pfn(TRICK1_PHYS), + .length = TRICK1_SIZE, + .type = MT_DEVICE + }, { /* PIT_BASE */ + .virtual = (unsigned long)PIT_BASE, + .pfn = __phys_to_pfn(TRICK0_PHYS), + .length = TRICK0_SIZE, + .type = MT_DEVICE + }, + + /* + * self-decode ISAIO space + */ + { + .virtual = ISAIO_BASE, + .pfn = __phys_to_pfn(ISAIO_PHYS), + .length = ISAIO_SIZE, + .type = MT_DEVICE + }, { + .virtual = ISAMEM_BASE, + .pfn = __phys_to_pfn(ISAMEM_PHYS), + .length = ISAMEM_SIZE, + .type = MT_DEVICE + } +}; + +static void __init ebsa110_map_io(void) +{ + iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc)); +} + +static void __iomem *ebsa110_ioremap_caller(phys_addr_t cookie, size_t size, + unsigned int flags, void *caller) +{ + return (void __iomem *)cookie; +} + +static void ebsa110_iounmap(volatile void __iomem *io_addr) +{} + +static void __init ebsa110_init_early(void) +{ + arch_ioremap_caller = ebsa110_ioremap_caller; + arch_iounmap = ebsa110_iounmap; +} + +#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 u32 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 * 1000; +} + +static irqreturn_t +ebsa110_timer_interrupt(int irq, void *dev_id) +{ + u32 count; + + /* 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(); + + return IRQ_HANDLED; +} + +/* + * Set up timer interrupt. + */ +void __init ebsa110_timer_init(void) +{ + int irq = IRQ_EBSA110_TIMER0; + + arch_gettimeoffset = ebsa110_gettimeoffset; + + /* + * Timer 1, mode 2, LSB/MSB + */ + __raw_writeb(0x70, PIT_CTRL); + __raw_writeb(COUNT & 0xff, PIT_T1); + __raw_writeb(COUNT >> 8, PIT_T1); + + if (request_irq(irq, ebsa110_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "EBSA110 Timer Tick", NULL)) + pr_err("Failed to request irq %d (EBSA110 Timer Tick)\n", irq); +} + +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 = PLAT8250_DEV_PLATFORM, + .dev = { + .platform_data = serial_platform_data, + }, +}; + +static struct resource am79c961_resources[] = { + { + .start = 0x220, + .end = 0x238, + .flags = IORESOURCE_IO, + }, { + .start = IRQ_EBSA110_ETHERNET, + .end = IRQ_EBSA110_ETHERNET, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device am79c961_device = { + .name = "am79c961", + .id = -1, + .num_resources = ARRAY_SIZE(am79c961_resources), + .resource = am79c961_resources, +}; + +static struct platform_device *ebsa110_devices[] = { + &serial_device, + &am79c961_device, +}; + +/* + * EBSA110 idling methodology: + * + * We can not execute the "wait for interrupt" instruction since that + * will stop our MCLK signal (which provides the clock for the glue + * logic, and therefore the timer interrupt). + * + * Instead, we spin, polling the IRQ_STAT register for the occurrence + * of any interrupt with core clock down to the memory clock. + */ +static void ebsa110_idle(void) +{ + const char *irq_stat = (char *)0xff000000; + + /* disable clock switching */ + asm volatile ("mcr p15, 0, ip, c15, c2, 2" : : : "cc"); + + /* wait for an interrupt to occur */ + while (!*irq_stat); + + /* enable clock switching */ + asm volatile ("mcr p15, 0, ip, c15, c1, 2" : : : "cc"); +} + +static int __init ebsa110_init(void) +{ + arm_pm_idle = ebsa110_idle; + return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices)); +} + +arch_initcall(ebsa110_init); + +static void ebsa110_restart(enum reboot_mode mode, const char *cmd) +{ + soft_restart(0x80000000); +} + +MACHINE_START(EBSA110, "EBSA110") + /* Maintainer: Russell King */ + .atag_offset = 0x400, + .reserve_lp0 = 1, + .reserve_lp2 = 1, + .map_io = ebsa110_map_io, + .init_early = ebsa110_init_early, + .init_irq = ebsa110_init_irq, + .init_time = ebsa110_timer_init, + .restart = ebsa110_restart, +MACHINE_END diff --git a/arch/arm/mach-ebsa110/core.h b/arch/arm/mach-ebsa110/core.h new file mode 100644 index 000000000..47acc610b --- /dev/null +++ b/arch/arm/mach-ebsa110/core.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 1996-2000 Russell King. + * + * This file contains the core hardware definitions of the EBSA-110. + */ +#ifndef CORE_H +#define CORE_H + +/* Physical addresses/sizes */ +#define ISAMEM_PHYS 0xe0000000 +#define ISAMEM_SIZE 0x10000000 + +#define ISAIO_PHYS 0xf0000000 +#define ISAIO_SIZE PGDIR_SIZE + +#define TRICK0_PHYS 0xf2000000 +#define TRICK0_SIZE PGDIR_SIZE +#define TRICK1_PHYS 0xf2400000 +#define TRICK1_SIZE PGDIR_SIZE +#define TRICK2_PHYS 0xf2800000 +#define TRICK3_PHYS 0xf2c00000 +#define TRICK3_SIZE PGDIR_SIZE +#define TRICK4_PHYS 0xf3000000 +#define TRICK4_SIZE PGDIR_SIZE +#define TRICK5_PHYS 0xf3400000 +#define TRICK6_PHYS 0xf3800000 +#define TRICK7_PHYS 0xf3c00000 + +/* Virtual addresses */ +#define PIT_BASE IOMEM(0xfc000000) /* trick 0 */ +#define SOFT_BASE IOMEM(0xfd000000) /* trick 1 */ +#define IRQ_MASK IOMEM(0xfe000000) /* trick 3 - read */ +#define IRQ_MSET IOMEM(0xfe000000) /* trick 3 - write */ +#define IRQ_STAT IOMEM(0xff000000) /* trick 4 - read */ +#define IRQ_MCLR IOMEM(0xff000000) /* trick 4 - write */ + +#endif diff --git a/arch/arm/mach-ebsa110/include/mach/entry-macro.S b/arch/arm/mach-ebsa110/include/mach/entry-macro.S new file mode 100644 index 000000000..14b110de7 --- /dev/null +++ b/arch/arm/mach-ebsa110/include/mach/entry-macro.S @@ -0,0 +1,33 @@ +/* + * arch/arm/mach-ebsa110/include/mach/entry-macro.S + * + * Low-level IRQ helper macros for ebsa110 platform. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + + + +#define IRQ_STAT 0xff000000 /* read */ + + .macro get_irqnr_preamble, base, tmp + mov \base, #IRQ_STAT + .endm + + .macro get_irqnr_and_base, irqnr, stat, base, tmp + ldrb \stat, [\base] @ get interrupts + mov \irqnr, #0 + tst \stat, #15 + addeq \irqnr, \irqnr, #4 + moveq \stat, \stat, lsr #4 + tst \stat, #3 + addeq \irqnr, \irqnr, #2 + moveq \stat, \stat, lsr #2 + tst \stat, #1 + addeq \irqnr, \irqnr, #1 + moveq \stat, \stat, lsr #1 + tst \stat, #1 @ bit 0 should be set + .endm + diff --git a/arch/arm/mach-ebsa110/include/mach/hardware.h b/arch/arm/mach-ebsa110/include/mach/hardware.h new file mode 100644 index 000000000..81f696768 --- /dev/null +++ b/arch/arm/mach-ebsa110/include/mach/hardware.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * arch/arm/mach-ebsa110/include/mach/hardware.h + * + * Copyright (C) 1996-2000 Russell King. + * + * This file contains the hardware definitions of the EBSA-110. + */ +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +#define ISAMEM_BASE 0xe0000000 +#define ISAIO_BASE 0xf0000000 + +/* + * RAM definitions + */ +#define UNCACHEABLE_ADDR 0xff000000 /* IRQ_STAT */ + +#endif + diff --git a/arch/arm/mach-ebsa110/include/mach/io.h b/arch/arm/mach-ebsa110/include/mach/io.h new file mode 100644 index 000000000..ad170886c --- /dev/null +++ b/arch/arm/mach-ebsa110/include/mach/io.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * arch/arm/mach-ebsa110/include/mach/io.h + * + * Copyright (C) 1997,1998 Russell King + * + * Modifications: + * 06-Dec-1997 RMK Created. + */ +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +u8 __inb8(unsigned int port); +void __outb8(u8 val, unsigned int port); + +u8 __inb16(unsigned int port); +void __outb16(u8 val, unsigned int port); + +u16 __inw(unsigned int port); +void __outw(u16 val, unsigned int port); + +u32 __inl(unsigned int port); +void __outl(u32 val, unsigned int port); + +u8 __readb(const volatile void __iomem *addr); +u16 __readw(const volatile void __iomem *addr); +u32 __readl(const volatile void __iomem *addr); + +void __writeb(u8 val, volatile void __iomem *addr); +void __writew(u16 val, volatile void __iomem *addr); +void __writel(u32 val, volatile void __iomem *addr); + +/* + * Argh, someone forgot the IOCS16 line. We therefore have to handle + * the byte stearing by selecting the correct byte IO functions here. + */ +#ifdef ISA_SIXTEEN_BIT_PERIPHERAL +#define inb(p) __inb16(p) +#define outb(v,p) __outb16(v,p) +#else +#define inb(p) __inb8(p) +#define outb(v,p) __outb8(v,p) +#endif + +#define inw(p) __inw(p) +#define outw(v,p) __outw(v,p) + +#define inl(p) __inl(p) +#define outl(v,p) __outl(v,p) + +#define readb(b) __readb(b) +#define readw(b) __readw(b) +#define readl(b) __readl(b) +#define readb_relaxed(addr) readb(addr) +#define readw_relaxed(addr) readw(addr) +#define readl_relaxed(addr) readl(addr) + +#define writeb(v,b) __writeb(v,b) +#define writew(v,b) __writew(v,b) +#define writel(v,b) __writel(v,b) + +#define insb insb +extern void insb(unsigned int port, void *buf, int sz); +#define insw insw +extern void insw(unsigned int port, void *buf, int sz); +#define insl insl +extern void insl(unsigned int port, void *buf, int sz); + +#define outsb outsb +extern void outsb(unsigned int port, const void *buf, int sz); +#define outsw outsw +extern void outsw(unsigned int port, const void *buf, int sz); +#define outsl outsl +extern void outsl(unsigned int port, const void *buf, int sz); + +/* can't support writesb atm */ +#define writesw writesw +extern void writesw(volatile void __iomem *addr, const void *data, int wordlen); +#define writesl writesl +extern void writesl(volatile void __iomem *addr, const void *data, int longlen); + +/* can't support readsb atm */ +#define readsw readsw +extern void readsw(const volatile void __iomem *addr, void *data, int wordlen); + +#define readsl readsl +extern void readsl(const volatile void __iomem *addr, void *data, int longlen); + +#endif diff --git a/arch/arm/mach-ebsa110/include/mach/irqs.h b/arch/arm/mach-ebsa110/include/mach/irqs.h new file mode 100644 index 000000000..29a8671fe --- /dev/null +++ b/arch/arm/mach-ebsa110/include/mach/irqs.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * arch/arm/mach-ebsa110/include/mach/irqs.h + * + * Copyright (C) 1996 Russell King + */ + +#define NR_IRQS 8 + +#define IRQ_EBSA110_PRINTER 0 +#define IRQ_EBSA110_COM1 1 +#define IRQ_EBSA110_COM2 2 +#define IRQ_EBSA110_ETHERNET 3 +#define IRQ_EBSA110_TIMER0 4 +#define IRQ_EBSA110_TIMER1 5 +#define IRQ_EBSA110_PCMCIA 6 +#define IRQ_EBSA110_IMMEDIATE 7 diff --git a/arch/arm/mach-ebsa110/include/mach/memory.h b/arch/arm/mach-ebsa110/include/mach/memory.h new file mode 100644 index 000000000..f025f405d --- /dev/null +++ b/arch/arm/mach-ebsa110/include/mach/memory.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * arch/arm/mach-ebsa110/include/mach/memory.h + * + * Copyright (C) 1996-1999 Russell King. + * + * Changelog: + * 20-Oct-1996 RMK Created + * 31-Dec-1997 RMK Fixed definitions to reduce warnings + * 21-Mar-1999 RMK Renamed to memory.h + * RMK Moved TASK_SIZE and PAGE_OFFSET here + */ +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +/* + * Cache flushing area - SRAM + */ +#define FLUSH_BASE_PHYS 0x40000000 +#define FLUSH_BASE 0xdf000000 + +#endif diff --git a/arch/arm/mach-ebsa110/include/mach/uncompress.h b/arch/arm/mach-ebsa110/include/mach/uncompress.h new file mode 100644 index 000000000..3ec12efe9 --- /dev/null +++ b/arch/arm/mach-ebsa110/include/mach/uncompress.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * arch/arm/mach-ebsa110/include/mach/uncompress.h + * + * Copyright (C) 1996,1997,1998 Russell King + */ + +#include <linux/serial_reg.h> + +#define SERIAL_BASE ((unsigned char *)0xf0000be0) + +/* + * This does not append a newline + */ +static inline void putc(int c) +{ + unsigned char v, *base = SERIAL_BASE; + + do { + v = base[UART_LSR << 2]; + barrier(); + } while (!(v & UART_LSR_THRE)); + + base[UART_TX << 2] = c; +} + +static inline void flush(void) +{ + unsigned char v, *base = SERIAL_BASE; + + do { + v = base[UART_LSR << 2]; + barrier(); + } while ((v & (UART_LSR_TEMT|UART_LSR_THRE)) != + (UART_LSR_TEMT|UART_LSR_THRE)); +} + +/* + * nothing to do + */ +#define arch_decomp_setup() diff --git a/arch/arm/mach-ebsa110/io.c b/arch/arm/mach-ebsa110/io.c new file mode 100644 index 000000000..3c44dd359 --- /dev/null +++ b/arch/arm/mach-ebsa110/io.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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 <linux/io.h> + +#include <mach/hardware.h> +#include <asm/page.h> + +static void __iomem *__isamem_convert_addr(const volatile 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(const volatile 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(const volatile void __iomem *addr) +{ + void __iomem *a = __isamem_convert_addr(addr); + + if ((unsigned long)addr & 1) + BUG(); + + return __raw_readw(a); +} + +u32 __readl(const volatile 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 readsw(const volatile void __iomem *addr, void *data, int len) +{ + void __iomem *a = __isamem_convert_addr(addr); + + BUG_ON((unsigned long)addr & 1); + + __raw_readsw(a, data, len); +} +EXPORT_SYMBOL(readsw); + +void readsl(const volatile void __iomem *addr, void *data, int len) +{ + void __iomem *a = __isamem_convert_addr(addr); + + BUG_ON((unsigned long)addr & 3); + + __raw_readsl(a, data, len); +} +EXPORT_SYMBOL(readsl); + +void __writeb(u8 val, volatile 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, volatile void __iomem *addr) +{ + void __iomem *a = __isamem_convert_addr(addr); + + if ((unsigned long)addr & 1) + BUG(); + + __raw_writew(val, a); +} + +void __writel(u32 val, volatile 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); + +void writesw(volatile void __iomem *addr, const void *data, int len) +{ + void __iomem *a = __isamem_convert_addr(addr); + + BUG_ON((unsigned long)addr & 1); + + __raw_writesw(a, data, len); +} +EXPORT_SYMBOL(writesw); + +void writesl(volatile void __iomem *addr, const void *data, int len) +{ + void __iomem *a = __isamem_convert_addr(addr); + + BUG_ON((unsigned long)addr & 3); + + __raw_writesl(a, data, len); +} +EXPORT_SYMBOL(writesl); + +/* + * The EBSA110 has a weird "ISA IO" region: + * + * Region 0 (addr = 0xf0000000 + io << 2) + * -------------------------------------------------------- + * Physical region IO region + * f0000fe0 - f0000ffc 3f8 - 3ff ttyS0 + * f0000e60 - f0000e64 398 - 399 + * f0000de0 - f0000dfc 378 - 37f lp0 + * f0000be0 - f0000bfc 2f8 - 2ff ttyS1 + * + * Region 1 (addr = 0xf0000000 + (io & ~1) << 1 + (io & 1)) + * -------------------------------------------------------- + * Physical region IO region + * f00014f1 a79 pnp write data + * f00007c0 - f00007c1 3e0 - 3e1 pcmcia + * f00004f1 279 pnp address + * f0000440 - f000046c 220 - 236 eth0 + * f0000405 203 pnp read data + */ +#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 000000000..fd1474b66 --- /dev/null +++ b/arch/arm/mach-ebsa110/leds.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the LED found on the EBSA110 machine + * Based on Versatile and RealView machine LED code + * + * Author: Bryan Wu <bryan.wu@canonical.com> + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/leds.h> + +#include <asm/mach-types.h> + +#include "core.h" + +#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS) +static void ebsa110_led_set(struct led_classdev *cdev, + enum led_brightness b) +{ + u8 reg = __raw_readb(SOFT_BASE); + + if (b != LED_OFF) + reg |= 0x80; + else + reg &= ~0x80; + + __raw_writeb(reg, SOFT_BASE); +} + +static enum led_brightness ebsa110_led_get(struct led_classdev *cdev) +{ + u8 reg = __raw_readb(SOFT_BASE); + + return (reg & 0x80) ? LED_FULL : LED_OFF; +} + +static int __init ebsa110_leds_init(void) +{ + + struct led_classdev *cdev; + int ret; + + if (!machine_is_ebsa110()) + return -ENODEV; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->name = "ebsa110:0"; + cdev->brightness_set = ebsa110_led_set; + cdev->brightness_get = ebsa110_led_get; + cdev->default_trigger = "heartbeat"; + + ret = led_classdev_register(NULL, cdev); + if (ret < 0) { + kfree(cdev); + return ret; + } + + return 0; +} + +/* + * Since we may have triggers on any subsystem, defer registration + * until after subsystem_init. + */ +fs_initcall(ebsa110_leds_init); +#endif |