From d1f7728259ef02ac20b7afb6e7eb5a9eb1696c25 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 15 Nov 2023 17:49:59 +0100 Subject: gpiolib: provide gpio_device_get_label() Provide a getter for the GPIO device label string so that users don't have to dereference struct gpio_chip directly. Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 0aed62f0c633..100c329dc986 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -786,6 +786,7 @@ struct gpio_device *gpiod_to_gpio_device(struct gpio_desc *desc); /* struct gpio_device getters */ int gpio_device_get_base(struct gpio_device *gdev); +const char *gpio_device_get_label(struct gpio_device *gdev); #else /* CONFIG_GPIOLIB */ -- cgit v1.2.3 From ee25fba76acd8324f9de6628872c8c612a684209 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 Dec 2023 10:35:00 +0100 Subject: gpiolib: provide gpiochip_dup_line_label() gpiochip_is_requested() not only has a misleading name but it returns a pointer to a string that is freed when the descriptor is released. Provide a new helper meant to replace it, which returns a copy of the label string instead. Signed-off-by: Bartosz Golaszewski Acked-by: Linus Walleij --- drivers/gpio/gpiolib.c | 29 +++++++++++++++++++++++++++++ include/linux/gpio/driver.h | 1 + 2 files changed, 30 insertions(+) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 95d2a7b2ea3e..0147c900afea 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2386,6 +2386,35 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_is_requested); +/** + * gpiochip_dup_line_label - Get a copy of the consumer label. + * @gc: GPIO chip controlling this line. + * @offset: Hardware offset of the line. + * + * Returns: + * Pointer to a copy of the consumer label if the line is requested or NULL + * if it's not. If a valid pointer was returned, it must be freed using + * kfree(). In case of a memory allocation error, the function returns %ENOMEM. + * + * Must not be called from atomic context. + */ +char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) +{ + const char *label; + char *copy; + + label = gpiochip_is_requested(gc, offset); + if (!label) + return NULL; + + copy = kstrdup(label, GFP_KERNEL); + if (!copy) + return ERR_PTR(-ENOMEM); + + return copy; +} +EXPORT_SYMBOL_GPL(gpiochip_dup_line_label); + /** * gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor * @gc: GPIO chip diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 0aed62f0c633..5ac6dc30c547 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -532,6 +532,7 @@ struct gpio_chip { }; const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset); +char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset); /** * for_each_requested_gpio_in_range - iterates over requested GPIOs in a given range -- cgit v1.2.3 From 6fd9c9933475a3efd7eed2f80c7778908a560a1f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 Dec 2023 10:35:08 +0100 Subject: gpiolib: use gpiochip_dup_line_label() in for_each helpers Rework for_each_requested_gpio_in_range() to use the new helper to retrieve a dynamically allocated copy of the descriptor label and free it at the end of each iteration. We need to leverage the CLASS()' destructor to make sure that the label is freed even when breaking out of the loop. Signed-off-by: Bartosz Golaszewski Acked-by: Linus Walleij --- include/linux/gpio/driver.h | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 5ac6dc30c547..cae4cdaa87db 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -534,17 +534,38 @@ struct gpio_chip { const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset); char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset); + +struct _gpiochip_for_each_data { + const char **label; + unsigned int *i; +}; + +DEFINE_CLASS(_gpiochip_for_each_data, + struct _gpiochip_for_each_data, + if (*_T.label) kfree(*_T.label), + ({ + struct _gpiochip_for_each_data _data = { label, i }; + *_data.i = 0; + _data; + }), + const char **label, int *i) + /** * for_each_requested_gpio_in_range - iterates over requested GPIOs in a given range - * @chip: the chip to query - * @i: loop variable - * @base: first GPIO in the range - * @size: amount of GPIOs to check starting from @base - * @label: label of current GPIO + * @_chip: the chip to query + * @_i: loop variable + * @_base: first GPIO in the range + * @_size: amount of GPIOs to check starting from @base + * @_label: label of current GPIO */ -#define for_each_requested_gpio_in_range(chip, i, base, size, label) \ - for (i = 0; i < size; i++) \ - if ((label = gpiochip_is_requested(chip, base + i)) == NULL) {} else +#define for_each_requested_gpio_in_range(_chip, _i, _base, _size, _label) \ + for (CLASS(_gpiochip_for_each_data, _data)(&_label, &_i); \ + *_data.i < _size; \ + (*_data.i)++, kfree(*(_data.label)), *_data.label = NULL) \ + if ((*_data.label = \ + gpiochip_dup_line_label(_chip, _base + *_data.i)) == NULL) {} \ + else if (IS_ERR(*_data.label)) {} \ + else /* Iterates over all requested GPIO of the given @chip */ #define for_each_requested_gpio(chip, i, label) \ -- cgit v1.2.3 From f8d05e276b45e3097dfddd628fa991ce69c05c99 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 Dec 2023 10:35:09 +0100 Subject: gpiolib: remove gpiochip_is_requested() We have no external users of gpiochip_is_requested(). Let's remove it and replace its internal calls with direct testing of the REQUESTED flag. Signed-off-by: Bartosz Golaszewski Acked-by: Linus Walleij --- drivers/gpio/gpiolib.c | 48 ++++++++++++--------------------------------- include/linux/gpio/driver.h | 1 - 2 files changed, 13 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 0147c900afea..0ca33397812c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1071,7 +1071,7 @@ void gpiochip_remove(struct gpio_chip *gc) spin_lock_irqsave(&gpio_lock, flags); for (i = 0; i < gdev->ngpio; i++) { - if (gpiochip_is_requested(gc, i)) + if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags)) break; } spin_unlock_irqrestore(&gpio_lock, flags); @@ -2359,33 +2359,6 @@ void gpiod_free(struct gpio_desc *desc) gpio_device_put(desc->gdev); } -/** - * gpiochip_is_requested - return string iff signal was requested - * @gc: controller managing the signal - * @offset: of signal within controller's 0..(ngpio - 1) range - * - * Returns NULL if the GPIO is not currently requested, else a string. - * The string returned is the label passed to gpio_request(); if none has been - * passed it is a meaningless, non-NULL constant. - * - * This function is for use by GPIO controller drivers. The label can - * help with diagnostics, and knowing that the signal is used as a GPIO - * can help avoid accidentally multiplexing it to another controller. - */ -const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset) -{ - struct gpio_desc *desc; - - desc = gpiochip_get_desc(gc, offset); - if (IS_ERR(desc)) - return NULL; - - if (test_bit(FLAG_REQUESTED, &desc->flags) == 0) - return NULL; - return desc->label; -} -EXPORT_SYMBOL_GPL(gpiochip_is_requested); - /** * gpiochip_dup_line_label - Get a copy of the consumer label. * @gc: GPIO chip controlling this line. @@ -2400,18 +2373,23 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); */ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) { - const char *label; - char *copy; + struct gpio_desc *desc; + char *label; - label = gpiochip_is_requested(gc, offset); - if (!label) + desc = gpiochip_get_desc(gc, offset); + if (IS_ERR(desc)) return NULL; - copy = kstrdup(label, GFP_KERNEL); - if (!copy) + guard(spinlock_irqsave)(&gpio_lock); + + if (!test_bit(FLAG_REQUESTED, &desc->flags)) + return NULL; + + label = kstrdup(desc->label, GFP_KERNEL); + if (!label) return ERR_PTR(-ENOMEM); - return copy; + return label; } EXPORT_SYMBOL_GPL(gpiochip_dup_line_label); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index cae4cdaa87db..d1a3cb061927 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -531,7 +531,6 @@ struct gpio_chip { #endif /* CONFIG_OF_GPIO */ }; -const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset); char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset); -- cgit v1.2.3 From 826a5d8c9df9605fb4fdefa45432f95580241a1f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 25 Oct 2023 21:42:57 +0300 Subject: device property: Implement device_is_big_endian() Some users want to use the struct device pointer to see if the device is big endian in terms of Open Firmware specifications, i.e. if it has a "big-endian" property, or if the kernel was compiled for BE *and* the device has a "native-endian" property. Provide inline helper for the users. Signed-off-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20231025184259.250588-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/property.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'include/linux') diff --git a/include/linux/property.h b/include/linux/property.h index 9f2585d705a8..55c2692ffa8c 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -80,12 +80,38 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode, bool fwnode_device_is_available(const struct fwnode_handle *fwnode); +static inline bool fwnode_device_is_big_endian(const struct fwnode_handle *fwnode) +{ + if (fwnode_property_present(fwnode, "big-endian")) + return true; + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) && + fwnode_property_present(fwnode, "native-endian")) + return true; + return false; +} + static inline bool fwnode_device_is_compatible(const struct fwnode_handle *fwnode, const char *compat) { return fwnode_property_match_string(fwnode, "compatible", compat) >= 0; } +/** + * device_is_big_endian - check if a device has BE registers + * @dev: Pointer to the struct device + * + * Returns: true if the device has a "big-endian" property, or if the kernel + * was compiled for BE *and* the device has a "native-endian" property. + * Returns false otherwise. + * + * Callers would nominally use ioread32be/iowrite32be if + * device_is_big_endian() == true, or readl/writel otherwise. + */ +static inline bool device_is_big_endian(const struct device *dev) +{ + return fwnode_device_is_big_endian(dev_fwnode(dev)); +} + /** * device_is_compatible - match 'compatible' property of the device with a given string * @dev: Pointer to the struct device -- cgit v1.2.3 From 5a2a2cda916335fff4d804e58f36b2305926841e Mon Sep 17 00:00:00 2001 From: Wang Jinchao Date: Mon, 18 Dec 2023 15:16:16 +0800 Subject: gpiolib: remove duplicate inclusions Remove second `#include `. Remove `#include ` too as it's included by `err.h`. Signed-off-by: Wang Jinchao Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index bd9bea7cb270..e846bd4e7559 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -722,7 +722,6 @@ int gpiochip_irqchip_add_domain(struct gpio_chip *gc, #else #include -#include static inline int gpiochip_irqchip_add_domain(struct gpio_chip *gc, struct irq_domain *domain) @@ -811,8 +810,6 @@ const char *gpio_device_get_label(struct gpio_device *gdev); #else /* CONFIG_GPIOLIB */ -#include - #include static inline struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) -- cgit v1.2.3