Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

gpio: aggregator: handle runtime registration of gpio_desc in gpiochip_fwd

Add request() callback to check if the GPIO descriptor was well registered
in the gpiochip_fwd before using it. This is done to handle the case where
GPIO descriptor is added at runtime in the forwarder.

If at least one GPIO descriptor was not added before the forwarder
registration, we assume the forwarder can sleep as if a GPIO is added at
runtime it may sleep.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Thomas Richard <thomas.richard@bootlin.com>
Link: https://lore.kernel.org/r/20250811-aaeon-up-board-pinctrl-support-v9-7-29f0cbbdfb30@bootlin.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

authored by

Thomas Richard and committed by
Bartosz Golaszewski
b31c68fd 6e986f88

+59 -6
+57 -6
drivers/gpio/gpio-aggregator.c
··· 246 246 spinlock_t slock; /* protects tmp[] if !can_sleep */ 247 247 }; 248 248 struct gpiochip_fwd_timing *delay_timings; 249 + unsigned long *valid_mask; 249 250 unsigned long tmp[]; /* values and descs for multiple ops */ 250 251 }; 251 252 ··· 255 254 256 255 #define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios)) 257 256 257 + static int gpio_fwd_request(struct gpio_chip *chip, unsigned int offset) 258 + { 259 + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); 260 + 261 + return test_bit(offset, fwd->valid_mask) ? 0 : -ENODEV; 262 + } 263 + 258 264 static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset) 259 265 { 260 266 struct gpiochip_fwd *fwd = gpiochip_get_data(chip); 267 + 268 + /* 269 + * get_direction() is called during gpiochip registration, return 270 + * -ENODEV if there is no GPIO desc for the line. 271 + */ 272 + if (!test_bit(offset, fwd->valid_mask)) 273 + return -ENODEV; 261 274 262 275 return gpiod_get_direction(fwd->descs[offset]); 263 276 } ··· 505 490 EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_get_gpiochip, "GPIO_FORWARDER"); 506 491 507 492 /** 493 + * gpiochip_fwd_gpio_request - Request a line of the GPIO forwarder 494 + * @fwd: GPIO forwarder 495 + * @offset: the offset of the line to request 496 + * 497 + * Returns: 0 on success, or negative errno on failure. 498 + */ 499 + int gpiochip_fwd_gpio_request(struct gpiochip_fwd *fwd, unsigned int offset) 500 + { 501 + struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd); 502 + 503 + return gpio_fwd_request(gc, offset); 504 + } 505 + EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_request, "GPIO_FORWARDER"); 506 + 507 + /** 508 508 * gpiochip_fwd_gpio_get_direction - Return the current direction of a GPIO forwarder line 509 509 * @fwd: GPIO forwarder 510 510 * @offset: the offset of the line ··· 693 663 if (!fwd->descs) 694 664 return ERR_PTR(-ENOMEM); 695 665 666 + fwd->valid_mask = devm_bitmap_zalloc(dev, ngpios, GFP_KERNEL); 667 + if (!fwd->valid_mask) 668 + return ERR_PTR(-ENOMEM); 669 + 696 670 chip = &fwd->chip; 697 671 698 672 chip->label = dev_name(dev); 699 673 chip->parent = dev; 700 674 chip->owner = THIS_MODULE; 675 + chip->request = gpio_fwd_request; 701 676 chip->get_direction = gpio_fwd_get_direction; 702 677 chip->direction_input = gpio_fwd_direction_input; 703 678 chip->direction_output = gpio_fwd_direction_output; ··· 729 694 int gpiochip_fwd_desc_add(struct gpiochip_fwd *fwd, struct gpio_desc *desc, 730 695 unsigned int offset) 731 696 { 732 - struct gpio_chip *parent = gpiod_to_chip(desc); 733 697 struct gpio_chip *chip = &fwd->chip; 734 698 735 699 if (offset > chip->ngpio) 736 700 return -EINVAL; 737 701 702 + if (test_and_set_bit(offset, fwd->valid_mask)) 703 + return -EEXIST; 704 + 738 705 /* 739 706 * If any of the GPIO lines are sleeping, then the entire forwarder 740 707 * will be sleeping. 741 - * If any of the chips support .set_config(), then the forwarder will 742 - * support setting configs. 743 708 */ 744 709 if (gpiod_cansleep(desc)) 745 710 chip->can_sleep = true; 746 - 747 - if (parent && parent->set_config) 748 - chip->set_config = gpio_fwd_set_config; 749 711 750 712 fwd->descs[offset] = desc; 751 713 ··· 754 722 EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_add, "GPIO_FORWARDER"); 755 723 756 724 /** 725 + * gpiochip_fwd_desc_free - Remove a GPIO desc from the forwarder 726 + * @fwd: GPIO forwarder 727 + * @offset: offset of GPIO desc to remove 728 + */ 729 + void gpiochip_fwd_desc_free(struct gpiochip_fwd *fwd, unsigned int offset) 730 + { 731 + if (test_and_clear_bit(offset, fwd->valid_mask)) 732 + gpiod_put(fwd->descs[offset]); 733 + } 734 + EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_free, "GPIO_FORWARDER"); 735 + 736 + /** 757 737 * gpiochip_fwd_register - Register a GPIO forwarder 758 738 * @fwd: GPIO forwarder 759 739 * ··· 774 730 int gpiochip_fwd_register(struct gpiochip_fwd *fwd) 775 731 { 776 732 struct gpio_chip *chip = &fwd->chip; 733 + 734 + /* 735 + * Some gpio_desc were not registered. They will be registered at runtime 736 + * but we have to suppose they can sleep. 737 + */ 738 + if (!bitmap_full(fwd->valid_mask, chip->ngpio)) 739 + chip->can_sleep = true; 777 740 778 741 if (chip->can_sleep) 779 742 mutex_init(&fwd->mlock);
+2
include/linux/gpio/forwarder.h
··· 10 10 unsigned int ngpios); 11 11 int gpiochip_fwd_desc_add(struct gpiochip_fwd *fwd, 12 12 struct gpio_desc *desc, unsigned int offset); 13 + void gpiochip_fwd_desc_free(struct gpiochip_fwd *fwd, unsigned int offset); 13 14 int gpiochip_fwd_register(struct gpiochip_fwd *fwd); 14 15 15 16 struct gpio_chip *gpiochip_fwd_get_gpiochip(struct gpiochip_fwd *fwd); 16 17 18 + int gpiochip_fwd_gpio_request(struct gpiochip_fwd *fwd, unsigned int offset); 17 19 int gpiochip_fwd_gpio_get_direction(struct gpiochip_fwd *fwd, 18 20 unsigned int offset); 19 21 int gpiochip_fwd_gpio_direction_input(struct gpiochip_fwd *fwd,