Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[firefly-linux-kernel-4.4.55.git] / drivers / pinctrl / pinctrl-exynos5440.c
index 1376eb7305dbbf8d9a20f7f723f486e8ecb066d8..6038503ed929cc5c9332e7f4903412cf2d1d3611 100644 (file)
@@ -20,6 +20,9 @@
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/pinctrl/pinmux.h>
 #include <linux/pinctrl/pinconf.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of_irq.h>
 #include "core.h"
 
 /* EXYNOS5440 GPIO and Pinctrl register offsets */
@@ -37,6 +40,7 @@
 #define GPIO_DS1               0x2C
 
 #define EXYNOS5440_MAX_PINS            23
+#define EXYNOS5440_MAX_GPIO_INT        8
 #define PIN_NAME_LENGTH                10
 
 #define GROUP_SUFFIX           "-grp"
@@ -109,6 +113,7 @@ struct exynos5440_pmx_func {
 struct exynos5440_pinctrl_priv_data {
        void __iomem                    *reg_base;
        struct gpio_chip                *gc;
+       struct irq_domain               *irq_domain;
 
        const struct exynos5440_pin_group       *pin_groups;
        unsigned int                    nr_groups;
@@ -116,6 +121,16 @@ struct exynos5440_pinctrl_priv_data {
        unsigned int                    nr_functions;
 };
 
+/**
+ * struct exynos5440_gpio_intr_data: private data for gpio interrupts.
+ * @priv: driver's private runtime data.
+ * @gpio_int: gpio interrupt number.
+ */
+struct exynos5440_gpio_intr_data {
+       struct exynos5440_pinctrl_priv_data     *priv;
+       unsigned int                            gpio_int;
+};
+
 /* list of all possible config options supported */
 static struct pin_config {
        char            *prop_cfg;
@@ -286,7 +301,7 @@ static void exynos5440_dt_free_map(struct pinctrl_dev *pctldev,
 }
 
 /* list of pinctrl callbacks for the pinctrl core */
-static struct pinctrl_ops exynos5440_pctrl_ops = {
+static const struct pinctrl_ops exynos5440_pctrl_ops = {
        .get_groups_count       = exynos5440_get_group_count,
        .get_group_name         = exynos5440_get_group_name,
        .get_group_pins         = exynos5440_get_group_pins,
@@ -374,7 +389,7 @@ static int exynos5440_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
 }
 
 /* list of pinmux callbacks for the pinmux vertical in pinctrl core */
-static struct pinmux_ops exynos5440_pinmux_ops = {
+static const struct pinmux_ops exynos5440_pinmux_ops = {
        .get_functions_count    = exynos5440_get_functions_count,
        .get_function_name      = exynos5440_pinmux_get_fname,
        .get_function_groups    = exynos5440_pinmux_get_groups,
@@ -523,7 +538,7 @@ static int exynos5440_pinconf_group_get(struct pinctrl_dev *pctldev,
 }
 
 /* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */
-static struct pinconf_ops exynos5440_pinconf_ops = {
+static const struct pinconf_ops exynos5440_pinconf_ops = {
        .pin_config_get         = exynos5440_pinconf_get,
        .pin_config_set         = exynos5440_pinconf_set,
        .pin_config_group_get   = exynos5440_pinconf_group_get,
@@ -598,6 +613,22 @@ static int exynos5440_gpio_direction_output(struct gpio_chip *gc, unsigned offse
        return 0;
 }
 
+/* gpiolib gpio_to_irq callback function */
+static int exynos5440_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+       struct exynos5440_pinctrl_priv_data *priv = dev_get_drvdata(gc->dev);
+       unsigned int virq;
+
+       if (offset < 16 || offset > 23)
+               return -ENXIO;
+
+       if (!priv->irq_domain)
+               return -ENXIO;
+
+       virq = irq_create_mapping(priv->irq_domain, offset - 16);
+       return virq ? : -ENXIO;
+}
+
 /* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */
 static int exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev,
                        struct device_node *cfg_np, unsigned int **pin_list,
@@ -670,8 +701,10 @@ static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev,
 
                ret = exynos5440_pinctrl_parse_dt_pins(pdev, cfg_np,
                                        &pin_list, &npins);
-               if (ret)
-                       return ret;
+               if (ret) {
+                       gname = NULL;
+                       goto skip_to_pin_function;
+               }
 
                /* derive pin group name from the node name */
                gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN,
@@ -687,6 +720,7 @@ static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev,
                grp->num_pins = npins;
                grp++;
 
+skip_to_pin_function:
                ret = of_property_read_u32(cfg_np, "samsung,exynos5440-pin-function",
                                                &function);
                if (ret)
@@ -709,7 +743,7 @@ static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev,
                        return -ENOMEM;
                }
                func->groups[0] = gname;
-               func->num_groups = 1;
+               func->num_groups = gname ? 1 : 0;
                func->function = function;
                func++;
                func_idx++;
@@ -818,6 +852,7 @@ static int exynos5440_gpiolib_register(struct platform_device *pdev,
        gc->get = exynos5440_gpio_get;
        gc->direction_input = exynos5440_gpio_direction_input;
        gc->direction_output = exynos5440_gpio_direction_output;
+       gc->to_irq = exynos5440_gpio_to_irq;
        gc->label = "gpiolib-exynos5440";
        gc->owner = THIS_MODULE;
        ret = gpiochip_add(gc);
@@ -842,6 +877,110 @@ static int exynos5440_gpiolib_unregister(struct platform_device *pdev,
        return 0;
 }
 
+static void exynos5440_gpio_irq_unmask(struct irq_data *irqd)
+{
+       struct exynos5440_pinctrl_priv_data *d;
+       unsigned long gpio_int;
+
+       d = irq_data_get_irq_chip_data(irqd);
+       gpio_int = readl(d->reg_base + GPIO_INT);
+       gpio_int |= 1 << irqd->hwirq;
+       writel(gpio_int, d->reg_base + GPIO_INT);
+}
+
+static void exynos5440_gpio_irq_mask(struct irq_data *irqd)
+{
+       struct exynos5440_pinctrl_priv_data *d;
+       unsigned long gpio_int;
+
+       d = irq_data_get_irq_chip_data(irqd);
+       gpio_int = readl(d->reg_base + GPIO_INT);
+       gpio_int &= ~(1 << irqd->hwirq);
+       writel(gpio_int, d->reg_base + GPIO_INT);
+}
+
+/* irq_chip for gpio interrupts */
+static struct irq_chip exynos5440_gpio_irq_chip = {
+       .name           = "exynos5440_gpio_irq_chip",
+       .irq_unmask     = exynos5440_gpio_irq_unmask,
+       .irq_mask       = exynos5440_gpio_irq_mask,
+};
+
+/* interrupt handler for GPIO interrupts 0..7 */
+static irqreturn_t exynos5440_gpio_irq(int irq, void *data)
+{
+       struct exynos5440_gpio_intr_data *intd = data;
+       struct exynos5440_pinctrl_priv_data *d = intd->priv;
+       int virq;
+
+       virq = irq_linear_revmap(d->irq_domain, intd->gpio_int);
+       if (!virq)
+               return IRQ_NONE;
+       generic_handle_irq(virq);
+       return IRQ_HANDLED;
+}
+
+static int exynos5440_gpio_irq_map(struct irq_domain *h, unsigned int virq,
+                                       irq_hw_number_t hw)
+{
+       struct exynos5440_pinctrl_priv_data *d = h->host_data;
+
+       irq_set_chip_data(virq, d);
+       irq_set_chip_and_handler(virq, &exynos5440_gpio_irq_chip,
+                                       handle_level_irq);
+       set_irq_flags(virq, IRQF_VALID);
+       return 0;
+}
+
+/* irq domain callbacks for gpio interrupt controller */
+static const struct irq_domain_ops exynos5440_gpio_irqd_ops = {
+       .map    = exynos5440_gpio_irq_map,
+       .xlate  = irq_domain_xlate_twocell,
+};
+
+/* setup handling of gpio interrupts */
+static int exynos5440_gpio_irq_init(struct platform_device *pdev,
+                               struct exynos5440_pinctrl_priv_data *priv)
+{
+       struct device *dev = &pdev->dev;
+       struct exynos5440_gpio_intr_data *intd;
+       int i, irq, ret;
+
+       intd = devm_kzalloc(dev, sizeof(*intd) * EXYNOS5440_MAX_GPIO_INT,
+                                       GFP_KERNEL);
+       if (!intd) {
+               dev_err(dev, "failed to allocate memory for gpio intr data\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < EXYNOS5440_MAX_GPIO_INT; i++) {
+               irq = irq_of_parse_and_map(dev->of_node, i);
+               if (irq <= 0) {
+                       dev_err(dev, "irq parsing failed\n");
+                       return -EINVAL;
+               }
+
+               intd->gpio_int = i;
+               intd->priv = priv;
+               ret = devm_request_irq(dev, irq, exynos5440_gpio_irq,
+                                       0, dev_name(dev), intd++);
+               if (ret) {
+                       dev_err(dev, "irq request failed\n");
+                       return -ENXIO;
+               }
+       }
+
+       priv->irq_domain = irq_domain_add_linear(dev->of_node,
+                               EXYNOS5440_MAX_GPIO_INT,
+                               &exynos5440_gpio_irqd_ops, priv);
+       if (!priv->irq_domain) {
+               dev_err(dev, "failed to create irq domain\n");
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
 static int exynos5440_pinctrl_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -854,7 +993,7 @@ static int exynos5440_pinctrl_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       priv = devm_kzalloc(dev, sizeof(priv), GFP_KERNEL);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv) {
                dev_err(dev, "could not allocate memory for private data\n");
                return -ENOMEM;
@@ -880,6 +1019,12 @@ static int exynos5440_pinctrl_probe(struct platform_device *pdev)
                return ret;
        }
 
+       ret = exynos5440_gpio_irq_init(pdev, priv);
+       if (ret) {
+               dev_err(dev, "failed to setup gpio interrupts\n");
+               return ret;
+       }
+
        platform_set_drvdata(pdev, priv);
        dev_info(dev, "EXYNOS5440 pinctrl driver registered\n");
        return 0;