Merge tag 'gpio-for-v3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[firefly-linux-kernel-4.4.55.git] / drivers / gpio / gpio-mxc.c
index 04691d3abe6061feade251bf9c363fc998b1f21c..4db460b6ecf7dbf1ffe7bfd14a1361d271609667 100644 (file)
@@ -37,7 +37,8 @@
 enum mxc_gpio_hwtype {
        IMX1_GPIO,      /* runs on i.mx1 */
        IMX21_GPIO,     /* runs on i.mx21 and i.mx27 */
-       IMX31_GPIO,     /* runs on all other i.mx */
+       IMX31_GPIO,     /* runs on i.mx31 */
+       IMX35_GPIO,     /* runs on all other i.mx */
 };
 
 /* device type dependent stuff */
@@ -49,6 +50,7 @@ struct mxc_gpio_hwdata {
        unsigned icr2_reg;
        unsigned imr_reg;
        unsigned isr_reg;
+       int edge_sel_reg;
        unsigned low_level;
        unsigned high_level;
        unsigned rise_edge;
@@ -73,6 +75,7 @@ static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
        .icr2_reg       = 0x2c,
        .imr_reg        = 0x30,
        .isr_reg        = 0x34,
+       .edge_sel_reg   = -EINVAL,
        .low_level      = 0x03,
        .high_level     = 0x02,
        .rise_edge      = 0x00,
@@ -87,6 +90,22 @@ static struct mxc_gpio_hwdata imx31_gpio_hwdata = {
        .icr2_reg       = 0x10,
        .imr_reg        = 0x14,
        .isr_reg        = 0x18,
+       .edge_sel_reg   = -EINVAL,
+       .low_level      = 0x00,
+       .high_level     = 0x01,
+       .rise_edge      = 0x02,
+       .fall_edge      = 0x03,
+};
+
+static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
+       .dr_reg         = 0x00,
+       .gdir_reg       = 0x04,
+       .psr_reg        = 0x08,
+       .icr1_reg       = 0x0c,
+       .icr2_reg       = 0x10,
+       .imr_reg        = 0x14,
+       .isr_reg        = 0x18,
+       .edge_sel_reg   = 0x1c,
        .low_level      = 0x00,
        .high_level     = 0x01,
        .rise_edge      = 0x02,
@@ -103,12 +122,13 @@ static struct mxc_gpio_hwdata *mxc_gpio_hwdata;
 #define GPIO_ICR2              (mxc_gpio_hwdata->icr2_reg)
 #define GPIO_IMR               (mxc_gpio_hwdata->imr_reg)
 #define GPIO_ISR               (mxc_gpio_hwdata->isr_reg)
+#define GPIO_EDGE_SEL          (mxc_gpio_hwdata->edge_sel_reg)
 
 #define GPIO_INT_LOW_LEV       (mxc_gpio_hwdata->low_level)
 #define GPIO_INT_HIGH_LEV      (mxc_gpio_hwdata->high_level)
 #define GPIO_INT_RISE_EDGE     (mxc_gpio_hwdata->rise_edge)
 #define GPIO_INT_FALL_EDGE     (mxc_gpio_hwdata->fall_edge)
-#define GPIO_INT_NONE          0x4
+#define GPIO_INT_BOTH_EDGES    0x4
 
 static struct platform_device_id mxc_gpio_devtype[] = {
        {
@@ -120,6 +140,9 @@ static struct platform_device_id mxc_gpio_devtype[] = {
        }, {
                .name = "imx31-gpio",
                .driver_data = IMX31_GPIO,
+       }, {
+               .name = "imx35-gpio",
+               .driver_data = IMX35_GPIO,
        }, {
                /* sentinel */
        }
@@ -129,6 +152,7 @@ static const struct of_device_id mxc_gpio_dt_ids[] = {
        { .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
        { .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
        { .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
+       { .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
        { /* sentinel */ }
 };
 
@@ -160,15 +184,19 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
                edge = GPIO_INT_FALL_EDGE;
                break;
        case IRQ_TYPE_EDGE_BOTH:
-               val = gpio_get_value(gpio);
-               if (val) {
-                       edge = GPIO_INT_LOW_LEV;
-                       pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
+               if (GPIO_EDGE_SEL >= 0) {
+                       edge = GPIO_INT_BOTH_EDGES;
                } else {
-                       edge = GPIO_INT_HIGH_LEV;
-                       pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
+                       val = gpio_get_value(gpio);
+                       if (val) {
+                               edge = GPIO_INT_LOW_LEV;
+                               pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
+                       } else {
+                               edge = GPIO_INT_HIGH_LEV;
+                               pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
+                       }
+                       port->both_edges |= 1 << gpio_idx;
                }
-               port->both_edges |= 1 << gpio_idx;
                break;
        case IRQ_TYPE_LEVEL_LOW:
                edge = GPIO_INT_LOW_LEV;
@@ -180,10 +208,23 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
                return -EINVAL;
        }
 
-       reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* ICR1 or ICR2 */
-       bit = gpio_idx & 0xf;
-       val = readl(reg) & ~(0x3 << (bit << 1));
-       writel(val | (edge << (bit << 1)), reg);
+       if (GPIO_EDGE_SEL >= 0) {
+               val = readl(port->base + GPIO_EDGE_SEL);
+               if (edge == GPIO_INT_BOTH_EDGES)
+                       writel(val | (1 << gpio_idx),
+                               port->base + GPIO_EDGE_SEL);
+               else
+                       writel(val & ~(1 << gpio_idx),
+                               port->base + GPIO_EDGE_SEL);
+       }
+
+       if (edge != GPIO_INT_BOTH_EDGES) {
+               reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
+               bit = gpio_idx & 0xf;
+               val = readl(reg) & ~(0x3 << (bit << 1));
+               writel(val | (edge << (bit << 1)), reg);
+       }
+
        writel(1 << gpio_idx, port->base + GPIO_ISR);
 
        return 0;
@@ -335,7 +376,9 @@ static void __devinit mxc_gpio_get_hw(struct platform_device *pdev)
                return;
        }
 
-       if (hwtype == IMX31_GPIO)
+       if (hwtype == IMX35_GPIO)
+               mxc_gpio_hwdata = &imx35_gpio_hwdata;
+       else if (hwtype == IMX31_GPIO)
                mxc_gpio_hwdata = &imx31_gpio_hwdata;
        else
                mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;