summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpiolib.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r--drivers/gpio/gpiolib.c107
1 files changed, 63 insertions, 44 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 60cd7cc33647..9535f48e18d1 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -189,9 +189,8 @@ static int gpiochip_find_base(int ngpio)
/* found a free space? */
if (gdev->base + gdev->ngpio <= base)
break;
- else
- /* nope, check the space right before the chip */
- base = gdev->base - ngpio;
+ /* nope, check the space right before the chip */
+ base = gdev->base - ngpio;
}
if (gpio_is_valid(base)) {
@@ -289,7 +288,6 @@ static int gpiodev_add_to_list(struct gpio_device *gdev)
}
}
- dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chip\n");
return -EBUSY;
}
@@ -310,15 +308,10 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(gdev, &gpio_devices, list) {
- int i;
+ struct gpio_desc *desc;
- for (i = 0; i != gdev->ngpio; ++i) {
- struct gpio_desc *desc = &gdev->descs[i];
-
- if (!desc->name)
- continue;
-
- if (!strcmp(desc->name, name)) {
+ for_each_gpio_desc(gdev->chip, desc) {
+ if (desc->name && !strcmp(desc->name, name)) {
spin_unlock_irqrestore(&gpio_lock, flags);
return desc;
}
@@ -728,6 +721,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = gpiodev_add_to_list(gdev);
if (ret) {
spin_unlock_irqrestore(&gpio_lock, flags);
+ chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
goto err_free_label;
}
@@ -1404,6 +1398,16 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset)
{
struct irq_domain *domain = gc->irq.domain;
+#ifdef CONFIG_GPIOLIB_IRQCHIP
+ /*
+ * Avoid race condition with other code, which tries to lookup
+ * an IRQ before the irqchip has been properly registered,
+ * i.e. while gpiochip is still being brought up.
+ */
+ if (!gc->irq.initialized)
+ return -EPROBE_DEFER;
+#endif
+
if (!gpiochip_irqchip_irq_valid(gc, offset))
return -ENXIO;
@@ -1423,19 +1427,21 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset)
return irq_create_mapping(domain, offset);
}
-static int gpiochip_irq_reqres(struct irq_data *d)
+int gpiochip_irq_reqres(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
return gpiochip_reqres_irq(gc, d->hwirq);
}
+EXPORT_SYMBOL(gpiochip_irq_reqres);
-static void gpiochip_irq_relres(struct irq_data *d)
+void gpiochip_irq_relres(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
gpiochip_relres_irq(gc, d->hwirq);
}
+EXPORT_SYMBOL(gpiochip_irq_relres);
static void gpiochip_irq_mask(struct irq_data *d)
{
@@ -1475,6 +1481,11 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
{
struct irq_chip *irqchip = gc->irq.chip;
+ if (irqchip->flags & IRQCHIP_IMMUTABLE)
+ return;
+
+ chip_warn(gc, "not an immutable chip, please consider fixing it!\n");
+
if (!irqchip->irq_request_resources &&
!irqchip->irq_release_resources) {
irqchip->irq_request_resources = gpiochip_irq_reqres;
@@ -1591,6 +1602,15 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
gpiochip_set_irq_hooks(gc);
+ /*
+ * Using barrier() here to prevent compiler from reordering
+ * gc->irq.initialized before initialization of above
+ * GPIO chip irq members.
+ */
+ barrier();
+
+ gc->irq.initialized = true;
+
acpi_gpiochip_request_interrupts(gc);
return 0;
@@ -1633,7 +1653,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc)
irq_domain_remove(gc->irq.domain);
}
- if (irqchip) {
+ if (irqchip && !(irqchip->flags & IRQCHIP_IMMUTABLE)) {
if (irqchip->irq_request_resources == gpiochip_irq_reqres) {
irqchip->irq_request_resources = NULL;
irqchip->irq_release_resources = NULL;
@@ -2401,8 +2421,7 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
ret = gpiod_direction_input(desc);
goto set_output_flag;
}
- }
- else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
+ } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE);
if (!ret)
goto set_output_value;
@@ -2578,6 +2597,11 @@ void gpiod_toggle_active_low(struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(gpiod_toggle_active_low);
+static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc)
+{
+ return gc->get ? gc->get(gc, gpio_chip_hwgpio(desc)) : -EIO;
+}
+
/* I/O calls are only valid after configuration completed; the relevant
* "is this a valid GPIO" error checks should already have been done.
*
@@ -2603,12 +2627,10 @@ EXPORT_SYMBOL_GPL(gpiod_toggle_active_low);
static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
{
struct gpio_chip *gc;
- int offset;
int value;
gc = desc->gdev->chip;
- offset = gpio_chip_hwgpio(desc);
- value = gc->get ? gc->get(gc, offset) : -EIO;
+ value = gpio_chip_get_value(gc, desc);
value = value < 0 ? value : !!value;
trace_gpio_value(desc_to_gpio(desc), 1, value);
return value;
@@ -2617,9 +2639,9 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
static int gpio_chip_get_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
- if (gc->get_multiple) {
+ if (gc->get_multiple)
return gc->get_multiple(gc, mask, bits);
- } else if (gc->get) {
+ if (gc->get) {
int i, value;
for_each_set_bit(i, mask, gc->ngpio) {
@@ -4179,9 +4201,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
static void gpiochip_free_hogs(struct gpio_chip *gc)
{
struct gpio_desc *desc;
- int id;
- for_each_gpio_desc_with_flag(id, gc, desc, FLAG_IS_HOGGED)
+ for_each_gpio_desc_with_flag(gc, desc, FLAG_IS_HOGGED)
gpiochip_free_own_desc(desc);
}
@@ -4441,34 +4462,32 @@ core_initcall(gpiolib_dev_init);
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
{
- unsigned i;
struct gpio_chip *gc = gdev->chip;
+ struct gpio_desc *desc;
unsigned gpio = gdev->base;
- struct gpio_desc *gdesc = &gdev->descs[0];
+ int value;
bool is_out;
bool is_irq;
bool active_low;
- for (i = 0; i < gdev->ngpio; i++, gpio++, gdesc++) {
- if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) {
- if (gdesc->name) {
- seq_printf(s, " gpio-%-3d (%-20.20s)\n",
- gpio, gdesc->name);
- }
- continue;
+ for_each_gpio_desc(gc, desc) {
+ if (test_bit(FLAG_REQUESTED, &desc->flags)) {
+ gpiod_get_direction(desc);
+ is_out = test_bit(FLAG_IS_OUT, &desc->flags);
+ value = gpio_chip_get_value(gc, desc);
+ is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags);
+ active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s%s\n",
+ gpio, desc->name ?: "", desc->label,
+ is_out ? "out" : "in ",
+ value >= 0 ? (value ? "hi" : "lo") : "? ",
+ is_irq ? "IRQ " : "",
+ active_low ? "ACTIVE LOW" : "");
+ } else if (desc->name) {
+ seq_printf(s, " gpio-%-3d (%-20.20s)\n", gpio, desc->name);
}
- gpiod_get_direction(gdesc);
- is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
- is_irq = test_bit(FLAG_USED_AS_IRQ, &gdesc->flags);
- active_low = test_bit(FLAG_ACTIVE_LOW, &gdesc->flags);
- seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s%s",
- gpio, gdesc->name ? gdesc->name : "", gdesc->label,
- is_out ? "out" : "in ",
- gc->get ? (gc->get(gc, i) ? "hi" : "lo") : "? ",
- is_irq ? "IRQ " : "",
- active_low ? "ACTIVE LOW" : "");
- seq_printf(s, "\n");
+ gpio++;
}
}