mfd: Add support for no-interrupt stmpe config
authorChris Blair <chris.blair@stericsson.com>
Thu, 26 Jan 2012 21:17:03 +0000 (22:17 +0100)
committerSamuel Ortiz <sameo@linux.intel.com>
Tue, 6 Mar 2012 17:46:38 +0000 (18:46 +0100)
Adds support for boards which have an STMPE device without the
interrupt pin connected.

Acked-by: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Chris Blair <chris.blair@stericsson.com>
Tested-by: Michel Jaouen <michel.jaouen@stericsson.com>
Reviewed-by: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
drivers/mfd/stmpe.c
include/linux/mfd/stmpe.h

index e07947e56b2a0e2bd68d5aa7da434ae6aa5ed878..2dd8d49cb30bc7d63d14250e673b2c12eb4cef3f 100644 (file)
@@ -298,6 +298,11 @@ static struct mfd_cell stmpe_gpio_cell = {
        .num_resources  = ARRAY_SIZE(stmpe_gpio_resources),
 };
 
+static struct mfd_cell stmpe_gpio_cell_noirq = {
+       .name           = "stmpe-gpio",
+       /* gpio cell resources consist of an irq only so no resources here */
+};
+
 /*
  * Keypad (1601, 2401, 2403)
  */
@@ -346,6 +351,13 @@ static struct stmpe_variant_block stmpe801_blocks[] = {
        },
 };
 
+static struct stmpe_variant_block stmpe801_blocks_noirq[] = {
+       {
+               .cell   = &stmpe_gpio_cell_noirq,
+               .block  = STMPE_BLOCK_GPIO,
+       },
+};
+
 static int stmpe801_enable(struct stmpe *stmpe, unsigned int blocks,
                           bool enable)
 {
@@ -367,6 +379,17 @@ static struct stmpe_variant_info stmpe801 = {
        .enable         = stmpe801_enable,
 };
 
+static struct stmpe_variant_info stmpe801_noirq = {
+       .name           = "stmpe801",
+       .id_val         = STMPE801_ID,
+       .id_mask        = 0xffff,
+       .num_gpios      = 8,
+       .regs           = stmpe801_regs,
+       .blocks         = stmpe801_blocks_noirq,
+       .num_blocks     = ARRAY_SIZE(stmpe801_blocks_noirq),
+       .enable         = stmpe801_enable,
+};
+
 /*
  * Touchscreen (STMPE811 or STMPE610)
  */
@@ -712,7 +735,7 @@ static struct stmpe_variant_info stmpe2403 = {
        .enable_autosleep       = stmpe1601_autosleep, /* same as stmpe1601 */
 };
 
-static struct stmpe_variant_info *stmpe_variant_info[] = {
+static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = {
        [STMPE610]      = &stmpe610,
        [STMPE801]      = &stmpe801,
        [STMPE811]      = &stmpe811,
@@ -721,6 +744,16 @@ static struct stmpe_variant_info *stmpe_variant_info[] = {
        [STMPE2403]     = &stmpe2403,
 };
 
+/*
+ * These devices can be connected in a 'no-irq' configuration - the irq pin
+ * is not used and the device cannot interrupt the CPU. Here we only list
+ * devices which support this configuration - the driver will fail probing
+ * for any devices not listed here which are configured in this way.
+ */
+static struct stmpe_variant_info *stmpe_noirq_variant_info[STMPE_NBR_PARTS] = {
+       [STMPE801]      = &stmpe801_noirq,
+};
+
 static irqreturn_t stmpe_irq(int irq, void *data)
 {
        struct stmpe *stmpe = data;
@@ -864,7 +897,7 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
        unsigned int irq_trigger = stmpe->pdata->irq_trigger;
        int autosleep_timeout = stmpe->pdata->autosleep_timeout;
        struct stmpe_variant_info *variant = stmpe->variant;
-       u8 icr;
+       u8 icr = 0;
        unsigned int id;
        u8 data[2];
        int ret;
@@ -887,31 +920,33 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
        if (ret)
                return ret;
 
-       if (id == STMPE801_ID)
-               icr = STMPE801_REG_SYS_CTRL_INT_EN;
-       else
-               icr = STMPE_ICR_LSB_GIM;
-
-       /* STMPE801 doesn't support Edge interrupts */
-       if (id != STMPE801_ID) {
-               if (irq_trigger == IRQF_TRIGGER_FALLING ||
-                               irq_trigger == IRQF_TRIGGER_RISING)
-                       icr |= STMPE_ICR_LSB_EDGE;
-       }
-
-       if (irq_trigger == IRQF_TRIGGER_RISING ||
-                       irq_trigger == IRQF_TRIGGER_HIGH) {
+       if (stmpe->irq >= 0) {
                if (id == STMPE801_ID)
-                       icr |= STMPE801_REG_SYS_CTRL_INT_HI;
+                       icr = STMPE801_REG_SYS_CTRL_INT_EN;
                else
-                       icr |= STMPE_ICR_LSB_HIGH;
-       }
+                       icr = STMPE_ICR_LSB_GIM;
 
-       if (stmpe->pdata->irq_invert_polarity) {
-               if (id == STMPE801_ID)
-                       icr ^= STMPE801_REG_SYS_CTRL_INT_HI;
-               else
-                       icr ^= STMPE_ICR_LSB_HIGH;
+               /* STMPE801 doesn't support Edge interrupts */
+               if (id != STMPE801_ID) {
+                       if (irq_trigger == IRQF_TRIGGER_FALLING ||
+                                       irq_trigger == IRQF_TRIGGER_RISING)
+                               icr |= STMPE_ICR_LSB_EDGE;
+               }
+
+               if (irq_trigger == IRQF_TRIGGER_RISING ||
+                               irq_trigger == IRQF_TRIGGER_HIGH) {
+                       if (id == STMPE801_ID)
+                               icr |= STMPE801_REG_SYS_CTRL_INT_HI;
+                       else
+                               icr |= STMPE_ICR_LSB_HIGH;
+               }
+
+               if (stmpe->pdata->irq_invert_polarity) {
+                       if (id == STMPE801_ID)
+                               icr ^= STMPE801_REG_SYS_CTRL_INT_HI;
+                       else
+                               icr ^= STMPE_ICR_LSB_HIGH;
+               }
        }
 
        if (stmpe->pdata->autosleep) {
@@ -1001,19 +1036,38 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
                stmpe->irq = ci->irq;
        }
 
+       if (stmpe->irq < 0) {
+               /* use alternate variant info for no-irq mode, if supported */
+               dev_info(stmpe->dev,
+                       "%s configured in no-irq mode by platform data\n",
+                       stmpe->variant->name);
+               if (!stmpe_noirq_variant_info[stmpe->partnum]) {
+                       dev_err(stmpe->dev,
+                               "%s does not support no-irq mode!\n",
+                               stmpe->variant->name);
+                       ret = -ENODEV;
+                       goto free_gpio;
+               }
+               stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum];
+       }
+
        ret = stmpe_chip_init(stmpe);
        if (ret)
                goto free_gpio;
 
-       ret = stmpe_irq_init(stmpe);
-       if (ret)
-               goto free_gpio;
+       if (stmpe->irq >= 0) {
+               ret = stmpe_irq_init(stmpe);
+               if (ret)
+                       goto free_gpio;
 
-       ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq,
-                       pdata->irq_trigger | IRQF_ONESHOT, "stmpe", stmpe);
-       if (ret) {
-               dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret);
-               goto out_removeirq;
+               ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq,
+                               pdata->irq_trigger | IRQF_ONESHOT,
+                               "stmpe", stmpe);
+               if (ret) {
+                       dev_err(stmpe->dev, "failed to request IRQ: %d\n",
+                                       ret);
+                       goto out_removeirq;
+               }
        }
 
        ret = stmpe_devices_init(stmpe);
@@ -1026,9 +1080,11 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
 
 out_removedevs:
        mfd_remove_devices(stmpe->dev);
-       free_irq(stmpe->irq, stmpe);
+       if (stmpe->irq >= 0)
+               free_irq(stmpe->irq, stmpe);
 out_removeirq:
-       stmpe_irq_remove(stmpe);
+       if (stmpe->irq >= 0)
+               stmpe_irq_remove(stmpe);
 free_gpio:
        if (pdata->irq_over_gpio)
                gpio_free(pdata->irq_gpio);
@@ -1041,8 +1097,10 @@ int stmpe_remove(struct stmpe *stmpe)
 {
        mfd_remove_devices(stmpe->dev);
 
-       free_irq(stmpe->irq, stmpe);
-       stmpe_irq_remove(stmpe);
+       if (stmpe->irq >= 0) {
+               free_irq(stmpe->irq, stmpe);
+               stmpe_irq_remove(stmpe);
+       }
 
        if (stmpe->pdata->irq_over_gpio)
                gpio_free(stmpe->pdata->irq_gpio);
@@ -1057,7 +1115,7 @@ static int stmpe_suspend(struct device *dev)
 {
        struct stmpe *stmpe = dev_get_drvdata(dev);
 
-       if (device_may_wakeup(dev))
+       if (stmpe->irq >= 0 && device_may_wakeup(dev))
                enable_irq_wake(stmpe->irq);
 
        return 0;
@@ -1067,7 +1125,7 @@ static int stmpe_resume(struct device *dev)
 {
        struct stmpe *stmpe = dev_get_drvdata(dev);
 
-       if (device_may_wakeup(dev))
+       if (stmpe->irq >= 0 && device_may_wakeup(dev))
                disable_irq_wake(stmpe->irq);
 
        return 0;
index ca1d7a3476004d6936c104a8a435dfd4ac4e5f52..a1c6aa72cc764e7e52130cafd9b9baa37419b93e 100644 (file)
@@ -26,6 +26,7 @@ enum stmpe_partnum {
        STMPE1601,
        STMPE2401,
        STMPE2403,
+       STMPE_NBR_PARTS
 };
 
 /*