rk2928: fix too many usb hub irqs when changing frequency
[firefly-linux-kernel-4.4.55.git] / drivers / mfd / wm831x-irq.c
old mode 100644 (file)
new mode 100755 (executable)
index 42b928e..f54285b
 #include <linux/irq.h>
 #include <linux/mfd/core.h>
 #include <linux/interrupt.h>
-
+#include <linux/slab.h>
 #include <linux/mfd/wm831x/core.h>
 #include <linux/mfd/wm831x/pdata.h>
 #include <linux/mfd/wm831x/gpio.h>
 #include <linux/mfd/wm831x/irq.h>
 
 #include <linux/delay.h>
+#include <linux/wakelock.h>
+/*
+ * Since generic IRQs don't currently support interrupt controllers on
+ * interrupt driven buses we don't use genirq but instead provide an
+ * interface that looks very much like the standard ones.  This leads
+ * to some bodges, including storing interrupt handler information in
+ * the static irq_data table we use to look up the data for individual
+ * interrupts, but hopefully won't last too long.
+ */
+#define WM831X_IRQ_TYPE IRQF_TRIGGER_LOW
 
 struct wm831x_irq_data {
        int primary;
@@ -32,6 +42,12 @@ struct wm831x_irq_data {
        int mask;
 };
 
+struct wm831x_handle_irq
+{      
+       int irq;
+       struct list_head        queue;
+};
+
 static struct wm831x_irq_data wm831x_irqs[] = {
        [WM831X_IRQ_TEMP_THW] = {
                .primary = WM831X_TEMP_INT,
@@ -373,6 +389,7 @@ static void wm831x_irq_enable(struct irq_data *data)
                                                             data->irq);
 
        wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+       //printk("%s:irq=%d\n",__FUNCTION__,irq);
 }
 
 static void wm831x_irq_disable(struct irq_data *data)
@@ -382,23 +399,23 @@ static void wm831x_irq_disable(struct irq_data *data)
                                                             data->irq);
 
        wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+       //printk("%s:irq=%d\n",__FUNCTION__,irq);
 }
 
 static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
 {
        struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
-       int val, irq;
+       int val, irq = 0;
 
        irq = data->irq - wm831x->irq_base;
-
-       if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) {
+       if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_12) {
                /* Ignore internal-only IRQs */
                if (irq >= 0 && irq < WM831X_NUM_IRQS)
                        return 0;
                else
                        return -EINVAL;
        }
-
+       //printk("wm831x_irq_set_type:type=%x,irq=%d\n",type,irq);
        switch (type) {
        case IRQ_TYPE_EDGE_BOTH:
                val = WM831X_GPN_INT_MODE;
@@ -413,10 +430,32 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
                return -EINVAL;
        }
 
-       return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq,
+       return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq - 1,
                               WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
 }
 
+static int wm831x_irq_set_wake(struct irq_data *data, unsigned state)
+{      
+       struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
+       int irq = data->irq;
+       //only wm831x irq
+       if ((irq > wm831x->irq_base + WM831X_IRQ_TEMP_THW) &&( irq < wm831x->irq_base + WM831X_NUM_IRQS)) 
+       {
+               if(state)
+               wm831x_irq_enable(data);
+               else    
+               wm831x_irq_disable(data);
+               return 0;
+       }
+       else
+       {
+               printk("%s:irq number err!irq=%d\n",__FUNCTION__,irq);
+               return -EINVAL;
+       }
+
+
+}
+
 static struct irq_chip wm831x_irq_chip = {
        .name                   = "wm831x",
        .irq_bus_lock           = wm831x_irq_lock,
@@ -424,18 +463,63 @@ static struct irq_chip wm831x_irq_chip = {
        .irq_disable            = wm831x_irq_disable,
        .irq_enable             = wm831x_irq_enable,
        .irq_set_type           = wm831x_irq_set_type,
+       .irq_set_wake   = wm831x_irq_set_wake,
 };
 
-/* The processing of the primary interrupt occurs in a thread so that
- * we can interact with the device over I2C or SPI. */
-static irqreturn_t wm831x_irq_thread(int irq, void *data)
+#if WM831X_IRQ_LIST
+static void wm831x_handle_worker(struct work_struct *work)
 {
-       struct wm831x *wm831x = data;
+       struct wm831x *wm831x = container_of(work, struct wm831x, handle_work);
+       int irq;
+
+       while (1) {
+               unsigned long flags;
+               struct wm831x_handle_irq *hd = NULL;
+
+               spin_lock_irqsave(&wm831x->work_lock, flags);
+               if (!list_empty(&wm831x->handle_queue)) {
+                       hd = list_first_entry(&wm831x->handle_queue, struct wm831x_handle_irq, queue);
+                       list_del(&hd->queue);
+               }
+               spin_unlock_irqrestore(&wm831x->work_lock, flags);
+
+               if (!hd)        // trans_queue empty
+                       break;
+
+               irq = hd->irq;  //get wm831x intterupt status
+               //printk("%s:irq=%d\n",__FUNCTION__,irq);
+               
+               /*start to handle wm831x intterupt*/
+               handle_nested_irq(wm831x->irq_base + irq);
+       
+               kfree(hd);
+
+       }
+}
+#endif
+/* Main interrupt handling occurs in a workqueue since we need
+ * interrupts enabled to interact with the chip. */
+static void wm831x_irq_worker(struct work_struct *work)
+{
+       struct wm831x *wm831x = container_of(to_delayed_work(work), struct wm831x, irq_work);
        unsigned int i;
        int primary;
        int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
        int read[WM831X_NUM_IRQ_REGS] = { 0 };
        int *status;
+       unsigned long flags;
+       struct wm831x_handle_irq *hd;
+
+#if (WM831X_IRQ_TYPE != IRQF_TRIGGER_LOW)
+       /*mask wm831x irq at first*/    
+       int ret;
+       ret = wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG,
+                             WM831X_IRQ_IM_MASK, WM831X_IRQ_IM_EANBLE);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to mask irq: %d\n", ret);
+               goto out;
+       }
+#endif
 
        primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS);
        if (primary < 0) {
@@ -443,25 +527,15 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
                        primary);
                goto out;
        }
-
-       /* The touch interrupts are visible in the primary register as
-        * an optimisation; open code this to avoid complicating the
-        * main handling loop and so we can also skip iterating the
-        * descriptors.
-        */
-       if (primary & WM831X_TCHPD_INT)
-               handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD);
-       if (primary & WM831X_TCHDATA_INT)
-               handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA);
-       if (primary & (WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT))
-               goto out;
+       
+       mutex_lock(&wm831x->irq_lock);
 
        for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
                int offset = wm831x_irqs[i].reg - 1;
-
+               
                if (!(primary & wm831x_irqs[i].primary))
                        continue;
-
+               
                status = &status_regs[offset];
 
                /* Hopefully there should only be one register to read
@@ -473,7 +547,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
                                dev_err(wm831x->dev,
                                        "Failed to read IRQ status: %d\n",
                                        *status);
-                               goto out;
+                               goto out_lock;
                        }
 
                        read[offset] = 1;
@@ -482,21 +556,84 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
                /* Report it if it isn't masked, or forget the status. */
                if ((*status & ~wm831x->irq_masks_cur[offset])
                    & wm831x_irqs[i].mask)
+               {
+                       #if WM831X_IRQ_LIST
+                       /*add intterupt handle on list*/
+                       hd = kzalloc(sizeof(struct wm831x_handle_irq), GFP_KERNEL);
+                       if (!hd)
+                       {
+                               printk("err:%s:ENOMEM\n",__FUNCTION__);
+                               return ;
+                       }
+                       
+                       if(i == WM831X_IRQ_ON)
+                       wake_lock(&wm831x->handle_wake);                //keep wake while handle WM831X_IRQ_ON
+                       hd->irq = i;
+                       spin_lock_irqsave(&wm831x->work_lock, flags);
+                       list_add_tail(&hd->queue, &wm831x->handle_queue);
+                       spin_unlock_irqrestore(&wm831x->work_lock, flags);
+                       queue_work(wm831x->handle_wq, &wm831x->handle_work);
+                       
+                       #else
+                       if(i == WM831X_IRQ_ON)
+                       wake_lock(&wm831x->handle_wake);                //keep wake while handle WM831X_IRQ_ON
                        handle_nested_irq(wm831x->irq_base + i);
+                       
+                       #endif
+               }
+                       
                else
                        *status &= ~wm831x_irqs[i].mask;
        }
-
+       
+out_lock:      
+       mutex_unlock(&wm831x->irq_lock);
+       
 out:
-       /* Touchscreen interrupts are handled specially in the driver */
-       status_regs[0] &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
-
        for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
                if (status_regs[i])
                        wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
                                         status_regs[i]);
        }
+       
+#if (WM831X_IRQ_TYPE != IRQF_TRIGGER_LOW)      
+       ret = wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG,
+                             WM831X_IRQ_IM_MASK, 0);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to open irq: %d\n", ret);
+       }
+#endif
+#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW)
+       enable_irq(wm831x->irq);        
+#endif
+       wake_unlock(&wm831x->irq_wake);
 
+}
+/* The processing of the primary interrupt occurs in a thread so that
+ * we can interact with the device over I2C or SPI. */
+static irqreturn_t wm831x_irq_thread(int irq, void *data)
+{
+       struct wm831x *wm831x = data;
+       int msdelay = 0;
+       /* Shut the interrupt to the CPU up and schedule the actual
+        * handler; we can't check that the IRQ is asserted. */
+#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW)
+       disable_irq_nosync(irq);
+#endif
+       wake_lock(&wm831x->irq_wake);
+       if(wm831x->flag_suspend)
+       {
+               spin_lock(&wm831x->flag_lock);
+               wm831x->flag_suspend = 0;
+               spin_unlock(&wm831x->flag_lock);
+               msdelay = 50;   //wait for spi/i2c resume
+               printk("%s:msdelay=%d\n",__FUNCTION__,msdelay);
+       }
+       else
+               msdelay = 0;
+               
+       queue_delayed_work(wm831x->irq_wq, &wm831x->irq_work, msecs_to_jiffies(msdelay));
+       //printk("%s\n",__FUNCTION__);
        return IRQ_HANDLED;
 }
 
@@ -504,7 +641,7 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
 {
        struct wm831x_pdata *pdata = wm831x->dev->platform_data;
        int i, cur_irq, ret;
-
+       printk( "wm831x_irq_init:irq=%d,%d\n",irq,pdata->irq_base);
        mutex_init(&wm831x->irq_lock);
 
        /* Mask the individual interrupt sources */
@@ -515,34 +652,42 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
                                 0xffff);
        }
 
+       if (!irq) {
+               dev_warn(wm831x->dev,
+                        "No interrupt specified - functionality limited\n");
+               return 0;
+       }
+
        if (!pdata || !pdata->irq_base) {
                dev_err(wm831x->dev,
                        "No interrupt base specified, no interrupts\n");
                return 0;
        }
 
-       if (pdata->irq_cmos)
-               i = 0;
-       else
-               i = WM831X_IRQ_OD;
-
-       wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG,
-                       WM831X_IRQ_OD, i);
-
-       /* Try to flag /IRQ as a wake source; there are a number of
-        * unconditional wake sources in the PMIC so this isn't
-        * conditional but we don't actually care *too* much if it
-        * fails.
-        */
-       ret = enable_irq_wake(irq);
-       if (ret != 0) {
-               dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d\n",
-                        ret);
+       wm831x->irq_wq = create_singlethread_workqueue("wm831x-irq");
+       if (!wm831x->irq_wq) {
+               dev_err(wm831x->dev, "Failed to allocate IRQ worker\n");
+               return -ESRCH;
        }
 
+       
        wm831x->irq = irq;
+       wm831x->flag_suspend = 0;
        wm831x->irq_base = pdata->irq_base;
+       INIT_DELAYED_WORK(&wm831x->irq_work, wm831x_irq_worker);
+       wake_lock_init(&wm831x->irq_wake, WAKE_LOCK_SUSPEND, "wm831x_irq_wake");
+       wake_lock_init(&wm831x->handle_wake, WAKE_LOCK_SUSPEND, "wm831x_handle_wake");
+#if WM831X_IRQ_LIST
+       wm831x->handle_wq = create_workqueue("wm831x_handle_wq");
+       if (!wm831x->handle_wq) {
+               printk("cannot create workqueue\n");
+               return -EBUSY;
+       }
+       INIT_WORK(&wm831x->handle_work, wm831x_handle_worker);
+       INIT_LIST_HEAD(&wm831x->handle_queue);
 
+#endif
+       
        /* Register them with genirq */
        for (cur_irq = wm831x->irq_base;
             cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
@@ -560,23 +705,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
                irq_set_noprobe(cur_irq);
 #endif
        }
-
-       if (irq) {
-               ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
-                                          IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-                                          "wm831x", wm831x);
-               if (ret != 0) {
-                       dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
-                               irq, ret);
-                       return ret;
-               }
-       } else {
-               dev_warn(wm831x->dev,
-                        "No interrupt specified - functionality limited\n");
+#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW)
+       ret = request_threaded_irq(wm831x->irq, wm831x_irq_thread, NULL, 
+                                IRQF_TRIGGER_LOW| IRQF_ONESHOT,//IRQF_TRIGGER_FALLING, // 
+                                  "wm831x", wm831x);
+#else
+       ret = request_threaded_irq(wm831x->irq, wm831x_irq_thread, NULL, 
+                                IRQF_TRIGGER_FALLING, //IRQF_TRIGGER_LOW| IRQF_ONESHOT,// 
+                                  "wm831x", wm831x);
+#endif
+       if (ret != 0) {
+               dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
+                       wm831x->irq, ret);
+               return ret;
        }
 
-
-
+       enable_irq_wake(wm831x->irq); // so wm831x irq can wake up system
        /* Enable top level interrupts, we mask at secondary level */
        wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);