1/* 2 * $Id: gpio.c,v 1.1.1.1 2007/08/03 18:52:15 Exp $ 3 * by Greg Banks <gbanks@pocketpenguins.com> 4 * (c) 2000 PocketPenguins Inc 5 * 6 * GPIO pin support for HD64465 companion chip. 7 */ 8 9#include <linux/kernel.h> 10#include <linux/init.h> 11#include <linux/module.h> 12#include <linux/sched.h> 13#include <linux/ioport.h> 14#include <asm/io.h> 15#include <asm/hd64465/gpio.h> 16 17#define _PORTOF(portpin) (((portpin)>>3)&0x7) 18#define _PINOF(portpin) ((portpin)&0x7) 19 20/* Register addresses parametrised on port */ 21#define GPIO_CR(port) (HD64465_REG_GPACR+((port)<<1)) 22#define GPIO_DR(port) (HD64465_REG_GPADR+((port)<<1)) 23#define GPIO_ICR(port) (HD64465_REG_GPAICR+((port)<<1)) 24#define GPIO_ISR(port) (HD64465_REG_GPAISR+((port)<<1)) 25 26#define GPIO_NPORTS 5 27 28#define MODNAME "hd64465_gpio" 29 30EXPORT_SYMBOL(hd64465_gpio_configure); 31EXPORT_SYMBOL(hd64465_gpio_get_pin); 32EXPORT_SYMBOL(hd64465_gpio_get_port); 33EXPORT_SYMBOL(hd64465_gpio_register_irq); 34EXPORT_SYMBOL(hd64465_gpio_set_pin); 35EXPORT_SYMBOL(hd64465_gpio_set_port); 36EXPORT_SYMBOL(hd64465_gpio_unregister_irq); 37 38/* TODO: each port should be protected with a spinlock */ 39 40 41void hd64465_gpio_configure(int portpin, int direction) 42{ 43 unsigned short cr; 44 unsigned int shift = (_PINOF(portpin)<<1); 45 46 cr = inw(GPIO_CR(_PORTOF(portpin))); 47 cr &= ~(3<<shift); 48 cr |= direction<<shift; 49 outw(cr, GPIO_CR(_PORTOF(portpin))); 50} 51 52void hd64465_gpio_set_pin(int portpin, unsigned int value) 53{ 54 unsigned short d; 55 unsigned short mask = 1<<(_PINOF(portpin)); 56 57 d = inw(GPIO_DR(_PORTOF(portpin))); 58 if (value) 59 d |= mask; 60 else 61 d &= ~mask; 62 outw(d, GPIO_DR(_PORTOF(portpin))); 63} 64 65unsigned int hd64465_gpio_get_pin(int portpin) 66{ 67 return inw(GPIO_DR(_PORTOF(portpin))) & (1<<(_PINOF(portpin))); 68} 69 70/* TODO: for cleaner atomicity semantics, add a mask to this routine */ 71 72void hd64465_gpio_set_port(int port, unsigned int value) 73{ 74 outw(value, GPIO_DR(port)); 75} 76 77unsigned int hd64465_gpio_get_port(int port) 78{ 79 return inw(GPIO_DR(port)); 80} 81 82 83static struct { 84 void (*func)(int portpin, void *dev); 85 void *dev; 86} handlers[GPIO_NPORTS * 8]; 87 88static irqreturn_t hd64465_gpio_interrupt(int irq, void *dev) 89{ 90 unsigned short port, pin, isr, mask, portpin; 91 92 for (port=0 ; port<GPIO_NPORTS ; port++) { 93 isr = inw(GPIO_ISR(port)); 94 95 for (pin=0 ; pin<8 ; pin++) { 96 mask = 1<<pin; 97 if (isr & mask) { 98 portpin = (port<<3)|pin; 99 if (handlers[portpin].func != 0) 100 handlers[portpin].func(portpin, handlers[portpin].dev); 101 else 102 printk(KERN_NOTICE "unexpected GPIO interrupt, pin %c%d\n", 103 port+'A', (int)pin); 104 } 105 } 106 107 /* Write 1s back to ISR to clear it? That's what the manual says.. */ 108 outw(isr, GPIO_ISR(port)); 109 } 110 111 return IRQ_HANDLED; 112} 113 114void hd64465_gpio_register_irq(int portpin, int mode, 115 void (*handler)(int portpin, void *dev), void *dev) 116{ 117 unsigned long flags; 118 unsigned short icr, mask; 119 120 if (handler == 0) 121 return; 122 123 local_irq_save(flags); 124 125 handlers[portpin].func = handler; 126 handlers[portpin].dev = dev; 127 128 /* 129 * Configure Interrupt Control Register 130 */ 131 icr = inw(GPIO_ICR(_PORTOF(portpin))); 132 mask = (1<<_PINOF(portpin)); 133 134 /* unmask interrupt */ 135 icr &= ~mask; 136 137 /* set TS bit */ 138 mask <<= 8; 139 icr &= ~mask; 140 if (mode == HD64465_GPIO_RISING) 141 icr |= mask; 142 143 outw(icr, GPIO_ICR(_PORTOF(portpin))); 144 145 local_irq_restore(flags); 146} 147 148void hd64465_gpio_unregister_irq(int portpin) 149{ 150 unsigned long flags; 151 unsigned short icr; 152 153 local_irq_save(flags); 154 155 /* 156 * Configure Interrupt Control Register 157 */ 158 icr = inw(GPIO_ICR(_PORTOF(portpin))); 159 icr |= (1<<_PINOF(portpin)); /* mask interrupt */ 160 outw(icr, GPIO_ICR(_PORTOF(portpin))); 161 162 handlers[portpin].func = 0; 163 handlers[portpin].dev = 0; 164 165 local_irq_restore(flags); 166} 167 168static int __init hd64465_gpio_init(void) 169{ 170 if (!request_region(HD64465_REG_GPACR, 0x1000, MODNAME)) 171 return -EBUSY; 172 if (request_irq(HD64465_IRQ_GPIO, hd64465_gpio_interrupt, 173 IRQF_DISABLED, MODNAME, 0)) 174 goto out_irqfailed; 175 176 printk("HD64465 GPIO layer on irq %d\n", HD64465_IRQ_GPIO); 177 178 return 0; 179 180out_irqfailed: 181 release_region(HD64465_REG_GPACR, 0x1000); 182 183 return -EINVAL; 184} 185 186static void __exit hd64465_gpio_exit(void) 187{ 188 release_region(HD64465_REG_GPACR, 0x1000); 189 free_irq(HD64465_IRQ_GPIO, 0); 190} 191 192module_init(hd64465_gpio_init); 193module_exit(hd64465_gpio_exit); 194 195MODULE_LICENSE("GPL"); 196