diff options
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 82 |
1 files changed, 57 insertions, 25 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e11a3bb03820..e8f8a1999393 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -431,7 +431,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, int i; if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { - /* TODO: check if descriptors are really input */ + /* NOTE: It's ok to read values of output lines. */ int ret = gpiod_get_array_value_complex(false, true, lh->numdescs, @@ -449,7 +449,13 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, return 0; } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { - /* TODO: check if descriptors are really output */ + /* + * All line descriptors were created at once with the same + * flags so just check if the first one is really output. + */ + if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags)) + return -EPERM; + if (copy_from_user(&ghd, ip, sizeof(ghd))) return -EFAULT; @@ -1256,6 +1262,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, /* If the gpiochip has an assigned OF node this takes precedence */ if (chip->of_node) gdev->dev.of_node = chip->of_node; + else + chip->of_node = gdev->dev.of_node; #endif gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL); @@ -1408,9 +1416,9 @@ err_free_descs: err_free_gdev: ida_simple_remove(&gpio_ida, gdev->id); /* failures here can mean systems won't boot... */ - pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__, + pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, gdev->base, gdev->base + gdev->ngpio - 1, - chip->label ? : "generic"); + chip->label ? : "generic", status); kfree(gdev); return status; } @@ -1664,8 +1672,7 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, if (parent_handler) { if (gpiochip->can_sleep) { chip_err(gpiochip, - "you cannot have chained interrupts on a " - "chip that may sleep\n"); + "you cannot have chained interrupts on a chip that may sleep\n"); return; } /* @@ -1800,16 +1807,18 @@ static const struct irq_domain_ops gpiochip_domain_ops = { static int gpiochip_irq_reqres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + int ret; if (!try_module_get(chip->gpiodev->owner)) return -ENODEV; - if (gpiochip_lock_as_irq(chip, d->hwirq)) { + ret = gpiochip_lock_as_irq(chip, d->hwirq); + if (ret) { chip_err(chip, "unable to lock HW IRQ %lu for IRQ\n", d->hwirq); module_put(chip->gpiodev->owner); - return -EINVAL; + return ret; } return 0; } @@ -1850,8 +1859,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, return 0; if (gpiochip->irq.parent_handler && gpiochip->can_sleep) { - chip_err(gpiochip, "you cannot have chained interrupts on a " - "chip that may sleep\n"); + chip_err(gpiochip, "you cannot have chained interrupts on a chip that may sleep\n"); return -EINVAL; } @@ -2259,6 +2267,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) struct gpio_chip *chip = desc->gdev->chip; int status; unsigned long flags; + unsigned offset; spin_lock_irqsave(&gpio_lock, flags); @@ -2277,7 +2286,11 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (chip->request) { /* chip->request may sleep */ spin_unlock_irqrestore(&gpio_lock, flags); - status = chip->request(chip, gpio_chip_hwgpio(desc)); + offset = gpio_chip_hwgpio(desc); + if (gpiochip_line_is_valid(chip, offset)) + status = chip->request(chip, offset); + else + status = -EINVAL; spin_lock_irqsave(&gpio_lock, flags); if (status < 0) { @@ -3194,6 +3207,19 @@ int gpiod_cansleep(const struct gpio_desc *desc) EXPORT_SYMBOL_GPL(gpiod_cansleep); /** + * gpiod_set_consumer_name() - set the consumer name for the descriptor + * @desc: gpio to set the consumer name on + * @name: the new consumer name + */ +void gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) +{ + VALIDATE_DESC_VOID(desc); + /* Just overwrite whatever the previous name was */ + desc->label = name; +} +EXPORT_SYMBOL_GPL(gpiod_set_consumer_name); + +/** * gpiod_to_irq() - return the IRQ corresponding to a GPIO * @desc: gpio whose IRQ will be returned (already requested) * @@ -3249,18 +3275,19 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset) * behind our back */ if (!chip->can_sleep && chip->get_direction) { - int dir = chip->get_direction(chip, offset); + int dir = gpiod_get_direction(desc); - if (dir) - clear_bit(FLAG_IS_OUT, &desc->flags); - else - set_bit(FLAG_IS_OUT, &desc->flags); + if (dir < 0) { + chip_err(chip, "%s: cannot get GPIO direction\n", + __func__); + return dir; + } } if (test_bit(FLAG_IS_OUT, &desc->flags)) { chip_err(chip, - "%s: tried to flag a GPIO set as output for IRQ\n", - __func__); + "%s: tried to flag a GPIO set as output for IRQ\n", + __func__); return -EIO; } @@ -3639,9 +3666,16 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, chip = find_chip_by_name(p->chip_label); if (!chip) { - dev_err(dev, "cannot find GPIO chip %s\n", - p->chip_label); - return ERR_PTR(-ENODEV); + /* + * As the lookup table indicates a chip with + * p->chip_label should exist, assume it may + * still appear later and let the interested + * consumer be probed again or let the Deferred + * Probe infrastructure handle the error. + */ + dev_warn(dev, "cannot find GPIO chip %s, deferring\n", + p->chip_label); + return ERR_PTR(-EPROBE_DEFER); } if (chip->ngpio <= p->chip_hwnum) { @@ -4215,7 +4249,7 @@ static int __init gpiolib_dev_init(void) int ret; /* Register GPIO sysfs bus */ - ret = bus_register(&gpio_bus_type); + ret = bus_register(&gpio_bus_type); if (ret < 0) { pr_err("gpiolib: could not register GPIO bus type\n"); return ret; @@ -4259,9 +4293,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s", gpio, gdesc->name ? gdesc->name : "", gdesc->label, is_out ? "out" : "in ", - chip->get - ? (chip->get(chip, i) ? "hi" : "lo") - : "? ", + chip->get ? (chip->get(chip, i) ? "hi" : "lo") : "? ", is_irq ? "IRQ" : " "); seq_printf(s, "\n"); } |