Message ID | 20250410-topic-sm8x50-pinctrl-reserved-ranges-v2-1-654488392b9a@linaro.org |
---|---|
State | New |
Headers | show |
Series | [v2] pinctrl: qcom: handle reserved ranges | expand |
On Thu, Apr 10, 2025 at 10:52:38AM +0200, neil.armstrong@linaro.org wrote: > From: Caleb Connolly <caleb.connolly@linaro.org> > > 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: Caleb Connolly <caleb.connolly@linaro.org> > Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org> > --- > Changes in v2: > - Switch to bitmap > - Link to v1: https://lore.kernel.org/r/20250401-topic-sm8x50-pinctrl-reserved-ranges-v1-1-0fe88b491707@linaro.org > --- > arch/arm/mach-snapdragon/include/mach/gpio.h | 15 +++++++ > drivers/gpio/msm_gpio.c | 9 ++++ > drivers/pinctrl/qcom/pinctrl-qcom.c | 67 ++++++++++++++++++++++++++++ > 3 files changed, 91 insertions(+) Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com> -Sumit > > diff --git a/arch/arm/mach-snapdragon/include/mach/gpio.h b/arch/arm/mach-snapdragon/include/mach/gpio.h > index cc8f405e20b4392cf9226b805bc85b73aedd9134..11e8104baf2328f5bf82cb318459a237168f6978 100644 > --- a/arch/arm/mach-snapdragon/include/mach/gpio.h > +++ b/arch/arm/mach-snapdragon/include/mach/gpio.h > @@ -46,4 +46,19 @@ static inline bool qcom_is_special_pin(const struct msm_pin_data *pindata, unsig > return pindata->special_pins_start && pin >= pindata->special_pins_start; > } > > +struct udevice; > + > +/** > + * msm_pinctrl_is_reserved() - Check if a pin lies in a reserved range > + * > + * @dev: pinctrl device > + * @pin: Pin number > + * > + * Returns: true if pin is reserved, otherwise false > + * > + * Call using dev_get_parent() from the GPIO device, it is a child of > + * the pinctrl device. > + */ > +bool msm_pinctrl_is_reserved(struct udevice *dev, unsigned int pin); > + > #endif /* _QCOM_GPIO_H_ */ > diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c > index cea073b329777d4e03fbfa86415041a825f65aad..647a616a29374fcf12099509c51fb6e96b19f9f5 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) { > @@ -186,6 +189,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); > > @@ -196,6 +202,9 @@ 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 0; > diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.c b/drivers/pinctrl/qcom/pinctrl-qcom.c > index 24d031947a3c00da352fee8b50d5ad38e2d93dfa..c95db56bc47ed3183822fcef2721fc00262b6182 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); > +} > > --- > base-commit: 5ca70325b64f760bf4190f206a0e88dda495e3d2 > change-id: 20250401-topic-sm8x50-pinctrl-reserved-ranges-b93cad6cafb3 > > Best regards, > -- > Neil Armstrong <neil.armstrong@linaro.org> >
diff --git a/arch/arm/mach-snapdragon/include/mach/gpio.h b/arch/arm/mach-snapdragon/include/mach/gpio.h index cc8f405e20b4392cf9226b805bc85b73aedd9134..11e8104baf2328f5bf82cb318459a237168f6978 100644 --- a/arch/arm/mach-snapdragon/include/mach/gpio.h +++ b/arch/arm/mach-snapdragon/include/mach/gpio.h @@ -46,4 +46,19 @@ static inline bool qcom_is_special_pin(const struct msm_pin_data *pindata, unsig return pindata->special_pins_start && pin >= pindata->special_pins_start; } +struct udevice; + +/** + * msm_pinctrl_is_reserved() - Check if a pin lies in a reserved range + * + * @dev: pinctrl device + * @pin: Pin number + * + * Returns: true if pin is reserved, otherwise false + * + * Call using dev_get_parent() from the GPIO device, it is a child of + * the pinctrl device. + */ +bool msm_pinctrl_is_reserved(struct udevice *dev, unsigned int pin); + #endif /* _QCOM_GPIO_H_ */ diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c index cea073b329777d4e03fbfa86415041a825f65aad..647a616a29374fcf12099509c51fb6e96b19f9f5 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) { @@ -186,6 +189,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); @@ -196,6 +202,9 @@ 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 0; diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.c b/drivers/pinctrl/qcom/pinctrl-qcom.c index 24d031947a3c00da352fee8b50d5ad38e2d93dfa..c95db56bc47ed3183822fcef2721fc00262b6182 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); +}