diff options
author | Caleb Connolly <caleb.connolly@linaro.org> | 2025-04-10 10:52:38 +0200 |
---|---|---|
committer | Caleb Connolly <caleb.connolly@linaro.org> | 2025-04-11 15:30:21 +0200 |
commit | 91ba4976c05882f88232eaa4fc5eb9192701dbe3 (patch) | |
tree | 341bf8a6d14bfc2dfc306111538f999b93c426ea /drivers | |
parent | 9c607005c51976b5e0301461bfc0f38d5bdebd0e (diff) |
pinctrl: qcom: handle reserved ranges
Some Qualcomm boards feature reserved ranges of pins which are protected
by firmware. Attempting to read or write any registers associated with
these pins results the board resetting.
Add support for parsing these ranges from devicetree and ensure that the
pinctrl and GPIO drivers don't try to interact with these pins.
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250410-topic-sm8x50-pinctrl-reserved-ranges-v2-1-654488392b9a@linaro.org
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/msm_gpio.c | 10 | ||||
-rw-r--r-- | drivers/pinctrl/qcom/pinctrl-qcom.c | 67 |
2 files changed, 77 insertions, 0 deletions
diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c index 54afd44fbd3..6783fc756f4 100644 --- a/drivers/gpio/msm_gpio.c +++ b/drivers/gpio/msm_gpio.c @@ -151,6 +151,9 @@ static int msm_gpio_direction_output(struct udevice *dev, unsigned int gpio, static int msm_gpio_set_flags(struct udevice *dev, unsigned int gpio, ulong flags) { + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return -EPERM; + if (flags & GPIOD_IS_OUT_ACTIVE) { return msm_gpio_direction_output(dev, gpio, 1); } else if (flags & GPIOD_IS_OUT) { @@ -193,6 +196,9 @@ static int msm_gpio_get_value(struct udevice *dev, unsigned int gpio) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return -EPERM; + if (qcom_is_special_pin(priv->pin_data, gpio)) return msm_gpio_get_value_special(priv, gpio); @@ -232,6 +238,10 @@ static int msm_gpio_get_function(struct udevice *dev, unsigned int gpio) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return GPIOF_UNKNOWN; + + /* Always NOP for special pins, assume they're in the correct state */ if (qcom_is_special_pin(priv->pin_data, gpio)) return msm_gpio_get_function_special(priv, gpio); diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.c b/drivers/pinctrl/qcom/pinctrl-qcom.c index 24d031947a3..c95db56bc47 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcom.c +++ b/drivers/pinctrl/qcom/pinctrl-qcom.c @@ -15,14 +15,18 @@ #include <asm/gpio.h> #include <dm/pinctrl.h> #include <linux/bitops.h> +#include <linux/bitmap.h> #include <linux/bug.h> #include <mach/gpio.h> #include "pinctrl-qcom.h" +#define MSM_PINCTRL_MAX_PINS 256 + struct msm_pinctrl_priv { phys_addr_t base; struct msm_pinctrl_data *data; + DECLARE_BITMAP(reserved_map, MSM_PINCTRL_MAX_PINS); }; #define GPIO_CONFIG_REG(priv, x) \ @@ -71,13 +75,60 @@ static const char *msm_get_function_name(struct udevice *dev, return priv->data->get_function_name(dev, selector); } +static int msm_pinctrl_parse_ranges(struct udevice *dev) +{ + struct msm_pinctrl_priv *priv = dev_get_priv(dev); + ofnode node = dev_ofnode(dev); + int ret, count, i; + u32 *ranges; + + if (ofnode_read_prop(node, "gpio-reserved-ranges", &count)) { + if (count % 2 == 1) { + dev_err(dev, "gpio-reserved-ranges must be a multiple of 2\n"); + return -EINVAL; + } + + ranges = malloc(count); + if (!ranges) + return -ENOMEM; + + ret = ofnode_read_u32_array(node, "gpio-reserved-ranges", ranges, count / 4); + if (ret) { + dev_err(dev, "failed to read gpio-reserved-ranges array (%d)\n", ret); + return ret; + } + + for (i = 0; i < count / 4; i += 2) { + if (ranges[i] >= MSM_PINCTRL_MAX_PINS || + (ranges[i] + ranges[i + 1]) >= MSM_PINCTRL_MAX_PINS) { + dev_err(dev, "invalid reserved-range (%d;%d)\n", + ranges[i], ranges[i + 1]); + return -EINVAL; + } + + bitmap_set(priv->reserved_map, ranges[i], ranges[i + 1]); + } + + free(ranges); + } + + return 0; +} + static int msm_pinctrl_probe(struct udevice *dev) { struct msm_pinctrl_priv *priv = dev_get_priv(dev); + int ret; priv->base = dev_read_addr(dev); priv->data = (struct msm_pinctrl_data *)dev_get_driver_data(dev); + ret = msm_pinctrl_parse_ranges(dev); + if (ret) { + printf("Couldn't parse reserved GPIO ranges!\n"); + return ret; + } + return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0; } @@ -97,6 +148,9 @@ static int msm_pinmux_set(struct udevice *dev, unsigned int pin_selector, if (func < 0) return func; + if (msm_pinctrl_is_reserved(dev, pin_selector)) + return -EPERM; + /* Always NOP for special pins, assume they're in the correct state */ if (qcom_is_special_pin(&priv->data->pin_data, pin_selector)) return 0; @@ -145,6 +199,9 @@ static int msm_pinconf_set(struct udevice *dev, unsigned int pin_selector, { struct msm_pinctrl_priv *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev, pin_selector)) + return -EPERM; + if (qcom_is_special_pin(&priv->data->pin_data, pin_selector)) return msm_pinconf_set_special(priv, pin_selector, param, argument); @@ -241,3 +298,13 @@ U_BOOT_DRIVER(pinctrl_qcom) = { .ops = &msm_pinctrl_ops, .probe = msm_pinctrl_probe, }; + +bool msm_pinctrl_is_reserved(struct udevice *dev, unsigned int pin) +{ + struct msm_pinctrl_priv *priv = dev_get_priv(dev); + + if (pin >= MSM_PINCTRL_MAX_PINS) + return false; + + return test_bit(pin, priv->reserved_map); +} |