diff mbox series

pinctrl: qcom: handle reserved ranges

Message ID 20250401-topic-sm8x50-pinctrl-reserved-ranges-v1-1-0fe88b491707@linaro.org
State New
Headers show
Series pinctrl: qcom: handle reserved ranges | expand

Commit Message

Neil Armstrong April 1, 2025, 7:46 a.m. UTC
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>
---
 arch/arm/mach-snapdragon/include/mach/gpio.h | 15 +++++++
 drivers/gpio/msm_gpio.c                      |  9 ++++
 drivers/pinctrl/qcom/pinctrl-qcom.c          | 64 ++++++++++++++++++++++++++++
 3 files changed, 88 insertions(+)


---
base-commit: 5ca70325b64f760bf4190f206a0e88dda495e3d2
change-id: 20250401-topic-sm8x50-pinctrl-reserved-ranges-b93cad6cafb3

Best regards,
diff mbox series

Patch

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..3ef773d61a99e5d1a64078b9767febbfa9954ec2 100644
--- a/drivers/pinctrl/qcom/pinctrl-qcom.c
+++ b/drivers/pinctrl/qcom/pinctrl-qcom.c
@@ -20,9 +20,13 @@ 
 
 #include "pinctrl-qcom.h"
 
+#define MSM_PINCTRL_MAX_RESERVED_RANGES 32
+
 struct msm_pinctrl_priv {
 	phys_addr_t base;
 	struct msm_pinctrl_data *data;
+	u32 reserved_ranges[MSM_PINCTRL_MAX_RESERVED_RANGES * 2];
+	int reserved_ranges_count;
 };
 
 #define GPIO_CONFIG_REG(priv, x) \
@@ -71,13 +75,53 @@  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);
+	int count;
+
+	if (ofnode_read_prop(dev_ofnode(dev), "gpio-reserved-ranges",
+			     &count)) {
+		if (count % 2 == 1) {
+			dev_err(dev, "gpio-reserved-ranges must be a multiple of 2\n");
+			return -EINVAL;
+		}
+		/* Size is in bytes, but we're indexing by ints */
+		count /= 4;
+
+		if (count > MSM_PINCTRL_MAX_RESERVED_RANGES) {
+			dev_err(dev, "gpio-reserved-ranges must be less than %d (got %d)\n",
+				MSM_PINCTRL_MAX_RESERVED_RANGES, count);
+			return -EINVAL;
+		}
+
+		priv->reserved_ranges_count = count;
+		for (count = 0; count < priv->reserved_ranges_count; count++) {
+			if (ofnode_read_u32_index(dev_ofnode(dev), "gpio-reserved-ranges",
+						  count, &priv->reserved_ranges[count])) {
+				dev_err(dev, "failed to read gpio-reserved-ranges[%d]\n", count);
+				return -EINVAL;
+			}
+		}
+	}
+
+	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 +141,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 +192,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 +291,17 @@  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);
+	unsigned int i, start;
+
+	for (i = 0; i < priv->reserved_ranges_count; i += 2) {
+		start = priv->reserved_ranges[i];
+		if (pin >= start && pin < start + priv->reserved_ranges[i + 1])
+			return true;
+	}
+
+	return false;
+}