gpiolib: link all gpio_chips using a list
Add a list member to gpio_chip that allows all chips to be parsed quickly. The current method requires parsing the entire GPIO integer space, which is painfully slow. Using a list makes many chip operations that involve lookup or parsing faster, and also simplifies the code. It is also necessary to eventually get rid of the global gpio_desc[] array. The list of gpio_chips is always ordered by base GPIO number to ensure chips traversal is done in the right order. Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
This commit is contained in:
		
							parent
							
								
									0fa2fd9a0d
								
							
						
					
					
						commit
						1a989d0f1d
					
				| @ -3,6 +3,7 @@ | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/debugfs.h> | ||||
| @ -71,6 +72,8 @@ struct gpio_desc { | ||||
| }; | ||||
| static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; | ||||
| 
 | ||||
| static LIST_HEAD(gpio_chips); | ||||
| 
 | ||||
| #ifdef CONFIG_GPIO_SYSFS | ||||
| static DEFINE_IDR(dirent_idr); | ||||
| #endif | ||||
| @ -1014,6 +1017,43 @@ static inline void gpiochip_unexport(struct gpio_chip *chip) | ||||
| 
 | ||||
| #endif /* CONFIG_GPIO_SYSFS */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Add a new chip to the global chips list, keeping the list of chips sorted | ||||
|  * by base order. | ||||
|  * | ||||
|  * Return -EBUSY if the new chip overlaps with some other chip's integer | ||||
|  * space. | ||||
|  */ | ||||
| static int gpiochip_add_to_list(struct gpio_chip *chip) | ||||
| { | ||||
| 	struct list_head *pos = &gpio_chips; | ||||
| 	struct gpio_chip *_chip; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	/* find where to insert our chip */ | ||||
| 	list_for_each(pos, &gpio_chips) { | ||||
| 		_chip = list_entry(pos, struct gpio_chip, list); | ||||
| 		/* shall we insert before _chip? */ | ||||
| 		if (_chip->base >= chip->base + chip->ngpio) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	/* are we stepping on the chip right before? */ | ||||
| 	if (pos != &gpio_chips && pos->prev != &gpio_chips) { | ||||
| 		_chip = list_entry(pos->prev, struct gpio_chip, list); | ||||
| 		if (_chip->base + _chip->ngpio > chip->base) { | ||||
| 			dev_err(chip->dev, | ||||
| 			       "GPIO integer space overlap, cannot add chip\n"); | ||||
| 			err = -EBUSY; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!err) | ||||
| 		list_add_tail(&chip->list, pos); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * gpiochip_add() - register a gpio_chip | ||||
|  * @chip: the chip to register, with chip->base initialized | ||||
| @ -1055,13 +1095,8 @@ int gpiochip_add(struct gpio_chip *chip) | ||||
| 		chip->base = base; | ||||
| 	} | ||||
| 
 | ||||
| 	/* these GPIO numbers must not be managed by another gpio_chip */ | ||||
| 	for (id = base; id < base + chip->ngpio; id++) { | ||||
| 		if (gpio_desc[id].chip != NULL) { | ||||
| 			status = -EBUSY; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	status = gpiochip_add_to_list(chip); | ||||
| 
 | ||||
| 	if (status == 0) { | ||||
| 		for (id = base; id < base + chip->ngpio; id++) { | ||||
| 			gpio_desc[id].chip = chip; | ||||
| @ -1135,6 +1170,8 @@ int gpiochip_remove(struct gpio_chip *chip) | ||||
| 	if (status == 0) { | ||||
| 		for (id = chip->base; id < chip->base + chip->ngpio; id++) | ||||
| 			gpio_desc[id].chip = NULL; | ||||
| 
 | ||||
| 		list_del(&chip->list); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&gpio_lock, flags); | ||||
|  | ||||
| @ -53,6 +53,7 @@ struct device_node; | ||||
|  * @label: for diagnostics | ||||
|  * @dev: optional device providing the GPIOs | ||||
|  * @owner: helps prevent removal of modules exporting active GPIOs | ||||
|  * @list: links gpio_chips together for traversal | ||||
|  * @request: optional hook for chip-specific activation, such as | ||||
|  *	enabling module power and clock; may sleep | ||||
|  * @free: optional hook for chip-specific deactivation, such as | ||||
| @ -98,6 +99,7 @@ struct gpio_chip { | ||||
| 	const char		*label; | ||||
| 	struct device		*dev; | ||||
| 	struct module		*owner; | ||||
| 	struct list_head        list; | ||||
| 
 | ||||
| 	int			(*request)(struct gpio_chip *chip, | ||||
| 						unsigned offset); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user