pinctrl: samsung: Add support for EXYNOS4X12
[cascardo/linux.git] / drivers / pinctrl / pinctrl-samsung.c
index 94e1378..81c9896 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
+#include <linux/irqdomain.h>
 
 #include "core.h"
 #include "pinctrl-samsung.h"
@@ -48,6 +49,11 @@ struct pin_config {
 
 static unsigned int pin_base = 0;
 
+static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc)
+{
+       return container_of(gc, struct samsung_pin_bank, gpio_chip);
+}
+
 /* check if the selector is a valid pin group selector */
 static int samsung_get_group_count(struct pinctrl_dev *pctldev)
 {
@@ -333,9 +339,12 @@ static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
        void __iomem *reg;
        u32 data, pin_offset, mask, shift;
 
+       bank = gc_to_pin_bank(range->gc);
        drvdata = pinctrl_dev_get_drvdata(pctldev);
 
-       pin_to_reg_bank(drvdata, offset, &reg, &pin_offset, &bank);
+       pin_offset = offset - bank->pin_base;
+       reg = drvdata->virt_base + bank->pctl_offset;
+
        mask = (1 << bank->func_width) - 1;
        shift = pin_offset * bank->func_width;
 
@@ -469,17 +478,16 @@ static struct pinconf_ops samsung_pinconf_ops = {
 /* gpiolib gpio_set callback function */
 static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
 {
+       struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
        void __iomem *reg;
-       u32 pin_offset, data;
-       struct samsung_pinctrl_drv_data *drvdata;
+       u32 data;
 
-       drvdata = dev_get_drvdata(gc->dev);
+       reg = bank->drvdata->virt_base + bank->pctl_offset;
 
-       pin_to_reg_bank(drvdata, offset, &reg, &pin_offset, NULL);
        data = readl(reg + DAT_REG);
-       data &= ~(1 << pin_offset);
+       data &= ~(1 << offset);
        if (value)
-               data |= 1 << pin_offset;
+               data |= 1 << offset;
        writel(data, reg + DAT_REG);
 }
 
@@ -487,14 +495,13 @@ static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
 static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
 {
        void __iomem *reg;
-       u32 pin_offset, data;
-       struct samsung_pinctrl_drv_data *drvdata;
+       u32 data;
+       struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
 
-       drvdata = dev_get_drvdata(gc->dev);
+       reg = bank->drvdata->virt_base + bank->pctl_offset;
 
-       pin_to_reg_bank(drvdata, offset, &reg, &pin_offset, NULL);
        data = readl(reg + DAT_REG);
-       data >>= pin_offset;
+       data >>= offset;
        data &= 1;
        return data;
 }
@@ -521,11 +528,28 @@ static int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
        return pinctrl_gpio_direction_output(gc->base + offset);
 }
 
+/*
+ * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin
+ * and a virtual IRQ, if not already present.
+ */
+static int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+       struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
+       unsigned int virq;
+
+       if (!bank->irq_domain)
+               return -ENXIO;
+
+       virq = irq_create_mapping(bank->irq_domain, offset);
+
+       return (virq) ? : -ENXIO;
+}
+
 /*
  * Parse the pin names listed in the 'samsung,pins' property and convert it
  * into a list of gpio numbers are create a pin group from it.
  */
-static int __init samsung_pinctrl_parse_dt_pins(struct platform_device *pdev,
+static int __devinit samsung_pinctrl_parse_dt_pins(struct platform_device *pdev,
                        struct device_node *cfg_np, struct pinctrl_desc *pctl,
                        unsigned int **pin_list, unsigned int *npins)
 {
@@ -572,7 +596,7 @@ static int __init samsung_pinctrl_parse_dt_pins(struct platform_device *pdev,
  * from device node of the pin-controller. A pin group is formed with all
  * the pins listed in the "samsung,pins" property.
  */
-static int __init samsung_pinctrl_parse_dt(struct platform_device *pdev,
+static int __devinit samsung_pinctrl_parse_dt(struct platform_device *pdev,
                                struct samsung_pinctrl_drv_data *drvdata)
 {
        struct device *dev = &pdev->dev;
@@ -667,7 +691,7 @@ static int __init samsung_pinctrl_parse_dt(struct platform_device *pdev,
 }
 
 /* register the pinctrl interface with the pinctrl subsystem */
-static int __init samsung_pinctrl_register(struct platform_device *pdev,
+static int __devinit samsung_pinctrl_register(struct platform_device *pdev,
                                struct samsung_pinctrl_drv_data *drvdata)
 {
        struct pinctrl_desc *ctrldesc = &drvdata->pctl;
@@ -724,12 +748,16 @@ static int __init samsung_pinctrl_register(struct platform_device *pdev,
                return -EINVAL;
        }
 
-       drvdata->grange.name = "samsung-pctrl-gpio-range";
-       drvdata->grange.id = 0;
-       drvdata->grange.base = drvdata->ctrl->base;
-       drvdata->grange.npins = drvdata->ctrl->nr_pins;
-       drvdata->grange.gc = drvdata->gc;
-       pinctrl_add_gpio_range(drvdata->pctl_dev, &drvdata->grange);
+       for (bank = 0; bank < drvdata->ctrl->nr_banks; ++bank) {
+               pin_bank = &drvdata->ctrl->pin_banks[bank];
+               pin_bank->grange.name = pin_bank->name;
+               pin_bank->grange.id = bank;
+               pin_bank->grange.pin_base = pin_bank->pin_base;
+               pin_bank->grange.base = pin_bank->gpio_chip.base;
+               pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
+               pin_bank->grange.gc = &pin_bank->gpio_chip;
+               pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange);
+       }
 
        ret = samsung_pinctrl_parse_dt(pdev, drvdata);
        if (ret) {
@@ -740,65 +768,87 @@ static int __init samsung_pinctrl_register(struct platform_device *pdev,
        return 0;
 }
 
+static const struct gpio_chip samsung_gpiolib_chip = {
+       .set = samsung_gpio_set,
+       .get = samsung_gpio_get,
+       .direction_input = samsung_gpio_direction_input,
+       .direction_output = samsung_gpio_direction_output,
+       .to_irq = samsung_gpio_to_irq,
+       .owner = THIS_MODULE,
+};
+
 /* register the gpiolib interface with the gpiolib subsystem */
-static int __init samsung_gpiolib_register(struct platform_device *pdev,
+static int __devinit samsung_gpiolib_register(struct platform_device *pdev,
                                struct samsung_pinctrl_drv_data *drvdata)
 {
+       struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
+       struct samsung_pin_bank *bank = ctrl->pin_banks;
        struct gpio_chip *gc;
        int ret;
+       int i;
 
-       gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
-       if (!gc) {
-               dev_err(&pdev->dev, "mem alloc for gpio_chip failed\n");
-               return -ENOMEM;
-       }
-
-       drvdata->gc = gc;
-       gc->base = drvdata->ctrl->base;
-       gc->ngpio = drvdata->ctrl->nr_pins;
-       gc->dev = &pdev->dev;
-       gc->set = samsung_gpio_set;
-       gc->get = samsung_gpio_get;
-       gc->direction_input = samsung_gpio_direction_input;
-       gc->direction_output = samsung_gpio_direction_output;
-       gc->label = drvdata->ctrl->label;
-       gc->owner = THIS_MODULE;
-       ret = gpiochip_add(gc);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register gpio_chip %s, error "
-                                       "code: %d\n", gc->label, ret);
-               return ret;
+       for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+               bank->gpio_chip = samsung_gpiolib_chip;
+
+               gc = &bank->gpio_chip;
+               gc->base = ctrl->base + bank->pin_base;
+               gc->ngpio = bank->nr_pins;
+               gc->dev = &pdev->dev;
+               gc->of_node = bank->of_node;
+               gc->label = bank->name;
+
+               ret = gpiochip_add(gc);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n",
+                                                       gc->label, ret);
+                       goto fail;
+               }
        }
 
        return 0;
+
+fail:
+       for (--i, --bank; i >= 0; --i, --bank)
+               if (gpiochip_remove(&bank->gpio_chip))
+                       dev_err(&pdev->dev, "gpio chip %s remove failed\n",
+                                                       bank->gpio_chip.label);
+       return ret;
 }
 
 /* unregister the gpiolib interface with the gpiolib subsystem */
-static int __init samsung_gpiolib_unregister(struct platform_device *pdev,
+static int __devinit samsung_gpiolib_unregister(struct platform_device *pdev,
                                struct samsung_pinctrl_drv_data *drvdata)
 {
-       int ret = gpiochip_remove(drvdata->gc);
-       if (ret) {
+       struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
+       struct samsung_pin_bank *bank = ctrl->pin_banks;
+       int ret = 0;
+       int i;
+
+       for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank)
+               ret = gpiochip_remove(&bank->gpio_chip);
+
+       if (ret)
                dev_err(&pdev->dev, "gpio chip remove failed\n");
-               return ret;
-       }
-       return 0;
+
+       return ret;
 }
 
 static const struct of_device_id samsung_pinctrl_dt_match[];
 
 /* retrieve the soc specific data */
 static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
+                               struct samsung_pinctrl_drv_data *d,
                                struct platform_device *pdev)
 {
        int id;
        const struct of_device_id *match;
-       const struct device_node *node = pdev->dev.of_node;
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *np;
        struct samsung_pin_ctrl *ctrl;
        struct samsung_pin_bank *bank;
        int i;
 
-       id = of_alias_get_id(pdev->dev.of_node, "pinctrl");
+       id = of_alias_get_id(node, "pinctrl");
        if (id < 0) {
                dev_err(&pdev->dev, "failed to get alias id\n");
                return NULL;
@@ -808,11 +858,20 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
 
        bank = ctrl->pin_banks;
        for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+               bank->drvdata = d;
                bank->pin_base = ctrl->nr_pins;
                ctrl->nr_pins += bank->nr_pins;
-               if (bank->eint_type == EINT_TYPE_GPIO) {
-                       bank->irq_base = ctrl->nr_gint;
-                       ctrl->nr_gint += bank->nr_pins;
+       }
+
+       for_each_child_of_node(node, np) {
+               if (!of_find_property(np, "gpio-controller", NULL))
+                       continue;
+               bank = ctrl->pin_banks;
+               for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+                       if (!strcmp(bank->name, np->name)) {
+                               bank->of_node = np;
+                               break;
+                       }
                }
        }
 
@@ -835,18 +894,18 @@ static int __devinit samsung_pinctrl_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       ctrl = samsung_pinctrl_get_soc_data(pdev);
-       if (!ctrl) {
-               dev_err(&pdev->dev, "driver data not available\n");
-               return -EINVAL;
-       }
-
        drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
        if (!drvdata) {
                dev_err(dev, "failed to allocate memory for driver's "
                                "private data\n");
                return -ENOMEM;
        }
+
+       ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev);
+       if (!ctrl) {
+               dev_err(&pdev->dev, "driver data not available\n");
+               return -EINVAL;
+       }
        drvdata->ctrl = ctrl;
        drvdata->dev = dev;
 
@@ -888,6 +947,8 @@ static int __devinit samsung_pinctrl_probe(struct platform_device *pdev)
 static const struct of_device_id samsung_pinctrl_dt_match[] = {
        { .compatible = "samsung,pinctrl-exynos4210",
                .data = (void *)exynos4210_pin_ctrl },
+       { .compatible = "samsung,pinctrl-exynos4x12",
+               .data = (void *)exynos4x12_pin_ctrl },
        {},
 };
 MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match);