diff options
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-iproc-gpio.c | 118 |
1 files changed, 111 insertions, 7 deletions
diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index 3670f5ea7a12..7f7700716398 100644 --- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -66,6 +66,14 @@ #define GPIO_DRV_STRENGTH_BITS 3 #define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) +enum iproc_pinconf_param { + IPROC_PINCONF_DRIVE_STRENGTH = 0, + IPROC_PINCONF_BIAS_DISABLE, + IPROC_PINCONF_BIAS_PULL_UP, + IPROC_PINCONF_BIAS_PULL_DOWN, + IPROC_PINCON_MAX, +}; + /* * Iproc GPIO core * @@ -78,6 +86,10 @@ * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs * @pinmux_is_supported: flag to indicate this GPIO controller contains pins * that can be individually muxed to GPIO + * @pinconf_disable: contains a list of PINCONF parameters that need to be + * disabled + * @nr_pinconf_disable: total number of PINCONF parameters that need to be + * disabled * @pctl: pointer to pinctrl_dev * @pctldesc: pinctrl descriptor */ @@ -94,6 +106,9 @@ struct iproc_gpio { bool pinmux_is_supported; + enum pin_config_param *pinconf_disable; + unsigned int nr_pinconf_disable; + struct pinctrl_dev *pctl; struct pinctrl_desc pctldesc; }; @@ -360,6 +375,65 @@ static int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) return !!(readl(chip->base + offset) & BIT(shift)); } +/* + * Mapping of the iProc PINCONF parameters to the generic pin configuration + * parameters + */ +static const enum pin_config_param iproc_pinconf_disable_map[] = { + [IPROC_PINCONF_DRIVE_STRENGTH] = PIN_CONFIG_DRIVE_STRENGTH, + [IPROC_PINCONF_BIAS_DISABLE] = PIN_CONFIG_BIAS_DISABLE, + [IPROC_PINCONF_BIAS_PULL_UP] = PIN_CONFIG_BIAS_PULL_UP, + [IPROC_PINCONF_BIAS_PULL_DOWN] = PIN_CONFIG_BIAS_PULL_DOWN, +}; + +static bool iproc_pinconf_param_is_disabled(struct iproc_gpio *chip, + enum pin_config_param param) +{ + unsigned int i; + + if (!chip->nr_pinconf_disable) + return false; + + for (i = 0; i < chip->nr_pinconf_disable; i++) + if (chip->pinconf_disable[i] == param) + return true; + + return false; +} + +static int iproc_pinconf_disable_map_create(struct iproc_gpio *chip, + unsigned long disable_mask) +{ + unsigned int map_size = ARRAY_SIZE(iproc_pinconf_disable_map); + unsigned int bit, nbits = 0; + + /* figure out total number of PINCONF parameters to disable */ + for_each_set_bit(bit, &disable_mask, map_size) + nbits++; + + if (!nbits) + return 0; + + /* + * Allocate an array to store PINCONF parameters that need to be + * disabled + */ + chip->pinconf_disable = devm_kcalloc(chip->dev, nbits, + sizeof(*chip->pinconf_disable), + GFP_KERNEL); + if (!chip->pinconf_disable) + return -ENOMEM; + + chip->nr_pinconf_disable = nbits; + + /* now store these parameters */ + nbits = 0; + for_each_set_bit(bit, &disable_mask, map_size) + chip->pinconf_disable[nbits++] = iproc_pinconf_disable_map[bit]; + + return 0; +} + static int iproc_get_groups_count(struct pinctrl_dev *pctldev) { return 1; @@ -500,6 +574,9 @@ static int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, bool disable, pull_up; int ret; + if (iproc_pinconf_param_is_disabled(chip, param)) + return -ENOTSUPP; + switch (param) { case PIN_CONFIG_BIAS_DISABLE: iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); @@ -548,6 +625,10 @@ static int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); + + if (iproc_pinconf_param_is_disabled(chip, param)) + return -ENOTSUPP; + arg = pinconf_to_config_argument(configs[i]); switch (param) { @@ -633,11 +714,13 @@ static int iproc_gpio_register_pinconf(struct iproc_gpio *chip) } static const struct of_device_id iproc_gpio_of_match[] = { + { .compatible = "brcm,iproc-gpio" }, { .compatible = "brcm,cygnus-ccm-gpio" }, { .compatible = "brcm,cygnus-asiu-gpio" }, { .compatible = "brcm,cygnus-crmu-gpio" }, - { .compatible = "brcm,iproc-gpio" }, - { } + { .compatible = "brcm,iproc-nsp-gpio" }, + { .compatible = "brcm,iproc-stingray-gpio" }, + { /* sentinel */ } }; static int iproc_gpio_probe(struct platform_device *pdev) @@ -646,8 +729,17 @@ static int iproc_gpio_probe(struct platform_device *pdev) struct resource *res; struct iproc_gpio *chip; struct gpio_chip *gc; - u32 ngpios; + u32 ngpios, pinconf_disable_mask = 0; int irq, ret; + bool no_pinconf = false; + + /* NSP does not support drive strength config */ + if (of_device_is_compatible(dev->of_node, "brcm,iproc-nsp-gpio")) + pinconf_disable_mask = BIT(IPROC_PINCONF_DRIVE_STRENGTH); + /* Stingray does not support pinconf in this controller */ + else if (of_device_is_compatible(dev->of_node, + "brcm,iproc-stingray-gpio")) + no_pinconf = true; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -702,10 +794,22 @@ static int iproc_gpio_probe(struct platform_device *pdev) return ret; } - ret = iproc_gpio_register_pinconf(chip); - if (ret) { - dev_err(dev, "unable to register pinconf\n"); - goto err_rm_gpiochip; + if (!no_pinconf) { + ret = iproc_gpio_register_pinconf(chip); + if (ret) { + dev_err(dev, "unable to register pinconf\n"); + goto err_rm_gpiochip; + } + + if (pinconf_disable_mask) { + ret = iproc_pinconf_disable_map_create(chip, + pinconf_disable_mask); + if (ret) { + dev_err(dev, + "unable to create pinconf disable map\n"); + goto err_rm_gpiochip; + } + } } /* optional GPIO interrupt support */ |