From patchwork Thu Feb 25 21:44:38 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joachim Eastwood X-Patchwork-Id: 588383 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id C460414033E for ; Fri, 26 Feb 2016 08:44:53 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=D3Yj8eKq; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750830AbcBYVox (ORCPT ); Thu, 25 Feb 2016 16:44:53 -0500 Received: from mail-lf0-f54.google.com ([209.85.215.54]:35349 "EHLO mail-lf0-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750717AbcBYVow (ORCPT ); Thu, 25 Feb 2016 16:44:52 -0500 Received: by mail-lf0-f54.google.com with SMTP id l143so41925380lfe.2 for ; Thu, 25 Feb 2016 13:44:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=5Roj/xOJdSLDJMsO09PCZoj1kn80FIdPXoS2criGszQ=; b=D3Yj8eKqEkULlZT9EYETTVRCrBvSZK3GvK8RH+9kt7Tijq+HQF28UIE/IQNiPIdkpT GBZ3oBTNYrWO41qAX3OWIV11W/eR0ok8DrIeCsa4LlANCmz/A7BFmH7bgxru6F0CTlBZ V3h/dXj2M/0r0QDkYD9PbegVx22Fv1AWwQmILao2XMSZ5CC87++K11pwHZeN0W40V60Q pyK3wXV4rGAtkbyUmZrEdgtAcdwBz6UGZ72sij3r+kJvA3+bJi017FIpEZqT6wqwzWpz r8QCE367iF3Jl2yvi+m4WDBgPl/DyjRepNT2feXqd7gPX94UH+gfznhGBv7ThAjuKj/s 35LQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=5Roj/xOJdSLDJMsO09PCZoj1kn80FIdPXoS2criGszQ=; b=d3yj/TITer27m7vcEfSRuzoZagdrUgoXLmG0UuIXW46Ugy1CLWwcaBY4lJk+M0rvOx o08//WvinR/q/52wUVH6wyFLjmk5s2D4WKCdiJNOayi1LETyV+PmXh7OU3XF3TVkT5rD fQLiLauMnHB905tnOYllRmrrbWIqST3Kn9lWCP0kn3Bydo94zqoqNoHZXQzYqvfpo8yF LHoW6Jy2K3aSANFehVTZGzWvUSoFSRAXIodpLpiwkBsqM9lrpVoq3nPY2GNksB2rvP2K m383jNfHtXyhfumLNRE11i/zyz9G+iBr31vbAM4phXw/Lez10144bLCr1nTPR+QiO0oK eEJA== X-Gm-Message-State: AG10YOQ4r+Y4t4MUZ0594pJmTl+b7NOr+txxtKz25B3sNSKThstKab1xxuk9MPyyosM0/w== X-Received: by 10.25.28.195 with SMTP id c186mr14392697lfc.22.1456436690682; Thu, 25 Feb 2016 13:44:50 -0800 (PST) Received: from localhost.localdomain (141.89-11-213.nextgentel.com. [89.11.213.141]) by smtp.gmail.com with ESMTPSA id yf3sm553391lbb.37.2016.02.25.13.44.49 (version=TLS1_1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 25 Feb 2016 13:44:50 -0800 (PST) From: Joachim Eastwood To: linus.walleij@linaro.org Cc: Joachim Eastwood , linux-gpio@vger.kernel.org Subject: [PATCH v2 2/3] pinctrl: lpc18xx: add nxp, gpio-pin-interrupt property Date: Thu, 25 Feb 2016 22:44:38 +0100 Message-Id: <1456436679-22077-3-git-send-email-manabian@gmail.com> X-Mailer: git-send-email 1.8.0 In-Reply-To: <1456436679-22077-1-git-send-email-manabian@gmail.com> References: <1456436679-22077-1-git-send-email-manabian@gmail.com> Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org 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 --- drivers/pinctrl/pinctrl-lpc18xx.c | 143 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 137 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/pinctrl-lpc18xx.c b/drivers/pinctrl/pinctrl-lpc18xx.c index f0bebbe0682b..cfd3984bc10c 100644 --- a/drivers/pinctrl/pinctrl-lpc18xx.c +++ b/drivers/pinctrl/pinctrl-lpc18xx.c @@ -49,6 +49,18 @@ #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, ®); else - ret = lpc18xx_pconf_set_pin(pctldev, param, param_val, ®, pin_cap); + ret = lpc18xx_pconf_set_pin(pctldev, param, param_val, ®, 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, };