--- /home/ph5/scm/hh.o/linux/kernel26/include/linux/mfd/htc-egpio.h 2007-09-16 00:23:04.000000000 +0200 +++ include/linux/mfd/htc-egpio.h 2007-11-24 21:51:37.000000000 +0100 @@ -5,56 +5,54 @@ #ifndef __HTC_EGPIO_H__ #define __HTC_EGPIO_H__ -enum { - /* Maximum number of 16 bit registers a chip may have. */ - MAX_EGPIO_REGS = 8, - /* Number of IRQS the EGPIO chip will claim. */ - MAX_EGPIO_IRQS = 16, -}; +#include -/* Available pin types. */ -enum { - /* This pin corresponds to an input gpio */ - HTC_EGPIO_TYPE_INPUT, - /* This pin corresponds to an output gpio */ - HTC_EGPIO_TYPE_OUTPUT, -}; +/* Descriptive values for all-in or all-out htc_egpio_chip descriptors. */ +#define HTC_EGPIO_OUTPUT (~0) +#define HTC_EGPIO_INPUT 0 -/* Information on each pin on the chip. */ -struct htc_egpio_pinInfo { - /* The gpio id of the pin (eg, gpio_base+18 is the third bit - * of the second register). */ - int gpio; - /* The type - input, irq, output */ - int type; - /* For output pins - the poweron default */ - int output_initial; - /* For input pins with a corresponding irq, the irqs bit offset. */ - int input_irq; +/** + * struct htc_egpio_chip - descriptor to create gpio_chip for register range + * @reg_start: index of first register + * @gpio_base: gpio number of first pin in this register range + * @num_gpios: number of gpios in this register range + * (number of registers = DIV_ROUND_UP(num_gpios, reg_width)) + * @direction: bitfield, '0' = input, '1' = output, + * this depends on ARCH_GPIOS_PER_CHIP not being greater than BITS_PER_LONG + */ +#if ARCH_GPIOS_PER_CHIP > BITS_PER_LONG +#error htc-egpio only works if ARCH_GPIOS_PER_CHIP <= BITS_PER_LONG +#endif +struct htc_egpio_chip { + int reg_start; + int gpio_base; + int num_gpios; + unsigned long direction; + unsigned long initial_values; }; -/* Platform data description provided by the arch */ +/** + * struct htc_egpio_platform_data - description provided by the arch + * @irq_base: beginning of available IRQs (eg, IRQ_BOARD_START) + * @num_irqs: number of irqs + * @reg_width: number of bits per register, either 8 or 16 bit + * @bus_width: alignment of the registers, either 16 or 32 bit + * @invert_acks: set if chip requires writing '0' to ack an irq, instead of '1' + * @ack_register: location of the irq/ack register + * @chip: pointer to array of htc_egpio_chip descriptors + * @num_chips: number of egpio chip descriptors + */ struct htc_egpio_platform_data { - /* Beginning of available irqs (eg, IRQ_BOARD_START) */ - int irq_base; - /* Beginning of available gpios (eg, GPIO_BASE_INCREMENT) */ - int gpio_base; - /* Shift register number by this value (bus_shift=1 for 32bit register alignment) */ - int bus_shift; - /* Set if chip requires writing '0' to ack an irq */ - int invertAcks; - /* Number of registers (optional if all output pins specified - * below) */ - int nrRegs; - /* The location of the irq ack register */ - int ackRegister; + int bus_width; + int reg_width; - /* List of pin descriptions. One must specify all input pins - * that have a corresponding irq pin and all output pins with - * a non-zero start value. Specifying other pins is - * optional. */ - struct htc_egpio_pinInfo *pins; - int nr_pins; + int irq_base; + int num_irqs; + int invert_acks; + int ack_register; + + struct htc_egpio_chip *chip; + int num_chips; }; /* Determine the wakeup irq, to be called during early resume */ --- /home/ph5/scm/hh.o/linux/kernel26/drivers/mfd/htc-egpio.c 2007-11-18 14:47:55.000000000 +0100 +++ drivers/mfd/htc-egpio.c 2007-11-24 21:51:39.000000000 +0100 @@ -1,8 +1,9 @@ /* - * Support for the EGPIO chip present on several HTC phones. This + * Support for the EGPIO chip present on several HTC phones. This * is believed to be related to the CPLD chip present on the board. * * (c) Copyright 2007 Kevin O'Connor + * Copyright (c) 2007 Philipp Zabel * * This file may be distributed under the terms of the GNU GPL license. */ @@ -13,39 +14,49 @@ #include /* spinlock_t */ #include /* platform_device */ #include -#include #include #include /* IRQT_RISING */ #include /* ioremap_nocache */ #include /* struct irq_chip */ +struct egpio_chip { + int reg_start; + struct gpio_chip chip; + + int cached_values; + + struct device *dev; +}; + struct egpio_info { - spinlock_t lock; - u16 *addrBase; - int bus_shift; + spinlock_t lock; + + /* iomem info */ + void __iomem *base_addr; + int bus_shift; + int reg_width; /* irq info */ - int ackRegister; - int ackWrite; - u16 irqsEnabled; - uint irqStart; - uint chainedirq; + int ack_register; + int ack_write; + u16 irqs_enabled; + uint irq_start; + int num_irqs; + uint chainedirq; - /* output info */ - int maxRegs; - u16 cached_values[MAX_EGPIO_REGS]; + /* egpio info */ + struct egpio_chip *chip; }; - -/**************************************************************** - * Irqs - ****************************************************************/ +/* + * IRQs + */ static inline void ackirqs(struct egpio_info *ei) { - writew(ei->ackWrite, &ei->addrBase[ei->ackRegister << ei->bus_shift]); - /* printk("EGPIO ack\n"); */ + writew(ei->ack_write, ei->base_addr + (ei->ack_register << ei->bus_shift)); + printk("EGPIO ack - write %x to %x\n", ei->ack_write, ei->ack_register << ei->bus_shift); } /* There does not appear to be a way to proactively mask interrupts @@ -54,14 +65,14 @@ static void egpio_mask(unsigned int irq) { struct egpio_info *ei = get_irq_chip_data(irq); - ei->irqsEnabled &= ~(1 << (irq - ei->irqStart)); - /* printk("EGPIO mask %d %04x\n", irq, ei->irqsEnabled); */ + ei->irqs_enabled &= ~(1 << (irq - ei->irq_start)); + printk("EGPIO mask %d %04x\n", irq, ei->irqs_enabled); } static void egpio_unmask(unsigned int irq) { struct egpio_info *ei = get_irq_chip_data(irq); - ei->irqsEnabled |= 1 << (irq - ei->irqStart); - /* printk("EGPIO unmask %d %04x\n", irq, ei->irqsEnabled); */ + ei->irqs_enabled |= 1 << (irq - ei->irq_start); + printk("EGPIO unmask %d %04x\n", irq, ei->irqs_enabled); } static struct irq_chip egpio_muxed_chip = { @@ -76,15 +87,16 @@ int irqpin; /* Read current pins. */ - u16 readval = readw(&ei->addrBase[ei->ackRegister << ei->bus_shift]); + unsigned long readval = readw(ei->base_addr + (ei->ack_register << ei->bus_shift)); + printk("IRQ reg: %x\n", readval); /* Ack/unmask interrupts. */ ackirqs(ei); /* Process all set pins. */ - for (irqpin=0; irqpinirqsEnabled & (1<irqs_enabled; + for_each_bit(irqpin, &readval, ei->num_irqs) { /* Run irq handler */ - irq = ei->irqStart + irqpin; + printk("got IRQ %d\n", irqpin); + irq = ei->irq_start + irqpin; desc = &irq_desc[irq]; desc->handle_irq(irq, desc); } @@ -93,128 +105,127 @@ int htc_egpio_get_wakeup_irq(struct device *dev) { struct egpio_info *ei = dev_get_drvdata(dev); - int irqpin; /* Read current pins. */ - u16 readval = readw(&ei->addrBase[ei->ackRegister << ei->bus_shift]); + u16 readval = readw(ei->base_addr + (ei->ack_register << ei->bus_shift)); /* Ack/unmask interrupts. */ ackirqs(ei); - /* Process all set pins. */ - for (irqpin=0; irqpinirqsEnabled & (1<irqStart + irqpin; - } - return 0; + /* Return first set pin. */ + readval &= ei->irqs_enabled; + return ei->irq_start + ffs(readval) - 1; } EXPORT_SYMBOL(htc_egpio_get_wakeup_irq); -/**************************************************************** +/* * Input pins - ****************************************************************/ + */ -static inline int u16pos(int bit) { - return bit/16; +static inline int egpio_pos(int bit, int reg_width) +{ + return bit/reg_width; } -static inline int u16bit(int bit) { - return 1<<(bit & (16-1)); + +static inline int egpio_bit(int bit, int reg_width) +{ + return 1<<(bit & (reg_width-1)); } /* Check an input pin to see if it is active. */ -static int egpio_get(struct device *dev, unsigned gpio) +static int egpio_get(struct gpio_chip *chip, unsigned offset) { - unsigned bit = gpio & GPIO_BASE_MASK; - struct egpio_info *ei = dev_get_drvdata(dev); - u16 readval = readw(&ei->addrBase[u16pos(bit) << ei->bus_shift]); - return readval & u16bit(bit); + struct egpio_chip *egpio; + struct egpio_info *ei; + unsigned bit; + int reg; + int value; + + printk(KERN_INFO "egpio_get_value(%d)\n", chip->base + offset); + + egpio = container_of(chip, struct egpio_chip, chip); + ei = dev_get_drvdata(egpio->dev); + bit = egpio_bit(offset, ei->reg_width); + reg = egpio->reg_start + egpio_pos(offset, ei->reg_width); + + value = readw(ei->base_addr + (reg << ei->bus_shift)); + printk(KERN_INFO "readw(%p + %x) = %x\n", ei->base_addr, reg << ei->bus_shift, value); + return value & bit; } -static int egpio_to_irq(struct device *dev, unsigned gpio_no) +static int egpio_direction_input(struct gpio_chip *chip, unsigned offset) { - struct egpio_info *ei = dev_get_drvdata(dev); - struct htc_egpio_platform_data *pdata = dev->platform_data; - int i; - for (i=0; inr_pins; i++) { - struct htc_egpio_pinInfo *pi = &pdata->pins[i]; - if (pi->type == HTC_EGPIO_TYPE_INPUT - && pi->gpio == gpio_no - && pi->input_irq >= 0) - return (pi->input_irq & 0xf) + ei->irqStart; - } - return -ENODEV; + return test_bit(offset, chip->is_out) ? -EINVAL : 0; } -/**************************************************************** +/* * Output pins - ****************************************************************/ + */ -static void egpio_set(struct device *dev, unsigned gpio, int val) +static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) { - unsigned bit = gpio & GPIO_BASE_MASK; - struct egpio_info *ei = dev_get_drvdata(dev); - int pos = u16pos(bit); - unsigned long flag; + unsigned long flag; + struct egpio_chip *egpio; + struct egpio_info *ei; + unsigned bit; + int pos; + int reg; + int mask; + int shift; + + printk(KERN_INFO "egpio_set(%s, %d(%d), %d)\n", chip->label, offset, offset+chip->base, value); + + egpio = container_of(chip, struct egpio_chip, chip); + ei = dev_get_drvdata(egpio->dev); + bit = egpio_bit(offset, ei->reg_width); + pos = egpio_pos(offset, ei->reg_width); + reg = egpio->reg_start + pos; spin_lock_irqsave(&ei->lock, flag); - if (val) { - ei->cached_values[pos] |= u16bit(bit); - printk("egpio set: reg %d = 0x%04x\n" - , pos, ei->cached_values[pos]); + if (value) { + egpio->cached_values |= (1 << offset); + printk(KERN_INFO "egpio set: reg %d = 0x%04x\n", + reg, egpio->cached_values); } else { - ei->cached_values[pos] &= ~u16bit(bit); - printk("egpio clear: reg %d = 0x%04x\n" - , pos, ei->cached_values[pos]); + egpio->cached_values &= ~(1 << offset); + printk(KERN_INFO "egpio clear: reg %d = 0x%04x\n", + reg, egpio->cached_values); } - writew(ei->cached_values[pos], &ei->addrBase[pos << ei->bus_shift]); + + mask = ((1 << ei->reg_width) - 1); + shift = ei->reg_width * pos; + printk("writew(%x, %p + %x)\n", (egpio->cached_values >> shift) & mask, + ei->base_addr, (pos << ei->bus_shift)); + writew((egpio->cached_values >> shift) & mask, + ei->base_addr + (pos << ei->bus_shift)); spin_unlock_irqrestore(&ei->lock, flag); } +static int egpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + if (test_bit(offset, chip->is_out)) { + egpio_set(chip, offset, value); + return 0; + } else { + return -EINVAL; + } +} + /**************************************************************** * Setup ****************************************************************/ -static void setup_pin(struct egpio_info *ei, struct htc_egpio_pinInfo *pi) -{ - int pin = pi->gpio & GPIO_BASE_MASK; - - if (pi->gpio < 0 || pin > 16*MAX_EGPIO_REGS) { - printk(KERN_ERR "EGPIO invalid pin %d\n", pi->gpio); - return; - } - - switch (pi->type) { - case HTC_EGPIO_TYPE_INPUT: - if (pi->input_irq < 0) - break; - pin = pi->input_irq & GPIO_BASE_MASK; - if (ei->ackRegister != u16pos(pin)) { - printk(KERN_ERR "EGPIO irq conflict %d vs %d\n" - , ei->ackRegister, u16pos(pin)); - return; - } - break; - case HTC_EGPIO_TYPE_OUTPUT: - if (ei->maxRegs < u16pos(pin)) - ei->maxRegs = u16pos(pin); - if (pi->output_initial) - ei->cached_values[u16pos(pin)] |= u16bit(pin); - break; - default: - printk(KERN_ERR "EGPIO unknown type %d\n", pi->type); - } -} - static int egpio_probe(struct platform_device *pdev) { struct htc_egpio_platform_data *pdata = pdev->dev.platform_data; - struct resource *res; + struct resource *res; struct egpio_info *ei; - int irq, i, ret; - struct gpio_ops ops; + struct gpio_chip *chip; + int irq; + int i; + int ret; /* Initialize ei data structure. */ ei = kzalloc(sizeof(*ei), GFP_KERNEL); @@ -233,33 +244,57 @@ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) goto fail; - ei->addrBase = (u16 *)ioremap_nocache(res->start, res->end - res->start); - if (!ei->addrBase) + ei->base_addr = ioremap_nocache(res->start, res->end - res->start); + if (!ei->base_addr) goto fail; - printk(KERN_NOTICE "EGPIO phys=%08x virt=%p\n" - , res->start, ei->addrBase); - ei->bus_shift = pdata->bus_shift; - + printk(KERN_NOTICE "EGPIO phys=%08x virt=%p\n", + res->start, ei->base_addr); + ei->bus_shift = fls(pdata->bus_width - 1) - 3; + printk("BUS_SHIFT = %d\n", ei->bus_shift); + ei->reg_width = pdata->reg_width; platform_set_drvdata(pdev, ei); - ops.get_value = egpio_get; - ops.set_value = egpio_set; - ops.to_irq = egpio_to_irq; - gpiodev_register(pdata->gpio_base, &pdev->dev, &ops); + ei->chip = kzalloc(sizeof(struct egpio_chip) * pdata->num_chips, GFP_KERNEL); + if (!ei) { + ret = -ENOMEM; + goto fail; + } + for (i = 0; i < pdata->num_chips; i++) { + ei->chip[i].reg_start = pdata->chip[i].reg_start; + ei->chip[i].cached_values = pdata->chip[i].initial_values; + ei->chip[i].dev = &(pdev->dev); + chip = &(ei->chip[i].chip); + chip->label = "htc-egpio"; + chip->get = egpio_get; + chip->set = egpio_set; + chip->direction_input = egpio_direction_input; + chip->direction_output = egpio_direction_output; - /* Go through list of pins. */ - ei->irqStart = pdata->irq_base; - ei->maxRegs = pdata->nrRegs - 1; - ei->ackRegister = pdata->ackRegister; - for (i = 0; i < pdata->nr_pins; i++) - setup_pin(ei, &pdata->pins[i]); + chip->base = pdata->chip[i].gpio_base; + chip->ngpio = pdata->chip[i].num_gpios; + /* only gpiolib is supposed to modify is_out, doesn't stop us */ + chip->is_out[0] = pdata->chip[i].direction; + + gpiochip_add(chip); + } + +#if 0 + /* Setup initial output pin values. */ + if (pdata->chip[i].type == HTC_EGPIO_TYPE_OUTPUT) { + printk("egpio read: reg %d\n", reg); pdata->chip[i].cached_values + } +#endif + + ei->irq_start = pdata->irq_base; + ei->num_irqs = pdata->num_irqs; + ei->ack_register = pdata->ack_register; if (ei->chainedirq) { /* Setup irq handlers */ - ei->ackWrite = 0xFFFF; - if (pdata->invertAcks) - ei->ackWrite = 0; - for (irq = ei->irqStart; irq < ei->irqStart+MAX_EGPIO_IRQS; irq++) { + ei->ack_write = 0xFFFF; + if (pdata->invert_acks) + ei->ack_write = 0; + for (irq = ei->irq_start; irq < ei->irq_start+ei->num_irqs; irq++) { set_irq_chip(irq, &egpio_muxed_chip); set_irq_chip_data(irq, ei); set_irq_handler(irq, handle_simple_irq); @@ -273,11 +308,6 @@ device_init_wakeup(&pdev->dev, 1); } - /* Setup initial output pin values. */ - for (i = 0; i<=ei->maxRegs; i++) - if (i != ei->ackRegister) - writew(ei->cached_values[i], &ei->addrBase[i << ei->bus_shift]); - return 0; fail: @@ -291,7 +321,7 @@ struct egpio_info *ei = platform_get_drvdata(pdev); unsigned int irq; if (ei->chainedirq) { - for (irq = ei->irqStart; irq < ei->irqStart+MAX_EGPIO_IRQS; irq++) { + for (irq = ei->irq_start; irq < ei->irq_start+ei->num_irqs; irq++) { set_irq_chip(irq, NULL); set_irq_handler(irq, NULL); set_irq_flags(irq, 0); @@ -299,7 +329,8 @@ set_irq_chained_handler(ei->chainedirq, NULL); device_init_wakeup(&pdev->dev, 0); } - iounmap(ei->addrBase); + iounmap(ei->base_addr); + kfree(ei->chip); kfree(ei); return 0; @@ -349,11 +380,8 @@ platform_driver_unregister(&egpio_driver); } -#ifdef MODULE -module_init(egpio_init); -#else /* start early for dependencies */ +/* start early for dependencies */ subsys_initcall(egpio_init); -#endif module_exit(egpio_exit) MODULE_LICENSE("GPL");