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.conno...@linaro.org> --- drivers/gpio/msm_gpio.c | 15 +++++++++ drivers/pinctrl/qcom/pinctrl-qcom.c | 64 +++++++++++++++++++++++++++++++++++++ include/qcom-gpio.h | 15 +++++++++ 3 files changed, 94 insertions(+) diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c index 7d77776a25fd..7a09abdafb2e 100644 --- a/drivers/gpio/msm_gpio.c +++ b/drivers/gpio/msm_gpio.c @@ -39,6 +39,9 @@ static int msm_gpio_direction_input(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 0; + /* Disable OE bit */ clrsetbits_le32(priv->base + GPIO_CONFIG_REG(dev, gpio), GPIO_OE_MASK, GPIO_OE_DISABLE); @@ -50,6 +53,9 @@ static int msm_gpio_set_value(struct udevice *dev, unsigned gpio, int value) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return 0; + value = !!value; /* set value */ writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_REG(dev, gpio)); @@ -62,6 +68,9 @@ static int msm_gpio_direction_output(struct udevice *dev, unsigned gpio, { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return 0; + value = !!value; /* set value */ writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_REG(dev, gpio)); @@ -76,6 +85,9 @@ static int msm_gpio_get_value(struct udevice *dev, unsigned gpio) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return 0; + return !!(readl(priv->base + GPIO_IN_OUT_REG(dev, gpio)) >> GPIO_IN); } @@ -83,6 +95,9 @@ static int msm_gpio_get_function(struct udevice *dev, unsigned gpio) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return GPIOF_UNKNOWN; + if (readl(priv->base + GPIO_CONFIG_REG(dev, gpio)) & GPIO_OE_ENABLE) return GPIOF_OUTPUT; diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.c b/drivers/pinctrl/qcom/pinctrl-qcom.c index fb255f9572f4..92b35c198788 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) \ @@ -61,13 +65,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; } @@ -83,6 +127,9 @@ static int msm_pinmux_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 0; + clrsetbits_le32(priv->base + GPIO_CONFIG_REG(priv, pin_selector), TLMM_FUNC_SEL_MASK | TLMM_GPIO_DISABLE, priv->data->get_function_mux(func_selector) << 2); @@ -94,6 +141,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 0; + switch (param) { case PIN_CONFIG_DRIVE_STRENGTH: argument = (argument / 2) - 1; @@ -178,3 +228,17 @@ U_BOOT_DRIVER(qcom_pinctrl) = { .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; +} diff --git a/include/qcom-gpio.h b/include/qcom-gpio.h index 8dac62f870b9..490a1de55f89 100644 --- a/include/qcom-gpio.h +++ b/include/qcom-gpio.h @@ -25,4 +25,19 @@ static inline u32 qcom_pin_offset(const unsigned int *offs, unsigned int selecto return out; } +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_ */ -- 2.42.0