2018-10-02 08:28:00

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v3 1/3] gpiolib: Add init_valid_mask exported function

Add a function that allows initializing the valid_mask from
gpiochip_add_data.

This prevents race conditions during gpiochip initialization.

If the function is not exported, then the old behaviour is respected,
this is, set all gpios as valid.

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/gpio/gpiolib.c | 3 +++
include/linux/gpio/driver.h | 7 ++++++-
2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index e8f8a1999393..6925196136ce 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -377,6 +377,9 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip)
if (!gpiochip->valid_mask)
return -ENOMEM;

+ if (gpiochip->init_valid_mask)
+ return gpiochip->init_valid_mask(gpiochip);
+
return 0;
}

diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index 0ea328e71ec9..df09749269ff 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -256,6 +256,9 @@ struct gpio_chip {

void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
+
+ int (*init_valid_mask)(struct gpio_chip *chip);
+
int base;
u16 ngpio;
const char *const *names;
@@ -294,7 +297,9 @@ struct gpio_chip {
/**
* @need_valid_mask:
*
- * If set core allocates @valid_mask with all bits set to one.
+ * If set core allocates @valid_mask with all its values initialized
+ * with init_valid_mask() or set to one if init_valid_mask() is not
+ * defined
*/
bool need_valid_mask;

--
2.19.0



2018-10-02 08:28:01

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v3 2/3] pinctrl: msm: Use init_valid_mask exported function

The current code produces XPU violation if get_direction is called just
after the initialization.

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/pinctrl/qcom/pinctrl-msm.c | 79 ++++++++++++++----------------
1 file changed, 37 insertions(+), 42 deletions(-)

diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index 5d72ffad32c2..ce1ade47ea37 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -566,6 +566,42 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
#define msm_gpio_dbg_show NULL
#endif

+static int msm_gpio_init_valid_mask(struct gpio_chip *chip)
+{
+ struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
+ int ret;
+ unsigned int len, i;
+ unsigned int max_gpios = pctrl->soc->ngpios;
+ u16 *tmp;
+
+ /* The number of GPIOs in the ACPI tables */
+ len = ret = device_property_read_u16_array(pctrl->dev, "gpios", NULL,
+ 0);
+ if (ret < 0)
+ return 0;
+
+ if (ret > max_gpios)
+ return -EINVAL;
+
+ tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ ret = device_property_read_u16_array(pctrl->dev, "gpios", tmp, len);
+ if (ret < 0) {
+ dev_err(pctrl->dev, "could not read list of GPIOs\n");
+ goto out;
+ }
+
+ bitmap_zero(chip->valid_mask, max_gpios);
+ for (i = 0; i < len; i++)
+ set_bit(tmp[i], chip->valid_mask);
+
+out:
+ kfree(tmp);
+ return ret;
+}
+
static const struct gpio_chip msm_gpio_template = {
.direction_input = msm_gpio_direction_input,
.direction_output = msm_gpio_direction_output,
@@ -575,6 +611,7 @@ static const struct gpio_chip msm_gpio_template = {
.request = gpiochip_generic_request,
.free = gpiochip_generic_free,
.dbg_show = msm_gpio_dbg_show,
+ .init_valid_mask = msm_gpio_init_valid_mask,
};

/* For dual-edge interrupts in software, since some hardware has no
@@ -855,41 +892,6 @@ static void msm_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}

-static int msm_gpio_init_valid_mask(struct gpio_chip *chip,
- struct msm_pinctrl *pctrl)
-{
- int ret;
- unsigned int len, i;
- unsigned int max_gpios = pctrl->soc->ngpios;
- u16 *tmp;
-
- /* The number of GPIOs in the ACPI tables */
- len = ret = device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0);
- if (ret < 0)
- return 0;
-
- if (ret > max_gpios)
- return -EINVAL;
-
- tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL);
- if (!tmp)
- return -ENOMEM;
-
- ret = device_property_read_u16_array(pctrl->dev, "gpios", tmp, len);
- if (ret < 0) {
- dev_err(pctrl->dev, "could not read list of GPIOs\n");
- goto out;
- }
-
- bitmap_zero(chip->valid_mask, max_gpios);
- for (i = 0; i < len; i++)
- set_bit(tmp[i], chip->valid_mask);
-
-out:
- kfree(tmp);
- return ret;
-}
-
static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
{
return device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0) > 0;
@@ -926,13 +928,6 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
return ret;
}

- ret = msm_gpio_init_valid_mask(chip, pctrl);
- if (ret) {
- dev_err(pctrl->dev, "Failed to setup irq valid bits\n");
- gpiochip_remove(&pctrl->chip);
- return ret;
- }
-
/*
* For DeviceTree-supported systems, the gpio core checks the
* pinctrl's device node for the "gpio-ranges" property.
--
2.19.0


2018-10-02 08:28:02

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v3 3/3] gpiolib: Show correct direction from the beginning

Current code assumes that the direction is input if direction_input
function is set.
This might not be the case on GPIOs with programmable direction.

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/gpio/gpiolib.c | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 6925196136ce..eaadbcb5c0f8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1344,20 +1344,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,

spin_unlock_irqrestore(&gpio_lock, flags);

- for (i = 0; i < chip->ngpio; i++) {
- struct gpio_desc *desc = &gdev->descs[i];
-
- desc->gdev = gdev;
-
- /* REVISIT: most hardware initializes GPIOs as inputs (often
- * with pullups enabled) so power usage is minimized. Linux
- * code should set the gpio direction first thing; but until
- * it does, and in case chip->get_direction is not set, we may
- * expose the wrong direction in sysfs.
- */
- desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
- }
-
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
@@ -1374,6 +1360,25 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
if (status)
goto err_remove_irqchip_mask;

+ for (i = 0; i < chip->ngpio; i++) {
+ struct gpio_desc *desc = &gdev->descs[i];
+
+ desc->gdev = gdev;
+
+ /* REVISIT: most hardware initializes GPIOs as inputs (often
+ * with pullups enabled) so power usage is minimized. Linux
+ * code should set the gpio direction first thing; but until
+ * it does, and in case chip->get_direction is not set, we may
+ * expose the wrong direction in sysfs.
+ */
+ if (chip->get_direction && gpiochip_line_is_valid(chip, i))
+ desc->flags = !chip->get_direction(chip, i) ?
+ (1 << FLAG_IS_OUT) : 0;
+ else
+ desc->flags = !chip->direction_input ?
+ (1 << FLAG_IS_OUT) : 0;
+ }
+
status = gpiochip_add_irqchip(chip, lock_key, request_key);
if (status)
goto err_remove_chip;
--
2.19.0


2018-10-02 09:15:44

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v3 1/3] gpiolib: Add init_valid_mask exported function

On Tue, Oct 2, 2018 at 10:27 AM Ricardo Ribalda Delgado
<[email protected]> wrote:

> Add a function that allows initializing the valid_mask from
> gpiochip_add_data.
>
> This prevents race conditions during gpiochip initialization.
>
> If the function is not exported, then the old behaviour is respected,
> this is, set all gpios as valid.
>
> Signed-off-by: Ricardo Ribalda Delgado <[email protected]>

This is a very appetizing patch set.

I think patches 1 & 2 should be applied for sure even if
we don't apply patch 3, simply because it is way more
elegant.

Looking forward to see some test on Qualcomm's hardware
for this!

Yours,
Linus Walleij

2018-10-02 12:31:22

by Timur Tabi

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] gpiolib: Show correct direction from the beginning

On 10/2/18 3:27 AM, Ricardo Ribalda Delgado wrote:
> + /* REVISIT: most hardware initializes GPIOs as inputs (often
> + * with pullups enabled) so power usage is minimized. Linux
> + * code should set the gpio direction first thing; but until
> + * it does, and in case chip->get_direction is not set, we may
> + * expose the wrong direction in sysfs.
> + */

I don't think this comment applies any more.

Also, please don't use [email protected] any more. That address is
no longer valid. Use [email protected] instead.