All pastes #796201 Raw Edit

Diff

public text v1 · immutable
#796201 ·published 2007-11-24 21:19 UTC
rendered paste body
--- /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 <asm/gpio.h> -/* 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 <kevin@koconnor.net>+ * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>  *  * This file may be distributed under the terms of the GNU GPL license.  */@@ -13,39 +14,49 @@ #include <linux/spinlock.h> /* spinlock_t */ #include <linux/platform_device.h> /* platform_device */ #include <linux/module.h>-#include <linux/gpiodev2.h> #include <linux/mfd/htc-egpio.h>  #include <asm/irq.h> /* IRQT_RISING */ #include <asm/io.h> /* ioremap_nocache */ #include <asm/mach/irq.h> /* 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; irqpin<MAX_EGPIO_IRQS; irqpin++) {-		if (!(readval & ei->irqsEnabled & (1<<irqpin)))-			continue;+	readval &= ei->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; irqpin<MAX_EGPIO_IRQS; irqpin++) {-		if (!(readval & ei->irqsEnabled & (1<<irqpin)))-			continue;-		/* Return first irq */-		return ei->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; i<pdata->nr_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");