diff options
Diffstat (limited to 'drivers/gpio/gpio-adnp.c')
| -rw-r--r-- | drivers/gpio/gpio-adnp.c | 253 |
1 files changed, 116 insertions, 137 deletions
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 91b90c0cea73..e5ac2d211013 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -1,16 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2011-2012 Avionic Design GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ +#include <linux/cleanup.h> #include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/interrupt.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_irq.h> +#include <linux/mutex.h> +#include <linux/property.h> #include <linux/seq_file.h> #include <linux/slab.h> @@ -80,7 +80,7 @@ static int adnp_gpio_get(struct gpio_chip *chip, unsigned offset) return (value & BIT(pos)) ? 1 : 0; } -static void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value) +static int __adnp_gpio_set(struct adnp *adnp, unsigned int offset, int value) { unsigned int reg = offset >> adnp->reg_shift; unsigned int pos = offset & 7; @@ -89,23 +89,23 @@ static void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value) err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &val); if (err < 0) - return; + return err; if (value) val |= BIT(pos); else val &= ~BIT(pos); - adnp_write(adnp, GPIO_PLR(adnp) + reg, val); + return adnp_write(adnp, GPIO_PLR(adnp) + reg, val); } -static void adnp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int adnp_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct adnp *adnp = gpiochip_get_data(chip); - mutex_lock(&adnp->i2c_lock); - __adnp_gpio_set(adnp, offset, value); - mutex_unlock(&adnp->i2c_lock); + guard(mutex)(&adnp->i2c_lock); + + return __adnp_gpio_set(adnp, offset, value); } static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -116,30 +116,26 @@ static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset) u8 value; int err; - mutex_lock(&adnp->i2c_lock); + guard(mutex)(&adnp->i2c_lock); err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value); if (err < 0) - goto out; + return err; value &= ~BIT(pos); err = adnp_write(adnp, GPIO_DDR(adnp) + reg, value); if (err < 0) - goto out; + return err; err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value); if (err < 0) - goto out; - - if (err & BIT(pos)) - err = -EACCES; + return err; - err = 0; + if (value & BIT(pos)) + return -EPERM; -out: - mutex_unlock(&adnp->i2c_lock); - return err; + return 0; } static int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset, @@ -151,33 +147,28 @@ static int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int err; u8 val; - mutex_lock(&adnp->i2c_lock); + guard(mutex)(&adnp->i2c_lock); err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val); if (err < 0) - goto out; + return err; val |= BIT(pos); err = adnp_write(adnp, GPIO_DDR(adnp) + reg, val); if (err < 0) - goto out; + return err; err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val); if (err < 0) - goto out; + return err; - if (!(val & BIT(pos))) { - err = -EPERM; - goto out; - } + if (!(val & BIT(pos))) + return -EPERM; __adnp_gpio_set(adnp, offset, value); - err = 0; -out: - mutex_unlock(&adnp->i2c_lock); - return err; + return 0; } static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -187,27 +178,26 @@ static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) int err; for (i = 0; i < num_regs; i++) { - u8 ddr, plr, ier, isr; + u8 ddr = 0, plr = 0, ier = 0, isr = 0; - mutex_lock(&adnp->i2c_lock); + scoped_guard(mutex, &adnp->i2c_lock) { + err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr); + if (err < 0) + return; - err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr); - if (err < 0) - goto unlock; - - err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr); - if (err < 0) - goto unlock; + err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr); + if (err < 0) + return; - err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); - if (err < 0) - goto unlock; + err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); + if (err < 0) + return; - err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); - if (err < 0) - goto unlock; + err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); + if (err < 0) + return; - mutex_unlock(&adnp->i2c_lock); + } for (j = 0; j < 8; j++) { unsigned int bit = (i << adnp->reg_shift) + j; @@ -232,41 +222,6 @@ static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) direction, level, interrupt, pending); } } - - return; - -unlock: - mutex_unlock(&adnp->i2c_lock); -} - -static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios) -{ - struct gpio_chip *chip = &adnp->gpio; - int err; - - adnp->reg_shift = get_count_order(num_gpios) - 3; - - chip->direction_input = adnp_gpio_direction_input; - chip->direction_output = adnp_gpio_direction_output; - chip->get = adnp_gpio_get; - chip->set = adnp_gpio_set; - chip->can_sleep = true; - - if (IS_ENABLED(CONFIG_DEBUG_FS)) - chip->dbg_show = adnp_gpio_dbg_show; - - chip->base = -1; - chip->ngpio = num_gpios; - chip->label = adnp->client->name; - chip->parent = &adnp->client->dev; - chip->of_node = chip->parent->of_node; - chip->owner = THIS_MODULE; - - err = devm_gpiochip_add_data(&adnp->client->dev, chip, adnp); - if (err) - return err; - - return 0; } static irqreturn_t adnp_irq(int irq, void *data) @@ -278,32 +233,24 @@ static irqreturn_t adnp_irq(int irq, void *data) for (i = 0; i < num_regs; i++) { unsigned int base = i << adnp->reg_shift, bit; - u8 changed, level, isr, ier; + u8 changed, level = 0, isr = 0, ier = 0; unsigned long pending; int err; - mutex_lock(&adnp->i2c_lock); + scoped_guard(mutex, &adnp->i2c_lock) { + err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level); + if (err < 0) + continue; - err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level); - if (err < 0) { - mutex_unlock(&adnp->i2c_lock); - continue; - } - - err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); - if (err < 0) { - mutex_unlock(&adnp->i2c_lock); - continue; - } + err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); + if (err < 0) + continue; - err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); - if (err < 0) { - mutex_unlock(&adnp->i2c_lock); - continue; + err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); + if (err < 0) + continue; } - mutex_unlock(&adnp->i2c_lock); - /* determine pins that changed levels */ changed = level ^ adnp->irq_level[i]; @@ -337,6 +284,7 @@ static void adnp_irq_mask(struct irq_data *d) unsigned int pos = d->hwirq & 7; adnp->irq_enable[reg] &= ~BIT(pos); + gpiochip_disable_irq(gc, irqd_to_hwirq(d)); } static void adnp_irq_unmask(struct irq_data *d) @@ -346,6 +294,7 @@ static void adnp_irq_unmask(struct irq_data *d) unsigned int reg = d->hwirq >> adnp->reg_shift; unsigned int pos = d->hwirq & 7; + gpiochip_enable_irq(gc, irqd_to_hwirq(d)); adnp->irq_enable[reg] |= BIT(pos); } @@ -393,22 +342,24 @@ static void adnp_irq_bus_unlock(struct irq_data *d) struct adnp *adnp = gpiochip_get_data(gc); unsigned int num_regs = 1 << adnp->reg_shift, i; - mutex_lock(&adnp->i2c_lock); - - for (i = 0; i < num_regs; i++) - adnp_write(adnp, GPIO_IER(adnp) + i, adnp->irq_enable[i]); + scoped_guard(mutex, &adnp->i2c_lock) { + for (i = 0; i < num_regs; i++) + adnp_write(adnp, GPIO_IER(adnp) + i, + adnp->irq_enable[i]); + } - mutex_unlock(&adnp->i2c_lock); mutex_unlock(&adnp->irq_lock); } -static struct irq_chip adnp_irq_chip = { +static const struct irq_chip adnp_irq_chip = { .name = "gpio-adnp", .irq_mask = adnp_irq_mask, .irq_unmask = adnp_irq_unmask, .irq_set_type = adnp_irq_set_type, .irq_bus_lock = adnp_irq_bus_lock, .irq_bus_sync_unlock = adnp_irq_bus_unlock, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int adnp_irq_setup(struct adnp *adnp) @@ -465,55 +416,83 @@ static int adnp_irq_setup(struct adnp *adnp) return err; } - err = gpiochip_irqchip_add_nested(chip, - &adnp_irq_chip, - 0, - handle_simple_irq, - IRQ_TYPE_NONE); - if (err) { - dev_err(chip->parent, - "could not connect irqchip to gpiochip\n"); - return err; + return 0; +} + +static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, + bool is_irq_controller) +{ + struct gpio_chip *chip = &adnp->gpio; + int err; + + adnp->reg_shift = get_count_order(num_gpios) - 3; + + chip->direction_input = adnp_gpio_direction_input; + chip->direction_output = adnp_gpio_direction_output; + chip->get = adnp_gpio_get; + chip->set = adnp_gpio_set; + chip->can_sleep = true; + + if (IS_ENABLED(CONFIG_DEBUG_FS)) + chip->dbg_show = adnp_gpio_dbg_show; + + chip->base = -1; + chip->ngpio = num_gpios; + chip->label = adnp->client->name; + chip->parent = &adnp->client->dev; + chip->owner = THIS_MODULE; + + if (is_irq_controller) { + struct gpio_irq_chip *girq; + + err = adnp_irq_setup(adnp); + if (err) + return err; + + girq = &chip->irq; + gpio_irq_chip_set_chip(girq, &adnp_irq_chip); + + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; } - gpiochip_set_nested_irqchip(chip, &adnp_irq_chip, adnp->client->irq); + err = devm_gpiochip_add_data(&adnp->client->dev, chip, adnp); + if (err) + return err; return 0; } -static int adnp_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adnp_i2c_probe(struct i2c_client *client) { - struct device_node *np = client->dev.of_node; + struct device *dev = &client->dev; struct adnp *adnp; u32 num_gpios; int err; - err = of_property_read_u32(np, "nr-gpios", &num_gpios); + err = device_property_read_u32(dev, "nr-gpios", &num_gpios); if (err < 0) return err; - client->irq = irq_of_parse_and_map(np, 0); - if (!client->irq) - return -EPROBE_DEFER; - adnp = devm_kzalloc(&client->dev, sizeof(*adnp), GFP_KERNEL); if (!adnp) return -ENOMEM; - mutex_init(&adnp->i2c_lock); + err = devm_mutex_init(&client->dev, &adnp->i2c_lock); + if (err) + return err; + adnp->client = client; - err = adnp_gpio_setup(adnp, num_gpios); + err = adnp_gpio_setup(adnp, num_gpios, device_property_read_bool(dev, "interrupt-controller")); if (err) return err; - if (of_find_property(np, "interrupt-controller", NULL)) { - err = adnp_irq_setup(adnp); - if (err) - return err; - } - i2c_set_clientdata(client, adnp); return 0; |
