summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-aggregator.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-aggregator.c')
-rw-r--r--drivers/gpio/gpio-aggregator.c63
1 files changed, 57 insertions, 6 deletions
diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
index c1952b28b7b7..f0d38d76cf73 100644
--- a/drivers/gpio/gpio-aggregator.c
+++ b/drivers/gpio/gpio-aggregator.c
@@ -246,6 +246,7 @@ struct gpiochip_fwd {
spinlock_t slock; /* protects tmp[] if !can_sleep */
};
struct gpiochip_fwd_timing *delay_timings;
+ unsigned long *valid_mask;
unsigned long tmp[]; /* values and descs for multiple ops */
};
@@ -254,10 +255,24 @@ struct gpiochip_fwd {
#define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios))
+static int gpio_fwd_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return test_bit(offset, fwd->valid_mask) ? 0 : -ENODEV;
+}
+
static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+ /*
+ * get_direction() is called during gpiochip registration, return
+ * -ENODEV if there is no GPIO desc for the line.
+ */
+ if (!test_bit(offset, fwd->valid_mask))
+ return -ENODEV;
+
return gpiod_get_direction(fwd->descs[offset]);
}
@@ -490,6 +505,21 @@ struct gpio_chip *gpiochip_fwd_get_gpiochip(struct gpiochip_fwd *fwd)
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_get_gpiochip, "GPIO_FORWARDER");
/**
+ * gpiochip_fwd_gpio_request - Request a line of the GPIO forwarder
+ * @fwd: GPIO forwarder
+ * @offset: the offset of the line to request
+ *
+ * Returns: 0 on success, or negative errno on failure.
+ */
+int gpiochip_fwd_gpio_request(struct gpiochip_fwd *fwd, unsigned int offset)
+{
+ struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
+
+ return gpio_fwd_request(gc, offset);
+}
+EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_request, "GPIO_FORWARDER");
+
+/**
* gpiochip_fwd_gpio_get_direction - Return the current direction of a GPIO forwarder line
* @fwd: GPIO forwarder
* @offset: the offset of the line
@@ -663,11 +693,16 @@ struct gpiochip_fwd *devm_gpiochip_fwd_alloc(struct device *dev,
if (!fwd->descs)
return ERR_PTR(-ENOMEM);
+ fwd->valid_mask = devm_bitmap_zalloc(dev, ngpios, GFP_KERNEL);
+ if (!fwd->valid_mask)
+ return ERR_PTR(-ENOMEM);
+
chip = &fwd->chip;
chip->label = dev_name(dev);
chip->parent = dev;
chip->owner = THIS_MODULE;
+ chip->request = gpio_fwd_request;
chip->get_direction = gpio_fwd_get_direction;
chip->direction_input = gpio_fwd_direction_input;
chip->direction_output = gpio_fwd_direction_output;
@@ -694,24 +729,21 @@ EXPORT_SYMBOL_NS_GPL(devm_gpiochip_fwd_alloc, "GPIO_FORWARDER");
int gpiochip_fwd_desc_add(struct gpiochip_fwd *fwd, struct gpio_desc *desc,
unsigned int offset)
{
- struct gpio_chip *parent = gpiod_to_chip(desc);
struct gpio_chip *chip = &fwd->chip;
if (offset > chip->ngpio)
return -EINVAL;
+ if (test_and_set_bit(offset, fwd->valid_mask))
+ return -EEXIST;
+
/*
* If any of the GPIO lines are sleeping, then the entire forwarder
* will be sleeping.
- * If any of the chips support .set_config(), then the forwarder will
- * support setting configs.
*/
if (gpiod_cansleep(desc))
chip->can_sleep = true;
- if (parent && parent->set_config)
- chip->set_config = gpio_fwd_set_config;
-
fwd->descs[offset] = desc;
dev_dbg(chip->parent, "%u => gpio %d irq %d\n", offset,
@@ -722,6 +754,18 @@ int gpiochip_fwd_desc_add(struct gpiochip_fwd *fwd, struct gpio_desc *desc,
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_add, "GPIO_FORWARDER");
/**
+ * gpiochip_fwd_desc_free - Remove a GPIO desc from the forwarder
+ * @fwd: GPIO forwarder
+ * @offset: offset of GPIO desc to remove
+ */
+void gpiochip_fwd_desc_free(struct gpiochip_fwd *fwd, unsigned int offset)
+{
+ if (test_and_clear_bit(offset, fwd->valid_mask))
+ gpiod_put(fwd->descs[offset]);
+}
+EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_free, "GPIO_FORWARDER");
+
+/**
* gpiochip_fwd_register - Register a GPIO forwarder
* @fwd: GPIO forwarder
*
@@ -731,6 +775,13 @@ int gpiochip_fwd_register(struct gpiochip_fwd *fwd)
{
struct gpio_chip *chip = &fwd->chip;
+ /*
+ * Some gpio_desc were not registered. They will be registered at runtime
+ * but we have to suppose they can sleep.
+ */
+ if (!bitmap_full(fwd->valid_mask, chip->ngpio))
+ chip->can_sleep = true;
+
if (chip->can_sleep)
mutex_init(&fwd->mlock);
else