diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 00:20:36 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 00:20:36 +0200 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sh/cchips/hd6446x/hd64465 | |
download | linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/sh/cchips/hd6446x/hd64465')
-rw-r--r-- | arch/sh/cchips/hd6446x/hd64465/Makefile | 6 | ||||
-rw-r--r-- | arch/sh/cchips/hd6446x/hd64465/gpio.c | 196 | ||||
-rw-r--r-- | arch/sh/cchips/hd6446x/hd64465/io.c | 216 | ||||
-rw-r--r-- | arch/sh/cchips/hd6446x/hd64465/setup.c | 202 |
4 files changed, 620 insertions, 0 deletions
diff --git a/arch/sh/cchips/hd6446x/hd64465/Makefile b/arch/sh/cchips/hd6446x/hd64465/Makefile new file mode 100644 index 000000000000..f66edcb52c5b --- /dev/null +++ b/arch/sh/cchips/hd6446x/hd64465/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the HD64465 +# + +obj-y := setup.o io.o gpio.o + diff --git a/arch/sh/cchips/hd6446x/hd64465/gpio.c b/arch/sh/cchips/hd6446x/hd64465/gpio.c new file mode 100644 index 000000000000..9785fdef868e --- /dev/null +++ b/arch/sh/cchips/hd6446x/hd64465/gpio.c @@ -0,0 +1,196 @@ +/* + * $Id: gpio.c,v 1.4 2003/05/19 22:24:18 lethal Exp $ + * by Greg Banks <gbanks@pocketpenguins.com> + * (c) 2000 PocketPenguins Inc + * + * GPIO pin support for HD64465 companion chip. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <asm/hd64465/gpio.h> + +#define _PORTOF(portpin) (((portpin)>>3)&0x7) +#define _PINOF(portpin) ((portpin)&0x7) + +/* Register addresses parametrised on port */ +#define GPIO_CR(port) (HD64465_REG_GPACR+((port)<<1)) +#define GPIO_DR(port) (HD64465_REG_GPADR+((port)<<1)) +#define GPIO_ICR(port) (HD64465_REG_GPAICR+((port)<<1)) +#define GPIO_ISR(port) (HD64465_REG_GPAISR+((port)<<1)) + +#define GPIO_NPORTS 5 + +#define MODNAME "hd64465_gpio" + +EXPORT_SYMBOL(hd64465_gpio_configure); +EXPORT_SYMBOL(hd64465_gpio_get_pin); +EXPORT_SYMBOL(hd64465_gpio_get_port); +EXPORT_SYMBOL(hd64465_gpio_register_irq); +EXPORT_SYMBOL(hd64465_gpio_set_pin); +EXPORT_SYMBOL(hd64465_gpio_set_port); +EXPORT_SYMBOL(hd64465_gpio_unregister_irq); + +/* TODO: each port should be protected with a spinlock */ + + +void hd64465_gpio_configure(int portpin, int direction) +{ + unsigned short cr; + unsigned int shift = (_PINOF(portpin)<<1); + + cr = inw(GPIO_CR(_PORTOF(portpin))); + cr &= ~(3<<shift); + cr |= direction<<shift; + outw(cr, GPIO_CR(_PORTOF(portpin))); +} + +void hd64465_gpio_set_pin(int portpin, unsigned int value) +{ + unsigned short d; + unsigned short mask = 1<<(_PINOF(portpin)); + + d = inw(GPIO_DR(_PORTOF(portpin))); + if (value) + d |= mask; + else + d &= ~mask; + outw(d, GPIO_DR(_PORTOF(portpin))); +} + +unsigned int hd64465_gpio_get_pin(int portpin) +{ + return inw(GPIO_DR(_PORTOF(portpin))) & (1<<(_PINOF(portpin))); +} + +/* TODO: for cleaner atomicity semantics, add a mask to this routine */ + +void hd64465_gpio_set_port(int port, unsigned int value) +{ + outw(value, GPIO_DR(port)); +} + +unsigned int hd64465_gpio_get_port(int port) +{ + return inw(GPIO_DR(port)); +} + + +static struct { + void (*func)(int portpin, void *dev); + void *dev; +} handlers[GPIO_NPORTS * 8]; + +static irqreturn_t hd64465_gpio_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + unsigned short port, pin, isr, mask, portpin; + + for (port=0 ; port<GPIO_NPORTS ; port++) { + isr = inw(GPIO_ISR(port)); + + for (pin=0 ; pin<8 ; pin++) { + mask = 1<<pin; + if (isr & mask) { + portpin = (port<<3)|pin; + if (handlers[portpin].func != 0) + handlers[portpin].func(portpin, handlers[portpin].dev); + else + printk(KERN_NOTICE "unexpected GPIO interrupt, pin %c%d\n", + port+'A', (int)pin); + } + } + + /* Write 1s back to ISR to clear it? That's what the manual says.. */ + outw(isr, GPIO_ISR(port)); + } + + return IRQ_HANDLED; +} + +void hd64465_gpio_register_irq(int portpin, int mode, + void (*handler)(int portpin, void *dev), void *dev) +{ + unsigned long flags; + unsigned short icr, mask; + + if (handler == 0) + return; + + local_irq_save(flags); + + handlers[portpin].func = handler; + handlers[portpin].dev = dev; + + /* + * Configure Interrupt Control Register + */ + icr = inw(GPIO_ICR(_PORTOF(portpin))); + mask = (1<<_PINOF(portpin)); + + /* unmask interrupt */ + icr &= ~mask; + + /* set TS bit */ + mask <<= 8; + icr &= ~mask; + if (mode == HD64465_GPIO_RISING) + icr |= mask; + + outw(icr, GPIO_ICR(_PORTOF(portpin))); + + local_irq_restore(flags); +} + +void hd64465_gpio_unregister_irq(int portpin) +{ + unsigned long flags; + unsigned short icr; + + local_irq_save(flags); + + /* + * Configure Interrupt Control Register + */ + icr = inw(GPIO_ICR(_PORTOF(portpin))); + icr |= (1<<_PINOF(portpin)); /* mask interrupt */ + outw(icr, GPIO_ICR(_PORTOF(portpin))); + + handlers[portpin].func = 0; + handlers[portpin].dev = 0; + + local_irq_restore(flags); +} + +static int __init hd64465_gpio_init(void) +{ + if (!request_region(HD64465_REG_GPACR, 0x1000, MODNAME)) + return -EBUSY; + if (request_irq(HD64465_IRQ_GPIO, hd64465_gpio_interrupt, + SA_INTERRUPT, MODNAME, 0)) + goto out_irqfailed; + + printk("HD64465 GPIO layer on irq %d\n", HD64465_IRQ_GPIO); + + return 0; + +out_irqfailed: + release_region(HD64465_REG_GPACR, 0x1000); + + return -EINVAL; +} + +static void __exit hd64465_gpio_exit(void) +{ + release_region(HD64465_REG_GPACR, 0x1000); + free_irq(HD64465_IRQ_GPIO, 0); +} + +module_init(hd64465_gpio_init); +module_exit(hd64465_gpio_exit); + +MODULE_LICENSE("GPL"); + diff --git a/arch/sh/cchips/hd6446x/hd64465/io.c b/arch/sh/cchips/hd6446x/hd64465/io.c new file mode 100644 index 000000000000..99ac709c550e --- /dev/null +++ b/arch/sh/cchips/hd6446x/hd64465/io.c @@ -0,0 +1,216 @@ +/* + * $Id: io.c,v 1.4 2003/08/03 03:05:10 lethal Exp $ + * by Greg Banks <gbanks@pocketpenguins.com> + * (c) 2000 PocketPenguins Inc + * + * Derived from io_hd64461.c, which bore the message: + * Copyright (C) 2000 YAEGASHI Takeshi + * + * Typical I/O routines for HD64465 system. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/hd64465/hd64465.h> + + +#define HD64465_DEBUG 0 + +#if HD64465_DEBUG +#define DPRINTK(args...) printk(args) +#define DIPRINTK(n, args...) if (hd64465_io_debug>(n)) printk(args) +#else +#define DPRINTK(args...) +#define DIPRINTK(n, args...) +#endif + + + +/* This is a hack suitable only for debugging IO port problems */ +int hd64465_io_debug; +EXPORT_SYMBOL(hd64465_io_debug); + +/* Low iomap maps port 0-1K to addresses in 8byte chunks */ +#define HD64465_IOMAP_LO_THRESH 0x400 +#define HD64465_IOMAP_LO_SHIFT 3 +#define HD64465_IOMAP_LO_MASK ((1<<HD64465_IOMAP_LO_SHIFT)-1) +#define HD64465_IOMAP_LO_NMAP (HD64465_IOMAP_LO_THRESH>>HD64465_IOMAP_LO_SHIFT) +static unsigned long hd64465_iomap_lo[HD64465_IOMAP_LO_NMAP]; +static unsigned char hd64465_iomap_lo_shift[HD64465_IOMAP_LO_NMAP]; + +/* High iomap maps port 1K-64K to addresses in 1K chunks */ +#define HD64465_IOMAP_HI_THRESH 0x10000 +#define HD64465_IOMAP_HI_SHIFT 10 +#define HD64465_IOMAP_HI_MASK ((1<<HD64465_IOMAP_HI_SHIFT)-1) +#define HD64465_IOMAP_HI_NMAP (HD64465_IOMAP_HI_THRESH>>HD64465_IOMAP_HI_SHIFT) +static unsigned long hd64465_iomap_hi[HD64465_IOMAP_HI_NMAP]; +static unsigned char hd64465_iomap_hi_shift[HD64465_IOMAP_HI_NMAP]; + +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + +#define PORT2ADDR(x) (sh_mv.mv_isa_port2addr(x)) + +void hd64465_port_map(unsigned short baseport, unsigned int nports, + unsigned long addr, unsigned char shift) +{ + unsigned int port, endport = baseport + nports; + + DPRINTK("hd64465_port_map(base=0x%04hx, n=0x%04hx, addr=0x%08lx,endport=0x%04x)\n", + baseport, nports, addr,endport); + + for (port = baseport ; + port < endport && port < HD64465_IOMAP_LO_THRESH ; + port += (1<<HD64465_IOMAP_LO_SHIFT)) { + DPRINTK(" maplo[0x%x] = 0x%08lx\n", port, addr); + hd64465_iomap_lo[port>>HD64465_IOMAP_LO_SHIFT] = addr; + hd64465_iomap_lo_shift[port>>HD64465_IOMAP_LO_SHIFT] = shift; + addr += (1<<(HD64465_IOMAP_LO_SHIFT)); + } + + for (port = MAX(baseport, HD64465_IOMAP_LO_THRESH) ; + port < endport && port < HD64465_IOMAP_HI_THRESH ; + port += (1<<HD64465_IOMAP_HI_SHIFT)) { + DPRINTK(" maphi[0x%x] = 0x%08lx\n", port, addr); + hd64465_iomap_hi[port>>HD64465_IOMAP_HI_SHIFT] = addr; + hd64465_iomap_hi_shift[port>>HD64465_IOMAP_HI_SHIFT] = shift; + addr += (1<<(HD64465_IOMAP_HI_SHIFT)); + } +} +EXPORT_SYMBOL(hd64465_port_map); + +void hd64465_port_unmap(unsigned short baseport, unsigned int nports) +{ + unsigned int port, endport = baseport + nports; + + DPRINTK("hd64465_port_unmap(base=0x%04hx, n=0x%04hx)\n", + baseport, nports); + + for (port = baseport ; + port < endport && port < HD64465_IOMAP_LO_THRESH ; + port += (1<<HD64465_IOMAP_LO_SHIFT)) { + hd64465_iomap_lo[port>>HD64465_IOMAP_LO_SHIFT] = 0; + } + + for (port = MAX(baseport, HD64465_IOMAP_LO_THRESH) ; + port < endport && port < HD64465_IOMAP_HI_THRESH ; + port += (1<<HD64465_IOMAP_HI_SHIFT)) { + hd64465_iomap_hi[port>>HD64465_IOMAP_HI_SHIFT] = 0; + } +} +EXPORT_SYMBOL(hd64465_port_unmap); + +unsigned long hd64465_isa_port2addr(unsigned long port) +{ + unsigned long addr = 0; + unsigned char shift; + + /* handle remapping of low IO ports */ + if (port < HD64465_IOMAP_LO_THRESH) { + addr = hd64465_iomap_lo[port >> HD64465_IOMAP_LO_SHIFT]; + shift = hd64465_iomap_lo_shift[port >> HD64465_IOMAP_LO_SHIFT]; + if (addr != 0) + addr += (port & HD64465_IOMAP_LO_MASK) << shift; + else + printk(KERN_NOTICE "io_hd64465: access to un-mapped port %lx\n", port); + } else if (port < HD64465_IOMAP_HI_THRESH) { + addr = hd64465_iomap_hi[port >> HD64465_IOMAP_HI_SHIFT]; + shift = hd64465_iomap_hi_shift[port >> HD64465_IOMAP_HI_SHIFT]; + if (addr != 0) + addr += (port & HD64465_IOMAP_HI_MASK) << shift; + else + printk(KERN_NOTICE "io_hd64465: access to un-mapped port %lx\n", port); + } + + /* HD64465 internal devices (0xb0000000) */ + else if (port < 0x20000) + addr = CONFIG_HD64465_IOBASE + port - 0x10000; + + /* Whole physical address space (0xa0000000) */ + else + addr = P2SEGADDR(port); + + DIPRINTK(2, "PORT2ADDR(0x%08lx) = 0x%08lx\n", port, addr); + + return addr; +} + +static inline void delay(void) +{ + ctrl_inw(0xa0000000); +} + +unsigned char hd64465_inb(unsigned long port) +{ + unsigned long addr = PORT2ADDR(port); + unsigned long b = (addr == 0 ? 0 : *(volatile unsigned char*)addr); + + DIPRINTK(0, "inb(%08lx) = %02x\n", addr, (unsigned)b); + return b; +} + +unsigned char hd64465_inb_p(unsigned long port) +{ + unsigned long v; + unsigned long addr = PORT2ADDR(port); + + v = (addr == 0 ? 0 : *(volatile unsigned char*)addr); + delay(); + DIPRINTK(0, "inb_p(%08lx) = %02x\n", addr, (unsigned)v); + return v; +} + +unsigned short hd64465_inw(unsigned long port) +{ + unsigned long addr = PORT2ADDR(port); + unsigned long b = (addr == 0 ? 0 : *(volatile unsigned short*)addr); + DIPRINTK(0, "inw(%08lx) = %04lx\n", addr, b); + return b; +} + +unsigned int hd64465_inl(unsigned long port) +{ + unsigned long addr = PORT2ADDR(port); + unsigned int b = (addr == 0 ? 0 : *(volatile unsigned long*)addr); + DIPRINTK(0, "inl(%08lx) = %08x\n", addr, b); + return b; +} + +void hd64465_outb(unsigned char b, unsigned long port) +{ + unsigned long addr = PORT2ADDR(port); + + DIPRINTK(0, "outb(%02x, %08lx)\n", (unsigned)b, addr); + if (addr != 0) + *(volatile unsigned char*)addr = b; +} + +void hd64465_outb_p(unsigned char b, unsigned long port) +{ + unsigned long addr = PORT2ADDR(port); + + DIPRINTK(0, "outb_p(%02x, %08lx)\n", (unsigned)b, addr); + if (addr != 0) + *(volatile unsigned char*)addr = b; + delay(); +} + +void hd64465_outw(unsigned short b, unsigned long port) +{ + unsigned long addr = PORT2ADDR(port); + DIPRINTK(0, "outw(%04x, %08lx)\n", (unsigned)b, addr); + if (addr != 0) + *(volatile unsigned short*)addr = b; +} + +void hd64465_outl(unsigned int b, unsigned long port) +{ + unsigned long addr = PORT2ADDR(port); + DIPRINTK(0, "outl(%08x, %08lx)\n", b, addr); + if (addr != 0) + *(volatile unsigned long*)addr = b; +} + diff --git a/arch/sh/cchips/hd6446x/hd64465/setup.c b/arch/sh/cchips/hd6446x/hd64465/setup.c new file mode 100644 index 000000000000..68e4c4e4283d --- /dev/null +++ b/arch/sh/cchips/hd6446x/hd64465/setup.c @@ -0,0 +1,202 @@ +/* + * $Id: setup.c,v 1.4 2003/08/03 03:05:10 lethal Exp $ + * + * Setup and IRQ handling code for the HD64465 companion chip. + * by Greg Banks <gbanks@pocketpenguins.com> + * Copyright (c) 2000 PocketPenguins Inc + * + * Derived from setup_hd64461.c which bore the message: + * Copyright (C) 2000 YAEGASHI Takeshi + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/irq.h> + +#include <asm/io.h> +#include <asm/irq.h> + +#include <asm/hd64465/hd64465.h> + +static void disable_hd64465_irq(unsigned int irq) +{ + unsigned long flags; + unsigned short nimr; + unsigned short mask = 1 << (irq - HD64465_IRQ_BASE); + + pr_debug("disable_hd64465_irq(%d): mask=%x\n", irq, mask); + local_irq_save(flags); + nimr = inw(HD64465_REG_NIMR); + nimr |= mask; + outw(nimr, HD64465_REG_NIMR); + local_irq_restore(flags); +} + + +static void enable_hd64465_irq(unsigned int irq) +{ + unsigned long flags; + unsigned short nimr; + unsigned short mask = 1 << (irq - HD64465_IRQ_BASE); + + pr_debug("enable_hd64465_irq(%d): mask=%x\n", irq, mask); + local_irq_save(flags); + nimr = inw(HD64465_REG_NIMR); + nimr &= ~mask; + outw(nimr, HD64465_REG_NIMR); + local_irq_restore(flags); +} + + +static void mask_and_ack_hd64465(unsigned int irq) +{ + disable_hd64465_irq(irq); +} + + +static void end_hd64465_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_hd64465_irq(irq); +} + + +static unsigned int startup_hd64465_irq(unsigned int irq) +{ + enable_hd64465_irq(irq); + return 0; +} + + +static void shutdown_hd64465_irq(unsigned int irq) +{ + disable_hd64465_irq(irq); +} + + +static struct hw_interrupt_type hd64465_irq_type = { + .typename = "HD64465-IRQ", + .startup = startup_hd64465_irq, + .shutdown = shutdown_hd64465_irq, + .enable = enable_hd64465_irq, + .disable = disable_hd64465_irq, + .ack = mask_and_ack_hd64465, + .end = end_hd64465_irq, +}; + + +static irqreturn_t hd64465_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + printk(KERN_INFO + "HD64465: spurious interrupt, nirr: 0x%x nimr: 0x%x\n", + inw(HD64465_REG_NIRR), inw(HD64465_REG_NIMR)); + + return IRQ_NONE; +} + + +/*====================================================*/ + +/* + * Support for a secondary IRQ demux step. This is necessary + * because the HD64465 presents a very thin interface to the + * PCMCIA bus; a lot of features (such as remapping interrupts) + * normally done in hardware by other PCMCIA host bridges is + * instead done in software. + */ +static struct +{ + int (*func)(int, void *); + void *dev; +} hd64465_demux[HD64465_IRQ_NUM]; + +void hd64465_register_irq_demux(int irq, + int (*demux)(int irq, void *dev), void *dev) +{ + hd64465_demux[irq - HD64465_IRQ_BASE].func = demux; + hd64465_demux[irq - HD64465_IRQ_BASE].dev = dev; +} +EXPORT_SYMBOL(hd64465_register_irq_demux); + +void hd64465_unregister_irq_demux(int irq) +{ + hd64465_demux[irq - HD64465_IRQ_BASE].func = 0; +} +EXPORT_SYMBOL(hd64465_unregister_irq_demux); + + + +int hd64465_irq_demux(int irq) +{ + if (irq == CONFIG_HD64465_IRQ) { + unsigned short i, bit; + unsigned short nirr = inw(HD64465_REG_NIRR); + unsigned short nimr = inw(HD64465_REG_NIMR); + + pr_debug("hd64465_irq_demux, nirr=%04x, nimr=%04x\n", nirr, nimr); + nirr &= ~nimr; + for (bit = 1, i = 0 ; i < HD64465_IRQ_NUM ; bit <<= 1, i++) + if (nirr & bit) + break; + + if (i < HD64465_IRQ_NUM) { + irq = HD64465_IRQ_BASE + i; + if (hd64465_demux[i].func != 0) + irq = hd64465_demux[i].func(irq, hd64465_demux[i].dev); + } + } + return irq; +} + +static struct irqaction irq0 = { hd64465_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "HD64465", NULL, NULL}; + + +static int __init setup_hd64465(void) +{ + int i; + unsigned short rev; + unsigned short smscr; + + if (!MACH_HD64465) + return 0; + + printk(KERN_INFO "HD64465 configured at 0x%x on irq %d(mapped into %d to %d)\n", + CONFIG_HD64465_IOBASE, + CONFIG_HD64465_IRQ, + HD64465_IRQ_BASE, + HD64465_IRQ_BASE+HD64465_IRQ_NUM-1); + + if (inw(HD64465_REG_SDID) != HD64465_SDID) { + printk(KERN_ERR "HD64465 device ID not found, check base address\n"); + } + + rev = inw(HD64465_REG_SRR); + printk(KERN_INFO "HD64465 hardware revision %d.%d\n", (rev >> 8) & 0xff, rev & 0xff); + + outw(0xffff, HD64465_REG_NIMR); /* mask all interrupts */ + + for (i = 0; i < HD64465_IRQ_NUM ; i++) { + irq_desc[HD64465_IRQ_BASE + i].handler = &hd64465_irq_type; + } + + setup_irq(CONFIG_HD64465_IRQ, &irq0); + +#ifdef CONFIG_SERIAL + /* wake up the UART from STANDBY at this point */ + smscr = inw(HD64465_REG_SMSCR); + outw(smscr & (~HD64465_SMSCR_UARTST), HD64465_REG_SMSCR); + + /* remap IO ports for first ISA serial port to HD64465 UART */ + hd64465_port_map(0x3f8, 8, CONFIG_HD64465_IOBASE + 0x8000, 1); +#endif + + return 0; +} + +module_init(setup_hd64465); |