[PATCH v2] pinctrl: qcom: handle reserved ranges

Sumit Garg sumit.garg at kernel.org
Thu Apr 10 11:19:07 CEST 2025


On Thu, Apr 10, 2025 at 10:52:38AM +0200, neil.armstrong at linaro.org wrote:
> From: Caleb Connolly <caleb.connolly at 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 at linaro.org>
> Signed-off-by: Neil Armstrong <neil.armstrong at 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 at 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 at linaro.org>
> 


More information about the U-Boot mailing list