mfd: Clarify twl4030 return value for read and write
[safe/jmp/linux-2.6] / drivers / mfd / twl4030-irq.c
index b108760..3f7e93c 100644 (file)
@@ -74,6 +74,8 @@ struct sih {
        u8      edr_offset;
        u8      bytes_edr;              /* bytelen of EDR */
 
+       u8      irq_lines;              /* number of supported irq lines */
+
        /* SIR ignored -- set interrupt, for testing only */
        struct irq_data {
                u8      isr_offset;
@@ -82,6 +84,9 @@ struct sih {
        /* + 2 bytes padding */
 };
 
+static const struct sih *sih_modules;
+static int nr_sih_modules;
+
 #define SIH_INITIALIZER(modname, nbits) \
        .module         = TWL4030_MODULE_ ## modname, \
        .control_offset = TWL4030_ ## modname ## _SIH_CTRL, \
@@ -89,6 +94,7 @@ struct sih {
        .bytes_ixr      = DIV_ROUND_UP(nbits, 8), \
        .edr_offset     = TWL4030_ ## modname ## _EDR, \
        .bytes_edr      = DIV_ROUND_UP((2*(nbits)), 8), \
+       .irq_lines      = 2, \
        .mask = { { \
                .isr_offset     = TWL4030_ ## modname ## _ISR1, \
                .imr_offset     = TWL4030_ ## modname ## _IMR1, \
@@ -107,7 +113,8 @@ struct sih {
 /* Order in this table matches order in PIH_ISR.  That is,
  * BIT(n) in PIH_ISR is sih_modules[n].
  */
-static const struct sih sih_modules[6] = {
+/* sih_modules_twl4030 is used both in twl4030 and twl5030 */
+static const struct sih sih_modules_twl4030[6] = {
        [0] = {
                .name           = "gpio",
                .module         = TWL4030_MODULE_GPIO,
@@ -118,6 +125,7 @@ static const struct sih sih_modules[6] = {
                /* Note: *all* of these IRQs default to no-trigger */
                .edr_offset     = REG_GPIO_EDR1,
                .bytes_edr      = 5,
+               .irq_lines      = 2,
                .mask = { {
                        .isr_offset     = REG_GPIO_ISR1A,
                        .imr_offset     = REG_GPIO_IMR1A,
@@ -140,6 +148,7 @@ static const struct sih sih_modules[6] = {
                .edr_offset     = TWL4030_INTERRUPTS_BCIEDR1,
                /* Note: most of these IRQs default to no-trigger */
                .bytes_edr      = 3,
+               .irq_lines      = 2,
                .mask = { {
                        .isr_offset     = TWL4030_INTERRUPTS_BCIISR1A,
                        .imr_offset     = TWL4030_INTERRUPTS_BCIIMR1A,
@@ -164,6 +173,99 @@ static const struct sih sih_modules[6] = {
                /* there are no SIH modules #6 or #7 ... */
 };
 
+static const struct sih sih_modules_twl5031[8] = {
+       [0] = {
+               .name           = "gpio",
+               .module         = TWL4030_MODULE_GPIO,
+               .control_offset = REG_GPIO_SIH_CTRL,
+               .set_cor        = true,
+               .bits           = TWL4030_GPIO_MAX,
+               .bytes_ixr      = 3,
+               /* Note: *all* of these IRQs default to no-trigger */
+               .edr_offset     = REG_GPIO_EDR1,
+               .bytes_edr      = 5,
+               .irq_lines      = 2,
+               .mask = { {
+                       .isr_offset     = REG_GPIO_ISR1A,
+                       .imr_offset     = REG_GPIO_IMR1A,
+               }, {
+                       .isr_offset     = REG_GPIO_ISR1B,
+                       .imr_offset     = REG_GPIO_IMR1B,
+               }, },
+       },
+       [1] = {
+               .name           = "keypad",
+               .set_cor        = true,
+               SIH_INITIALIZER(KEYPAD_KEYP, 4)
+       },
+       [2] = {
+               .name           = "bci",
+               .module         = TWL5031_MODULE_INTERRUPTS,
+               .control_offset = TWL5031_INTERRUPTS_BCISIHCTRL,
+               .bits           = 7,
+               .bytes_ixr      = 1,
+               .edr_offset     = TWL5031_INTERRUPTS_BCIEDR1,
+               /* Note: most of these IRQs default to no-trigger */
+               .bytes_edr      = 2,
+               .irq_lines      = 2,
+               .mask = { {
+                       .isr_offset     = TWL5031_INTERRUPTS_BCIISR1,
+                       .imr_offset     = TWL5031_INTERRUPTS_BCIIMR1,
+               }, {
+                       .isr_offset     = TWL5031_INTERRUPTS_BCIISR2,
+                       .imr_offset     = TWL5031_INTERRUPTS_BCIIMR2,
+               }, },
+       },
+       [3] = {
+               .name           = "madc",
+               SIH_INITIALIZER(MADC, 4)
+       },
+       [4] = {
+               /* USB doesn't use the same SIH organization */
+               .name           = "usb",
+       },
+       [5] = {
+               .name           = "power",
+               .set_cor        = true,
+               SIH_INITIALIZER(INT_PWR, 8)
+       },
+       [6] = {
+               /*
+                * ACI doesn't use the same SIH organization.
+                * For example, it supports only one interrupt line
+                */
+               .name           = "aci",
+               .module         = TWL5031_MODULE_ACCESSORY,
+               .bits           = 9,
+               .bytes_ixr      = 2,
+               .irq_lines      = 1,
+               .mask = { {
+                       .isr_offset     = TWL5031_ACIIDR_LSB,
+                       .imr_offset     = TWL5031_ACIIMR_LSB,
+               }, },
+
+       },
+       [7] = {
+               /* Accessory */
+               .name           = "acc",
+               .module         = TWL5031_MODULE_ACCESSORY,
+               .control_offset = TWL5031_ACCSIHCTRL,
+               .bits           = 2,
+               .bytes_ixr      = 1,
+               .edr_offset     = TWL5031_ACCEDR1,
+               /* Note: most of these IRQs default to no-trigger */
+               .bytes_edr      = 1,
+               .irq_lines      = 2,
+               .mask = { {
+                       .isr_offset     = TWL5031_ACCISR1,
+                       .imr_offset     = TWL5031_ACCIMR1,
+               }, {
+                       .isr_offset     = TWL5031_ACCISR2,
+                       .imr_offset     = TWL5031_ACCIMR2,
+               }, },
+       },
+};
+
 #undef TWL4030_MODULE_KEYPAD_KEYP
 #undef TWL4030_MODULE_INT_PWR
 #undef TWL4030_INT_PWR_EDR
@@ -180,14 +282,9 @@ static struct completion irq_event;
 static int twl4030_irq_thread(void *data)
 {
        long irq = (long)data;
-       struct irq_desc *desc = irq_to_desc(irq);
        static unsigned i2c_errors;
-       const static unsigned max_i2c_errors = 100;
+       static const unsigned max_i2c_errors = 100;
 
-       if (!desc) {
-               pr_err("twl4030: Invalid IRQ: %ld\n", irq);
-               return -EINVAL;
-       }
 
        current->flags |= PF_NOFREEZE;
 
@@ -240,7 +337,7 @@ static int twl4030_irq_thread(void *data)
                }
                local_irq_enable();
 
-               desc->chip->unmask(irq);
+               enable_irq(irq);
        }
 
        return 0;
@@ -255,25 +352,13 @@ static int twl4030_irq_thread(void *data)
  * thread.  All we do here is acknowledge and mask the interrupt and wakeup
  * the kernel thread.
  */
-static void handle_twl4030_pih(unsigned int irq, irq_desc_t *desc)
+static irqreturn_t handle_twl4030_pih(int irq, void *devid)
 {
        /* Acknowledge, clear *AND* mask the interrupt... */
-       desc->chip->ack(irq);
-       complete(&irq_event);
-}
-
-static struct task_struct *start_twl4030_irq_thread(long irq)
-{
-       struct task_struct *thread;
-
-       init_completion(&irq_event);
-       thread = kthread_run(twl4030_irq_thread, (void *)irq, "twl4030-irq");
-       if (!thread)
-               pr_err("twl4030: could not create irq %ld thread!\n", irq);
-
-       return thread;
+       disable_irq_nosync(irq);
+       complete(devid);
+       return IRQ_HANDLED;
 }
-
 /*----------------------------------------------------------------------*/
 
 /*
@@ -301,12 +386,16 @@ static int twl4030_init_sih_modules(unsigned line)
        /* disable all interrupts on our line */
        memset(buf, 0xff, sizeof buf);
        sih = sih_modules;
-       for (i = 0; i < ARRAY_SIZE(sih_modules); i++, sih++) {
+       for (i = 0; i < nr_sih_modules; i++, sih++) {
 
                /* skip USB -- it's funky */
                if (!sih->bytes_ixr)
                        continue;
 
+               /* Not all the SIH modules support multiple interrupt lines */
+               if (sih->irq_lines <= line)
+                       continue;
+
                status = twl4030_i2c_write(sih->module, buf,
                                sih->mask[line].imr_offset, sih->bytes_ixr);
                if (status < 0)
@@ -331,7 +420,7 @@ static int twl4030_init_sih_modules(unsigned line)
        }
 
        sih = sih_modules;
-       for (i = 0; i < ARRAY_SIZE(sih_modules); i++, sih++) {
+       for (i = 0; i < nr_sih_modules; i++, sih++) {
                u8 rxbuf[4];
                int j;
 
@@ -339,6 +428,10 @@ static int twl4030_init_sih_modules(unsigned line)
                if (!sih->bytes_ixr)
                        continue;
 
+               /* Not all the SIH modules support multiple interrupt lines */
+               if (sih->irq_lines <= line)
+                       continue;
+
                /* Clear pending interrupt status.  Either the read was
                 * enough, or we need to write those bits.  Repeat, in
                 * case an IRQ is pending (PENDDIS=0) ... that's not
@@ -441,7 +534,7 @@ static void twl4030_sih_do_edge(struct work_struct *work)
        /* see what work we have */
        spin_lock_irq(&sih_agent_lock);
        edge_change = agent->edge_change;
-       agent->edge_change = 0;;
+       agent->edge_change = 0;
        sih = edge_change ? agent->sih : NULL;
        spin_unlock_irq(&sih_agent_lock);
        if (!sih)
@@ -628,7 +721,7 @@ int twl4030_sih_setup(int module)
 
        /* only support modules with standard clear-on-read for now */
        for (sih_mod = 0, sih = sih_modules;
-                       sih_mod < ARRAY_SIZE(sih_modules);
+                       sih_mod < nr_sih_modules;
                        sih_mod++, sih++) {
                if (sih->module == module && sih->set_cor) {
                        if (!WARN((irq_base + sih->bits) > NR_IRQS,
@@ -734,18 +827,29 @@ int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
        }
 
        /* install an irq handler to demultiplex the TWL4030 interrupt */
-       task = start_twl4030_irq_thread(irq_num);
-       if (!task) {
-               pr_err("twl4030: irq thread FAIL\n");
-               status = -ESRCH;
-               goto fail;
-       }
 
-       set_irq_data(irq_num, task);
-       set_irq_chained_handler(irq_num, handle_twl4030_pih);
 
-       return status;
+       init_completion(&irq_event);
 
+       status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED,
+                               "TWL4030-PIH", &irq_event);
+       if (status < 0) {
+               pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status);
+               goto fail_rqirq;
+       }
+
+       task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num,
+                                                               "twl4030-irq");
+       if (IS_ERR(task)) {
+               pr_err("twl4030: could not create irq %d thread!\n", irq_num);
+               status = PTR_ERR(task);
+               goto fail_kthread;
+       }
+       return status;
+fail_kthread:
+       free_irq(irq_num, &irq_event);
+fail_rqirq:
+       /* clean up twl4030_sih_setup */
 fail:
        for (i = irq_base; i < irq_end; i++)
                set_irq_chip_and_handler(i, NULL, NULL);
@@ -763,3 +867,16 @@ int twl_exit_irq(void)
        }
        return 0;
 }
+
+int twl_init_chip_irq(const char *chip)
+{
+       if (!strcmp(chip, "twl5031")) {
+               sih_modules = sih_modules_twl5031;
+               nr_sih_modules = ARRAY_SIZE(sih_modules_twl5031);
+       } else {
+               sih_modules = sih_modules_twl4030;
+               nr_sih_modules = ARRAY_SIZE(sih_modules_twl4030);
+       }
+
+       return 0;
+}