]> git.karo-electronics.de Git - linux-beck.git/commitdiff
pinctrl: lpc18xx: add nxp,gpio-pin-interrupt property
authorJoachim Eastwood <manabian@gmail.com>
Thu, 25 Feb 2016 21:44:38 +0000 (22:44 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Mon, 7 Mar 2016 03:44:30 +0000 (10:44 +0700)
Add support for setting up GPIO pin interrupts in the lpc18xx pinctrl
driver. The LPC18xx SCU contain two registers that sets up the signal
routing to the GPIO pin interrupt (PINT) block. The routing uses the
GPIO namespace and not the pin namespace so a lookup is preformed on
the pin.

Routing configuration is done in the device tree by using the new
nxp,gpio-pin-interrupt property. This property takes single parameter
which sets the PINT hwirq for the GPIO.

Signed-off-by: Joachim Eastwood <manabian@gmail.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/pinctrl/pinctrl-lpc18xx.c

index ed1cfa7e43dd946e51c3fde4a527bff1eb52d794..b1767f7e45d10ed582c174165d75fa80b9606343 100644 (file)
 
 #define LPC18XX_SCU_FUNC_PER_PIN       8
 
+/* LPC18XX SCU pin interrupt select registers */
+#define LPC18XX_SCU_PINTSEL0           0xe00
+#define LPC18XX_SCU_PINTSEL1           0xe04
+#define LPC18XX_SCU_PINTSEL_VAL_MASK   0xff
+#define LPC18XX_SCU_PINTSEL_PORT_SHIFT 5
+#define LPC18XX_SCU_IRQ_PER_PINTSEL    4
+#define LPC18XX_GPIO_PINS_PER_PORT     32
+#define LPC18XX_GPIO_PIN_INT_MAX       8
+
+#define LPC18XX_SCU_PINTSEL_VAL(val, n) \
+       ((val) << (((n) % LPC18XX_SCU_IRQ_PER_PINTSEL) * 8))
+
 /* LPC18xx pin types */
 enum {
        TYPE_ND,        /* Normal-drive */
@@ -618,6 +630,25 @@ static const struct pinctrl_pin_desc lpc18xx_pins[] = {
        LPC18XX_PIN(i2c0_sda, PIN_I2C0_SDA),
 };
 
+/**
+ * enum lpc18xx_pin_config_param - possible pin configuration parameters
+ * @PIN_CONFIG_GPIO_PIN_INT: route gpio to the gpio pin interrupt
+ *     controller.
+ */
+enum lpc18xx_pin_config_param {
+       PIN_CONFIG_GPIO_PIN_INT = PIN_CONFIG_END + 1,
+};
+
+static const struct pinconf_generic_params lpc18xx_params[] = {
+       {"nxp,gpio-pin-interrupt", PIN_CONFIG_GPIO_PIN_INT, 0},
+};
+
+#ifdef CONFIG_DEBUG_FS
+static const struct pin_config_item lpc18xx_conf_items[ARRAY_SIZE(lpc18xx_params)] = {
+       PCONFDUMP(PIN_CONFIG_GPIO_PIN_INT, "gpio pin int", NULL, true),
+};
+#endif
+
 static int lpc18xx_pconf_get_usb1(enum pin_config_param param, int *arg, u32 reg)
 {
        switch (param) {
@@ -693,7 +724,71 @@ static int lpc18xx_pconf_get_i2c0(enum pin_config_param param, int *arg, u32 reg
        return 0;
 }
 
-static int lpc18xx_pconf_get_pin(enum pin_config_param param, int *arg, u32 reg,
+static int lpc18xx_pin_to_gpio(struct pinctrl_dev *pctldev, unsigned pin)
+{
+       struct pinctrl_gpio_range *range;
+
+       range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin);
+       if (!range)
+               return -EINVAL;
+
+       return pin - range->pin_base + range->base;
+}
+
+static int lpc18xx_get_pintsel(void __iomem *addr, u32 val, int *arg)
+{
+       u32 reg_val;
+       int i;
+
+       reg_val = readl(addr);
+       for (i = 0; i < LPC18XX_SCU_IRQ_PER_PINTSEL; i++) {
+               if ((reg_val & LPC18XX_SCU_PINTSEL_VAL_MASK) == val)
+                       return 0;
+
+               reg_val >>= BITS_PER_BYTE;
+               *arg += 1;
+       }
+
+       return -EINVAL;
+}
+
+static u32 lpc18xx_gpio_to_pintsel_val(int gpio)
+{
+       unsigned int gpio_port, gpio_pin;
+
+       gpio_port = gpio / LPC18XX_GPIO_PINS_PER_PORT;
+       gpio_pin  = gpio % LPC18XX_GPIO_PINS_PER_PORT;
+
+       return gpio_pin | (gpio_port << LPC18XX_SCU_PINTSEL_PORT_SHIFT);
+}
+
+static int lpc18xx_pconf_get_gpio_pin_int(struct pinctrl_dev *pctldev,
+                                         int *arg, unsigned pin)
+{
+       struct lpc18xx_scu_data *scu = pinctrl_dev_get_drvdata(pctldev);
+       int gpio, ret;
+       u32 val;
+
+       gpio = lpc18xx_pin_to_gpio(pctldev, pin);
+       if (gpio < 0)
+               return -ENOTSUPP;
+
+       val = lpc18xx_gpio_to_pintsel_val(gpio);
+
+       /*
+        * Check if this pin has been enabled as a interrupt in any of the two
+        * PINTSEL registers. *arg indicates which interrupt number (0-7).
+        */
+       *arg = 0;
+       ret = lpc18xx_get_pintsel(scu->base + LPC18XX_SCU_PINTSEL0, val, arg);
+       if (ret == 0)
+               return ret;
+
+       return lpc18xx_get_pintsel(scu->base + LPC18XX_SCU_PINTSEL1, val, arg);
+}
+
+static int lpc18xx_pconf_get_pin(struct pinctrl_dev *pctldev, unsigned param,
+                                int *arg, u32 reg, unsigned pin,
                                 struct lpc18xx_pin_caps *pin_cap)
 {
        switch (param) {
@@ -755,6 +850,9 @@ static int lpc18xx_pconf_get_pin(enum pin_config_param param, int *arg, u32 reg,
                }
                break;
 
+       case PIN_CONFIG_GPIO_PIN_INT:
+               return lpc18xx_pconf_get_gpio_pin_int(pctldev, arg, pin);
+
        default:
                return -ENOTSUPP;
        }
@@ -794,7 +892,7 @@ static int lpc18xx_pconf_get(struct pinctrl_dev *pctldev, unsigned pin,
        else if (pin_cap->type == TYPE_USB1)
                ret = lpc18xx_pconf_get_usb1(param, &arg, reg);
        else
-               ret = lpc18xx_pconf_get_pin(param, &arg, reg, pin_cap);
+               ret = lpc18xx_pconf_get_pin(pctldev, param, &arg, reg, pin, pin_cap);
 
        if (ret < 0)
                return ret;
@@ -883,9 +981,34 @@ static int lpc18xx_pconf_set_i2c0(struct pinctrl_dev *pctldev,
        return 0;
 }
 
-static int lpc18xx_pconf_set_pin(struct pinctrl_dev *pctldev,
-                                enum pin_config_param param,
-                                u16 param_val, u32 *reg,
+static int lpc18xx_pconf_set_gpio_pin_int(struct pinctrl_dev *pctldev,
+                                         u16 param_val, unsigned pin)
+{
+       struct lpc18xx_scu_data *scu = pinctrl_dev_get_drvdata(pctldev);
+       u32 val, reg_val, reg_offset = LPC18XX_SCU_PINTSEL0;
+       int gpio;
+
+       if (param_val >= LPC18XX_GPIO_PIN_INT_MAX)
+               return -EINVAL;
+
+       gpio = lpc18xx_pin_to_gpio(pctldev, pin);
+       if (gpio < 0)
+               return -ENOTSUPP;
+
+       val = lpc18xx_gpio_to_pintsel_val(gpio);
+
+       reg_offset += (param_val / LPC18XX_SCU_IRQ_PER_PINTSEL) * sizeof(u32);
+
+       reg_val = readl(scu->base + reg_offset);
+       reg_val &= ~LPC18XX_SCU_PINTSEL_VAL(LPC18XX_SCU_PINTSEL_VAL_MASK, param_val);
+       reg_val |= LPC18XX_SCU_PINTSEL_VAL(val, param_val);
+       writel(reg_val, scu->base + reg_offset);
+
+       return 0;
+}
+
+static int lpc18xx_pconf_set_pin(struct pinctrl_dev *pctldev, unsigned param,
+                                u16 param_val, u32 *reg, unsigned pin,
                                 struct lpc18xx_pin_caps *pin_cap)
 {
        switch (param) {
@@ -948,6 +1071,9 @@ static int lpc18xx_pconf_set_pin(struct pinctrl_dev *pctldev,
                *reg |= param_val << LPC18XX_SCU_PIN_EHD_POS;
                break;
 
+       case PIN_CONFIG_GPIO_PIN_INT:
+               return lpc18xx_pconf_set_gpio_pin_int(pctldev, param_val, pin);
+
        default:
                dev_err(pctldev->dev, "Property not supported\n");
                return -ENOTSUPP;
@@ -982,7 +1108,7 @@ static int lpc18xx_pconf_set(struct pinctrl_dev *pctldev, unsigned pin,
                else if (pin_cap->type == TYPE_USB1)
                        ret = lpc18xx_pconf_set_usb1(pctldev, param, param_val, &reg);
                else
-                       ret = lpc18xx_pconf_set_pin(pctldev, param, param_val, &reg, pin_cap);
+                       ret = lpc18xx_pconf_set_pin(pctldev, param, param_val, &reg, pin, pin_cap);
 
                if (ret)
                        return ret;
@@ -1136,6 +1262,11 @@ static struct pinctrl_desc lpc18xx_scu_desc = {
        .pctlops = &lpc18xx_pctl_ops,
        .pmxops = &lpc18xx_pmx_ops,
        .confops = &lpc18xx_pconf_ops,
+       .num_custom_params = ARRAY_SIZE(lpc18xx_params),
+       .custom_params = lpc18xx_params,
+#ifdef CONFIG_DEBUG_FS
+       .custom_conf_items = lpc18xx_conf_items,
+#endif
        .owner = THIS_MODULE,
 };