Message ID | 1510096056-13765-3-git-send-email-timur@codeaurora.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Timur, > Add support for specifying that some GPIOs within a range are unavailable. > Some systems have a sparse list of GPIOs, where a range of GPIOs is > specified (usually 0 to n-1), but some subset within that range is > absent or unavailable for whatever reason. > > To support this, allow drivers to specify a bitmask of GPIOs that > are present or absent. Gpiolib will then block access to those that > are absent. > > Signed-off-by: Timur Tabi <timur@codeaurora.org> > --- > drivers/gpio/gpiolib.c | 43 +++++++++++++++++++++++++++++++++++-------- > include/linux/gpio/driver.h | 2 ++ > 2 files changed, 37 insertions(+), 8 deletions(-) > > diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c > index 60553af4c004..c32387936cdd 100644 > --- a/drivers/gpio/gpiolib.c > +++ b/drivers/gpio/gpiolib.c > @@ -1481,22 +1481,36 @@ static struct gpio_chip *find_chip_by_name(const char *name) > > static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) > { > - if (!gpiochip->irq_need_valid_mask) > - return 0; > + if (gpiochip->irq_need_valid_mask) { > + gpiochip->irq_valid_mask = > + kcalloc(BITS_TO_LONGS(gpiochip->ngpio), > + sizeof(long), GFP_KERNEL); Since 'irq_valid_mask' is getting filled below wouldn't a kmalloc suffice? > + if (!gpiochip->irq_valid_mask) > + return -ENOMEM; > > - gpiochip->irq_valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio), > - sizeof(long), GFP_KERNEL); > - if (!gpiochip->irq_valid_mask) > - return -ENOMEM; > + /* Assume by default all GPIOs are valid */ > + bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); > + } > > - /* Assume by default all GPIOs are valid */ > - bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); > + if (gpiochip->line_need_valid_mask) { > + gpiochip->line_valid_mask = > + kcalloc(BITS_TO_LONGS(gpiochip->ngpio), > + sizeof(long), GFP_KERNEL); Since 'line_valid_mask' is getting filled below wouldn't a kmalloc suffice? Thanks Varada > + if (!gpiochip->line_valid_mask) > + return -ENOMEM; > + > + /* Assume by default all GPIOs are valid */ > + bitmap_fill(gpiochip->line_valid_mask, gpiochip->ngpio); > + } > > return 0; > } > > static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip) > { > + kfree(gpiochip->line_valid_mask); > + gpiochip->line_valid_mask = NULL; > + > kfree(gpiochip->irq_valid_mask); > gpiochip->irq_valid_mask = NULL; > } > @@ -1510,6 +1524,15 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, > return test_bit(offset, gpiochip->irq_valid_mask); > } > > +static bool gpiochip_irqchip_line_valid(const struct gpio_chip *gpiochip, > + unsigned int offset) > +{ > + /* No mask means all valid */ > + if (likely(!gpiochip->line_valid_mask)) > + return true; > + return test_bit(offset, gpiochip->line_valid_mask); > +} > + > /** > * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip > * @gpiochip: the gpiochip to set the irqchip chain to > @@ -3320,6 +3343,10 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, > return desc; > } > > + /* Make sure the GPIO is valid before we request it. */ > + if (!gpiochip_irqchip_line_valid(desc->gdev->chip, idx)) > + return ERR_PTR(-EACCES); > + > status = gpiod_request(desc, con_id); > if (status < 0) > return ERR_PTR(status); > diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h > index 424e5139ff10..853828ccabc8 100644 > --- a/include/linux/gpio/driver.h > +++ b/include/linux/gpio/driver.h > @@ -173,6 +173,8 @@ struct gpio_chip { > bool irq_nested; > bool irq_need_valid_mask; > unsigned long *irq_valid_mask; > + bool line_need_valid_mask; > + unsigned long *line_valid_mask; > struct lock_class_key *lock_key; > #endif > > -- > Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm > Technologies, Inc. Qualcomm Technologies, Inc. is a member of the > Code Aurora Forum, a Linux Foundation Collaborative Project. > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > > -- > "QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation > > --- > This email has been checked for viruses by Avast antivirus software. > https://www.avast.com/antivirus > -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
On Wed, 2017-11-15 at 11:58 +0530, Varadarajan Narayanan wrote: > > + if (gpiochip->irq_need_valid_mask) { > > + gpiochip->irq_valid_mask = > > + kcalloc(BITS_TO_LONGS(gpiochip->ngpio), > > + sizeof(long), GFP_KERNEL); > > Since 'irq_valid_mask' is getting filled below wouldn't a kmalloc > suffice? I suppose you meant kmalloc_array. Anyway, it's a separate change if you wish, because Timur didn't change the original approach here. > > + if (!gpiochip->irq_valid_mask) > > + return -ENOMEM; > > > > + /* Assume by default all GPIOs are valid */ > > + bitmap_fill(gpiochip->irq_valid_mask, gpiochip- > > >ngpio); > > + } > > > > + if (gpiochip->line_need_valid_mask) { > > + gpiochip->line_valid_mask = > > + kcalloc(BITS_TO_LONGS(gpiochip->ngpio), > > + sizeof(long), GFP_KERNEL); > > Since 'line_valid_mask' is getting filled below wouldn't a kmalloc > suffice? This one just mimics previous, so, see above.
On 11/15/2017 05:38 AM, Andy Shevchenko wrote: > I suppose you meant kmalloc_array. > Anyway, it's a separate change if you wish, because Timur didn't change > the original approach here. Unfortunately for me, all of the irq stuff has moved into its own structure in 4.15, so I need to rewrite all this code anyway. I'll drop the use of kcalloc.
Hi, On 11/08/2017 04:37 AM, Timur Tabi wrote: > Add support for specifying that some GPIOs within a range are unavailable. > Some systems have a sparse list of GPIOs, where a range of GPIOs is > specified (usually 0 to n-1), but some subset within that range is > absent or unavailable for whatever reason. > > To support this, allow drivers to specify a bitmask of GPIOs that > are present or absent. Gpiolib will then block access to those that > are absent. > > Signed-off-by: Timur Tabi <timur@codeaurora.org> > --- > drivers/gpio/gpiolib.c | 43 +++++++++++++++++++++++++++++++++++-------- > include/linux/gpio/driver.h | 2 ++ > 2 files changed, 37 insertions(+), 8 deletions(-) > > diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c > index 60553af4c004..c32387936cdd 100644 > --- a/drivers/gpio/gpiolib.c > +++ b/drivers/gpio/gpiolib.c > @@ -1481,22 +1481,36 @@ static struct gpio_chip *find_chip_by_name(const char *name) > > static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) > { > - if (!gpiochip->irq_need_valid_mask) > - return 0; > + if (gpiochip->irq_need_valid_mask) { > + gpiochip->irq_valid_mask = > + kcalloc(BITS_TO_LONGS(gpiochip->ngpio), > + sizeof(long), GFP_KERNEL); > + if (!gpiochip->irq_valid_mask) > + return -ENOMEM; > > - gpiochip->irq_valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio), > - sizeof(long), GFP_KERNEL); > - if (!gpiochip->irq_valid_mask) > - return -ENOMEM; > + /* Assume by default all GPIOs are valid */ > + bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); > + } > > - /* Assume by default all GPIOs are valid */ > - bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); > + if (gpiochip->line_need_valid_mask) { > + gpiochip->line_valid_mask = > + kcalloc(BITS_TO_LONGS(gpiochip->ngpio), > + sizeof(long), GFP_KERNEL); > + if (!gpiochip->line_valid_mask) > + return -ENOMEM; > + > + /* Assume by default all GPIOs are valid */ > + bitmap_fill(gpiochip->line_valid_mask, gpiochip->ngpio); > + } > > return 0; > } > > static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip) > { > + kfree(gpiochip->line_valid_mask); > + gpiochip->line_valid_mask = NULL; > + > kfree(gpiochip->irq_valid_mask); > gpiochip->irq_valid_mask = NULL; > } > @@ -1510,6 +1524,15 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, > return test_bit(offset, gpiochip->irq_valid_mask); > } > > +static bool gpiochip_irqchip_line_valid(const struct gpio_chip *gpiochip, > + unsigned int offset) > +{ > + /* No mask means all valid */ > + if (likely(!gpiochip->line_valid_mask)) > + return true; > + return test_bit(offset, gpiochip->line_valid_mask); > +} > + > /** > * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip > * @gpiochip: the gpiochip to set the irqchip chain to > @@ -3320,6 +3343,10 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, > return desc; > } > > + /* Make sure the GPIO is valid before we request it. */ > + if (!gpiochip_irqchip_line_valid(desc->gdev->chip, idx)) > + return ERR_PTR(-EACCES); We're hitting an issue here ^ when a consumer calls the devm_gpiod_get() API. This API internally sets idx in gpiod_get_index to always 0. For example, in the call sequence below, the idx argument to gpiochip_irqchip_line_valid is always 0: devm_gpiod_get(dev, con_id, flags) devm_gpiod_get_index(dev, con_id, 0, flags) gpiod_get_index(dev, con_id, 0, flags) if (!gpiochip_irqchip_line_valid(desc->gdev->chip, 0)) return ERR_PTR(-EACCES); Therefore, with these patches, if we create a sparse gpio map, and the 0th gpio is set to "not accessible", all gpio requests end up failing with EACCESS because idx is always passed as 0. In gpiochip_irqchip_line_valid, shouldn't we test the nth bit (that corresponds to this gpio_desc) in chip->line_valid_mask instead of idx passed above, which is always 0? Thanks, Archit > + > status = gpiod_request(desc, con_id); > if (status < 0) > return ERR_PTR(status); > diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h > index 424e5139ff10..853828ccabc8 100644 > --- a/include/linux/gpio/driver.h > +++ b/include/linux/gpio/driver.h > @@ -173,6 +173,8 @@ struct gpio_chip { > bool irq_nested; > bool irq_need_valid_mask; > unsigned long *irq_valid_mask; > + bool line_need_valid_mask; > + unsigned long *line_valid_mask; > struct lock_class_key *lock_key; > #endif > >
On 12/01/2017 05:38 AM, Archit Taneja wrote: > > We're hitting an issue here ^ when a consumer calls the devm_gpiod_get() > API. This API > internally sets idx in gpiod_get_index to always 0. For example, in the > call sequence > below, the idx argument to gpiochip_irqchip_line_valid is always 0: > > devm_gpiod_get(dev, con_id, flags) > devm_gpiod_get_index(dev, con_id, 0, flags) > gpiod_get_index(dev, con_id, 0, flags) > if (!gpiochip_irqchip_line_valid(desc->gdev->chip, 0)) > return ERR_PTR(-EACCES); > > Therefore, with these patches, if we create a sparse gpio map, and the > 0th gpio is set to "not accessible", all gpio requests end up failing > with EACCESS > because idx is always passed as 0. Ok, I can see the problem, but I don't know how to fix it. struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) { return devm_gpiod_get_index(dev, con_id, 0, flags); } I thought that by putting the check for "availability" inside of the GPIO request, it would handle all situations where a client (whether kernel or user-space) tries to access a specific GPIO. However, this doesn't work with [devm_]gpiod_get. This function attempts to claim the entire GPIO block by claiming only the first GPIO. This is straining my understanding of gpiolib. Maybe we need to do something like this: struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) { return gpiod_get_index(dev, con_id, -1, flags); } Where idx == -1 is a special-case for gpiod_get_index() where it returns the gpio_desc without actually requesting it? > In gpiochip_irqchip_line_valid, shouldn't we test the nth bit (that > corresponds to > this gpio_desc) in chip->line_valid_mask instead of idx passed above, > which is always > 0? The code has changed upstream, so I need to refactor that function. But I think it's already testing the nth bit: static bool gpiochip_irqchip_line_valid(const struct gpio_chip gpiochip, unsigned int offset) { /* No mask means all valid */ if (likely(!gpiochip->line_valid_mask)) return true; return test_bit(offset, gpiochip->line_valid_mask); 'offset' is the nth bit.
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 60553af4c004..c32387936cdd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1481,22 +1481,36 @@ static struct gpio_chip *find_chip_by_name(const char *name) static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) { - if (!gpiochip->irq_need_valid_mask) - return 0; + if (gpiochip->irq_need_valid_mask) { + gpiochip->irq_valid_mask = + kcalloc(BITS_TO_LONGS(gpiochip->ngpio), + sizeof(long), GFP_KERNEL); + if (!gpiochip->irq_valid_mask) + return -ENOMEM; - gpiochip->irq_valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio), - sizeof(long), GFP_KERNEL); - if (!gpiochip->irq_valid_mask) - return -ENOMEM; + /* Assume by default all GPIOs are valid */ + bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); + } - /* Assume by default all GPIOs are valid */ - bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); + if (gpiochip->line_need_valid_mask) { + gpiochip->line_valid_mask = + kcalloc(BITS_TO_LONGS(gpiochip->ngpio), + sizeof(long), GFP_KERNEL); + if (!gpiochip->line_valid_mask) + return -ENOMEM; + + /* Assume by default all GPIOs are valid */ + bitmap_fill(gpiochip->line_valid_mask, gpiochip->ngpio); + } return 0; } static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip) { + kfree(gpiochip->line_valid_mask); + gpiochip->line_valid_mask = NULL; + kfree(gpiochip->irq_valid_mask); gpiochip->irq_valid_mask = NULL; } @@ -1510,6 +1524,15 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, return test_bit(offset, gpiochip->irq_valid_mask); } +static bool gpiochip_irqchip_line_valid(const struct gpio_chip *gpiochip, + unsigned int offset) +{ + /* No mask means all valid */ + if (likely(!gpiochip->line_valid_mask)) + return true; + return test_bit(offset, gpiochip->line_valid_mask); +} + /** * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip * @gpiochip: the gpiochip to set the irqchip chain to @@ -3320,6 +3343,10 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, return desc; } + /* Make sure the GPIO is valid before we request it. */ + if (!gpiochip_irqchip_line_valid(desc->gdev->chip, idx)) + return ERR_PTR(-EACCES); + status = gpiod_request(desc, con_id); if (status < 0) return ERR_PTR(status); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 424e5139ff10..853828ccabc8 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -173,6 +173,8 @@ struct gpio_chip { bool irq_nested; bool irq_need_valid_mask; unsigned long *irq_valid_mask; + bool line_need_valid_mask; + unsigned long *line_valid_mask; struct lock_class_key *lock_key; #endif
Add support for specifying that some GPIOs within a range are unavailable. Some systems have a sparse list of GPIOs, where a range of GPIOs is specified (usually 0 to n-1), but some subset within that range is absent or unavailable for whatever reason. To support this, allow drivers to specify a bitmask of GPIOs that are present or absent. Gpiolib will then block access to those that are absent. Signed-off-by: Timur Tabi <timur@codeaurora.org> --- drivers/gpio/gpiolib.c | 43 +++++++++++++++++++++++++++++++++++-------- include/linux/gpio/driver.h | 2 ++ 2 files changed, 37 insertions(+), 8 deletions(-)