Index: linux-2.6.15gum/arch/arm/Kconfig =================================================================== --- linux-2.6.15gum.orig/arch/arm/Kconfig +++ linux-2.6.15gum/arch/arm/Kconfig @@ -316,6 +316,8 @@ config PCI_HOST_VIA82C505 depends on PCI && ARCH_SHARK default y +source "drivers/gpio/Kconfig" + source "drivers/pci/Kconfig" source "drivers/pcmcia/Kconfig" Index: linux-2.6.15gum/drivers/Makefile =================================================================== --- linux-2.6.15gum.orig/drivers/Makefile +++ linux-2.6.15gum/drivers/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_SGI_IOC4) += sn/ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ +obj-$(CONFIG_PROC_GPIO) += gpio/ Index: linux-2.6.15gum/drivers/gpio/Kconfig =================================================================== --- /dev/null +++ linux-2.6.15gum/drivers/gpio/Kconfig @@ -0,0 +1,6 @@ +config PROC_GPIO + tristate "GPIO /proc interface" + depends on PXA25x + help + This enables an interface under /proc/gpio which allows reading or setting + of any GPIO. Currently only reading is supported. Index: linux-2.6.15gum/drivers/gpio/Makefile =================================================================== --- /dev/null +++ linux-2.6.15gum/drivers/gpio/Makefile @@ -0,0 +1,3 @@ +# Expose GPIOs under /proc +obj-$(CONFIG_PROC_GPIO) += proc_gpio.o + Index: linux-2.6.15gum/drivers/gpio/proc_gpio.c =================================================================== --- /dev/null +++ linux-2.6.15gum/drivers/gpio/proc_gpio.c @@ -0,0 +1,355 @@ +/* + * + * PXA25x GPIOs exposed under /proc for reading and writing + * They will show up under /proc/gpio/NN + * + * Based on patch 1773/1 in the arm kernel patch repository at arm.linux.co.uk + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct proc_dir_entry *proc_gpio_parent; +static struct proc_dir_entry *proc_gpios[85]; + +typedef struct +{ + int gpio; + char name[32]; +} gpio_summary_type; + +static gpio_summary_type gpio_summaries[85] = +{ + { 0, "GPIO0" }, + { 1, "GPIO1" }, + { 2, "GPIO2" }, + { 3, "GPIO3" }, + { 4, "GPIO4" }, + { 5, "GPIO5" }, + { 6, "GPIO6" }, + { 7, "GPIO7" }, + { 8, "GPIO8" }, + { 9, "GPIO9" }, + { 10, "GPIO10" }, + { 11, "GPIO11" }, + { 12, "GPIO12" }, + { 13, "GPIO13" }, + { 14, "GPIO14" }, + { 15, "GPIO15" }, + { 16, "GPIO16" }, + { 17, "GPIO17" }, + { 18, "GPIO18" }, + { 19, "GPIO19" }, + { 20, "GPIO20" }, + { 21, "GPIO21" }, + { 22, "GPIO22" }, + { 23, "GPIO23" }, + { 24, "GPIO24" }, + { 25, "GPIO25" }, + { 26, "GPIO26" }, + { 27, "GPIO27" }, + { 28, "GPIO28" }, + { 29, "GPIO29" }, + { 30, "GPIO30" }, + { 31, "GPIO31" }, + { 32, "GPIO32" }, + { 33, "GPIO33" }, + { 34, "GPIO34" }, + { 35, "GPIO35" }, + { 36, "GPIO36" }, + { 37, "GPIO37" }, + { 38, "GPIO38" }, + { 39, "GPIO39" }, + { 40, "GPIO40" }, + { 41, "GPIO41" }, + { 42, "GPIO42" }, + { 43, "GPIO43" }, + { 44, "GPIO44" }, + { 45, "GPIO45" }, + { 46, "GPIO46" }, + { 47, "GPIO47" }, + { 48, "GPIO48" }, + { 49, "GPIO49" }, + { 50, "GPIO50" }, + { 51, "GPIO51" }, + { 52, "GPIO52" }, + { 53, "GPIO53" }, + { 54, "GPIO54" }, + { 55, "GPIO55" }, + { 56, "GPIO56" }, + { 57, "GPIO57" }, + { 58, "GPIO58" }, + { 59, "GPIO59" }, + { 60, "GPIO60" }, + { 61, "GPIO61" }, + { 62, "GPIO62" }, + { 63, "GPIO63" }, + { 64, "GPIO64" }, + { 65, "GPIO65" }, + { 66, "GPIO66" }, + { 67, "GPIO67" }, + { 68, "GPIO68" }, + { 69, "GPIO69" }, + { 70, "GPIO70" }, + { 71, "GPIO71" }, + { 72, "GPIO72" }, + { 73, "GPIO73" }, + { 74, "GPIO74" }, + { 75, "GPIO75" }, + { 76, "GPIO76" }, + { 77, "GPIO77" }, + { 78, "GPIO78" }, + { 79, "GPIO79" }, + { 80, "GPIO80" }, + { 81, "GPIO81" }, + { 82, "GPIO82" }, + { 83, "GPIO83" }, + { 84, "GPIO84" }, +}; + +static int proc_gpio_write(struct file *file, const char __user *buf, + unsigned long count, void *data) +{ + char *cur, lbuf[count + 1]; + gpio_summary_type *summary = data; + u32 altfn, direction, setclear, gafr; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + memset(lbuf, 0, count + 1); + + if (copy_from_user(lbuf, buf, count)) + return -EFAULT; + + cur = lbuf; + + // Initialize to current state + altfn = ((GAFR(summary->gpio) >> ((summary->gpio & 0x0f) << 0x01)) & 0x03); + direction = GPDR(summary->gpio) & GPIO_bit(summary->gpio); + setclear = GPLR(summary->gpio) & GPIO_bit(summary->gpio); + while(1) + { + // We accept options: {GPIO|AF1|AF2|AF3}, {set|clear}, {in|out} + // Anything else is an error + while(cur[0] && (isspace(cur[0]) || ispunct(cur[0]))) cur = &(cur[1]); + + if('\0' == cur[0]) break; + + // Ok, so now we're pointing at the start of something + switch(cur[0]) + { + case 'G': + // Check that next is "PIO" -- '\0' will cause safe short-circuit if end of buf + if(!(cur[1] == 'P' && cur[2] == 'I' && cur[3] == 'O')) goto parse_error; + // Ok, so set this GPIO to GPIO (non-ALT) function + altfn = 0; + cur = &(cur[4]); + break; + case 'A': + if(!(cur[1] == 'F' && cur[2] >= '1' && cur[2] <= '3')) goto parse_error; + altfn = cur[2] - '0'; + cur = &(cur[3]); + break; + case 's': + if(!(cur[1] == 'e' && cur[2] == 't')) goto parse_error; + setclear = 1; + cur = &(cur[3]); + break; + case 'c': + if(!(cur[1] == 'l' && cur[2] == 'e' && cur[3] == 'a' && cur[4] == 'r')) goto parse_error; + setclear = 0; + cur = &(cur[5]); + break; + case 'i': + if(!(cur[1] == 'n')) goto parse_error; + direction = 0; + cur = &(cur[2]); + break; + case 'o': + if(!(cur[1] == 'u' && cur[2] == 't')) goto parse_error; + direction = 1; + cur = &(cur[3]); + break; + default: goto parse_error; + } + } + // Ok, now set gpio mode and value + if(direction) + GPDR(summary->gpio) |= GPIO_bit(summary->gpio); + else + GPDR(summary->gpio) &= ~GPIO_bit(summary->gpio); + + gafr = GAFR(summary->gpio) & ~(0x3 << (((summary->gpio) & 0xf)*2)); + GAFR(summary->gpio) = gafr | (altfn << (((summary->gpio) & 0xf)*2)); + + if(direction && !altfn) + { + if(setclear) GPSR(summary->gpio) = GPIO_bit(summary->gpio); + else GPCR(summary->gpio) = GPIO_bit(summary->gpio); + } + + printk(KERN_INFO "Set (%s,%s,%s) via /proc/gpio/%s\n",altfn ? (altfn == 1 ? "AF1" : (altfn == 2 ? "AF2" : "AF3")) : "GPIO", + direction ? "out" : "in", + setclear ? "set" : "clear", + summary->name); + + return count; + +parse_error: + printk(KERN_CRIT "Parse error: Expect \"[GPIO|AF1|AF2|AF3]|[set|clear]|[in|out] ...\"\n"); + return -EINVAL; +} + +static int proc_gpio_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + gpio_summary_type *summary = data; + int len, i, af; + i = summary->gpio; + + p += sprintf(p, "%d\t%s\t%s\t%s\n", i, + (af = ((GAFR(i) >> ((i & 0x0f) << 0x01)) & 0x03)) ? (af == 1 ? "AF1" : (af == 2 ? "AF2" : "AF3")) : "GPIO", + (GPDR(i) & GPIO_bit(i)) ? "out" : "in", + (GPLR(i) & GPIO_bit(i)) ? "set" : "clear"); + + len = (p - page) - off; + + if(len < 0) + { + len = 0; + } + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static const char const *GAFR_DESC[] = { "GAFR0_L", "GAFR0_U", "GAFR1_L", "GAFR1_U", "GAFR2_L", "GAFR2_U" }; + +static int proc_gafr_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + int i, len; + + for(i=0; i<=5; i++) + { + p += sprintf(p, "%s: %08x\n", GAFR_DESC[i], GAFR(i*16)); + } + + len = (p - page) - off; + + if(len < 0) + { + len = 0; + } + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int proc_gpdr_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + int i, len; + + for(i=0; i<=2; i++) + { + p += sprintf(p, "GPDR%d: %08x\n", i, GPDR(i * 32)); + } + + len = (p - page) - off; + + if(len < 0) + { + len = 0; + } + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int proc_gplr_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + int i, len; + + for(i=0; i<=2; i++) + { + p += sprintf(p, "GPLR%d: %08x\n", i, GPLR(i * 32)); + } + + len = (p - page) - off; + + if(len < 0) + { + len = 0; + } + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int __init gpio_init(void) +{ + int i; + + proc_gpio_parent = create_proc_entry("gpio", S_IFDIR | S_IRUGO | S_IXUGO, NULL); + if(!proc_gpio_parent) return 0; + + for(i=0; i < 85; i++) + { + proc_gpios[i] = create_proc_entry(gpio_summaries[i].name, 0644, proc_gpio_parent); + if(proc_gpios[i]) + { + proc_gpios[i]->data = &gpio_summaries[i]; + proc_gpios[i]->read_proc = proc_gpio_read; + proc_gpios[i]->write_proc = proc_gpio_write; + } + } + + create_proc_read_entry("GAFR", 0444, proc_gpio_parent, proc_gafr_read, NULL); + create_proc_read_entry("GPDR", 0444, proc_gpio_parent, proc_gpdr_read, NULL); + create_proc_read_entry("GPLR", 0444, proc_gpio_parent, proc_gplr_read, NULL); + + return 0; +} + +static void gpio_exit(void) +{ + int i; + + remove_proc_entry("GAFR", proc_gpio_parent); + remove_proc_entry("GPDR", proc_gpio_parent); + remove_proc_entry("GPLR", proc_gpio_parent); + + for(i=0; i < 85; i++) + { + if(proc_gpios[i]) remove_proc_entry(gpio_summaries[i].name, proc_gpio_parent); + } + if(proc_gpio_parent) remove_proc_entry("gpio", NULL); +} + +module_init(gpio_init); +module_exit(gpio_exit); +MODULE_LICENSE("GPL");