|
@@ -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);
|