Merge branch 'drm-intel-next' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt...
[pandora-kernel.git] / drivers / gpio / langwell_gpio.c
index 00c3a14..8383a8d 100644 (file)
@@ -17,6 +17,7 @@
 
 /* Supports:
  * Moorestown platform Langwell chip.
+ * Medfield platform Penwell chip.
  */
 
 #include <linux/module.h>
 #include <linux/gpio.h>
 #include <linux/slab.h>
 
-struct lnw_gpio_register {
-       u32     GPLR[2];
-       u32     GPDR[2];
-       u32     GPSR[2];
-       u32     GPCR[2];
-       u32     GRER[2];
-       u32     GFER[2];
-       u32     GEDR[2];
+/*
+ * Langwell chip has 64 pins and thus there are 2 32bit registers to control
+ * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit
+ * registers to control them, so we only define the order here instead of a
+ * structure, to get a bit offset for a pin (use GPDR as an example):
+ *
+ * nreg = ngpio / 32;
+ * reg = offset / 32;
+ * bit = offset % 32;
+ * reg_addr = reg_base + GPDR * nreg * 4 + reg * 4;
+ *
+ * so the bit of reg_addr is to control pin offset's GPDR feature
+*/
+
+enum GPIO_REG {
+       GPLR = 0,       /* pin level read-only */
+       GPDR,           /* pin direction */
+       GPSR,           /* pin set */
+       GPCR,           /* pin clear */
+       GRER,           /* rising edge detect */
+       GFER,           /* falling edge detect */
+       GEDR,           /* edge detect result */
 };
 
 struct lnw_gpio {
        struct gpio_chip                chip;
-       struct lnw_gpio_register        *reg_base;
+       void                            *reg_base;
        spinlock_t                      lock;
        unsigned                        irq_base;
 };
 
-static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset)
+static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
+                       enum GPIO_REG reg_type)
 {
        struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
+       unsigned nreg = chip->ngpio / 32;
        u8 reg = offset / 32;
-       void __iomem *gplr;
+       void __iomem *ptr;
+
+       ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4);
+       return ptr;
+}
+
+static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       void __iomem *gplr = gpio_reg(chip, offset, GPLR);
 
-       gplr = (void __iomem *)(&lnw->reg_base->GPLR[reg]);
        return readl(gplr) & BIT(offset % 32);
 }
 
 static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
-       struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
-       u8 reg = offset / 32;
        void __iomem *gpsr, *gpcr;
 
        if (value) {
-               gpsr = (void __iomem *)(&lnw->reg_base->GPSR[reg]);
+               gpsr = gpio_reg(chip, offset, GPSR);
                writel(BIT(offset % 32), gpsr);
        } else {
-               gpcr = (void __iomem *)(&lnw->reg_base->GPCR[reg]);
+               gpcr = gpio_reg(chip, offset, GPCR);
                writel(BIT(offset % 32), gpcr);
        }
 }
@@ -76,12 +98,10 @@ static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 {
        struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
-       u8 reg = offset / 32;
+       void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
        u32 value;
        unsigned long flags;
-       void __iomem *gpdr;
 
-       gpdr = (void __iomem *)(&lnw->reg_base->GPDR[reg]);
        spin_lock_irqsave(&lnw->lock, flags);
        value = readl(gpdr);
        value &= ~BIT(offset % 32);
@@ -94,12 +114,10 @@ static int lnw_gpio_direction_output(struct gpio_chip *chip,
                        unsigned offset, int value)
 {
        struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
-       u8 reg = offset / 32;
+       void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
        unsigned long flags;
-       void __iomem *gpdr;
 
        lnw_gpio_set(chip, offset, value);
-       gpdr = (void __iomem *)(&lnw->reg_base->GPDR[reg]);
        spin_lock_irqsave(&lnw->lock, flags);
        value = readl(gpdr);
        value |= BIT(offset % 32);;
@@ -118,11 +136,10 @@ static int lnw_irq_type(unsigned irq, unsigned type)
 {
        struct lnw_gpio *lnw = get_irq_chip_data(irq);
        u32 gpio = irq - lnw->irq_base;
-       u8 reg = gpio / 32;
        unsigned long flags;
        u32 value;
-       void __iomem *grer = (void __iomem *)(&lnw->reg_base->GRER[reg]);
-       void __iomem *gfer = (void __iomem *)(&lnw->reg_base->GFER[reg]);
+       void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER);
+       void __iomem *gfer = gpio_reg(&lnw->chip, gpio, GFER);
 
        if (gpio >= lnw->chip.ngpio)
                return -EINVAL;
@@ -158,8 +175,10 @@ static struct irq_chip lnw_irqchip = {
        .set_type       = lnw_irq_type,
 };
 
-static struct pci_device_id lnw_gpio_ids[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f) },
+static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = {   /* pin number */
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), .driver_data = 64 },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), .driver_data = 96 },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), .driver_data = 96 },
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, lnw_gpio_ids);
@@ -167,17 +186,17 @@ MODULE_DEVICE_TABLE(pci, lnw_gpio_ids);
 static void lnw_irq_handler(unsigned irq, struct irq_desc *desc)
 {
        struct lnw_gpio *lnw = (struct lnw_gpio *)get_irq_data(irq);
-       u32 reg, gpio;
+       u32 base, gpio;
        void __iomem *gedr;
        u32 gedr_v;
 
        /* check GPIO controller to check which pin triggered the interrupt */
-       for (reg = 0; reg < lnw->chip.ngpio / 32; reg++) {
-               gedr = (void __iomem *)(&lnw->reg_base->GEDR[reg]);
+       for (base = 0; base < lnw->chip.ngpio; base += 32) {
+               gedr = gpio_reg(&lnw->chip, base, GEDR);
                gedr_v = readl(gedr);
                if (!gedr_v)
                        continue;
-               for (gpio = reg*32; gpio < reg*32+32; gpio++)
+               for (gpio = base; gpio < base + 32; gpio++)
                        if (gedr_v & BIT(gpio % 32)) {
                                pr_debug("pin %d triggered\n", gpio);
                                generic_handle_irq(lnw->irq_base + gpio);
@@ -245,7 +264,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
        lnw->chip.set = lnw_gpio_set;
        lnw->chip.to_irq = lnw_gpio_to_irq;
        lnw->chip.base = gpio_base;
-       lnw->chip.ngpio = 64;
+       lnw->chip.ngpio = id->driver_data;
        lnw->chip.can_sleep = 0;
        pci_set_drvdata(pdev, lnw);
        retval = gpiochip_add(&lnw->chip);