diff options
Diffstat (limited to 'drivers/gpio')
48 files changed, 1955 insertions, 1583 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c6b5c65c8405..8030fd91a3cc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -410,7 +410,7 @@ config GPIO_MXS config GPIO_OCTEON tristate "Cavium OCTEON GPIO" - depends on GPIOLIB && CAVIUM_OCTEON_SOC + depends on CAVIUM_OCTEON_SOC default y help Say yes here to support the on-chip GPIO lines on the OCTEON @@ -962,6 +962,14 @@ config GPIO_PCA953X_IRQ Say yes here to enable the pca953x to be used as an interrupt controller. It requires the driver to be built in the kernel. +config GPIO_PCA9570 + tristate "PCA9570 4-Bit I2C GPO expander" + help + Say yes here to enable the GPO driver for the NXP PCA9570 chip. + + To compile this driver as a module, choose M here: the module will + be called gpio-pca9570. + config GPIO_PCF857X tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders" select GPIOLIB_IRQCHIP @@ -1117,7 +1125,7 @@ config GPIO_DLN2 config HTC_EGPIO bool "HTC EGPIO support" - depends on GPIOLIB && ARM + depends on ARM help This driver supports the CPLD egpio chip present on several HTC phones. It provides basic support for input diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1e4894e0bf0f..4f9abff4f2dc 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o obj-$(CONFIG_GPIOLIB) += gpiolib-devprop.o +obj-$(CONFIG_GPIOLIB) += gpiolib-cdev.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o @@ -110,6 +111,7 @@ obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o +obj-$(CONFIG_GPIO_PCA9570) += gpio-pca9570.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index b989c9352da2..e560e45e84f8 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -5,7 +5,7 @@ subsystem. GPIO descriptors Starting with commit 79a9becda894 the GPIO subsystem embarked on a journey -to move away from the global GPIO numberspace and toward a decriptor-based +to move away from the global GPIO numberspace and toward a descriptor-based approach. This means that GPIO consumers, drivers and machine descriptions ideally have no use or idea of the global GPIO numberspace that has/was used in the inception of the GPIO subsystem. diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index 1f7d9bbec0fc..7a9021c4fa48 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -368,10 +368,21 @@ static const char *dio48e_names[DIO48E_NGPIO] = { "PPI Group 1 Port C 5", "PPI Group 1 Port C 6", "PPI Group 1 Port C 7" }; +static int dio48e_irq_init_hw(struct gpio_chip *gc) +{ + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(gc); + + /* Disable IRQ by default */ + inb(dio48egpio->base + 0xB); + + return 0; +} + static int dio48e_probe(struct device *dev, unsigned int id) { struct dio48e_gpio *dio48egpio; const char *const name = dev_name(dev); + struct gpio_irq_chip *girq; int err; dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL); @@ -399,13 +410,17 @@ static int dio48e_probe(struct device *dev, unsigned int id) dio48egpio->chip.set_multiple = dio48e_gpio_set_multiple; dio48egpio->base = base[id]; - raw_spin_lock_init(&dio48egpio->lock); + girq = &dio48egpio->chip.irq; + girq->chip = &dio48e_irqchip; + /* 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_edge_irq; + girq->init_hw = dio48e_irq_init_hw; - err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); - return err; - } + raw_spin_lock_init(&dio48egpio->lock); /* initialize all GPIO as output */ outb(0x80, base[id] + 3); @@ -419,13 +434,9 @@ static int dio48e_probe(struct device *dev, unsigned int id) outb(0x00, base[id] + 6); outb(0x00, base[id] + 7); - /* disable IRQ by default */ - inb(base[id] + 0xB); - - err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); + err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio); if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); + dev_err(dev, "GPIO registering failed (%d)\n", err); return err; } diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index d350ac0de06b..94c3a9bc4e75 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -247,10 +247,22 @@ static const char *idi48_names[IDI48_NGPIO] = { "Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B" }; +static int idi_48_irq_init_hw(struct gpio_chip *gc) +{ + struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc); + + /* Disable IRQ by default */ + outb(0, idi48gpio->base + 7); + inb(idi48gpio->base + 7); + + return 0; +} + static int idi_48_probe(struct device *dev, unsigned int id) { struct idi_48_gpio *idi48gpio; const char *const name = dev_name(dev); + struct gpio_irq_chip *girq; int err; idi48gpio = devm_kzalloc(dev, sizeof(*idi48gpio), GFP_KERNEL); @@ -275,6 +287,16 @@ static int idi_48_probe(struct device *dev, unsigned int id) idi48gpio->chip.get_multiple = idi_48_gpio_get_multiple; idi48gpio->base = base[id]; + girq = &idi48gpio->chip.irq; + girq->chip = &idi_48_irqchip; + /* 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_edge_irq; + girq->init_hw = idi_48_irq_init_hw; + raw_spin_lock_init(&idi48gpio->lock); spin_lock_init(&idi48gpio->ack_lock); @@ -284,17 +306,6 @@ static int idi_48_probe(struct device *dev, unsigned int id) return err; } - /* Disable IRQ by default */ - outb(0, base[id] + 7); - inb(base[id] + 7); - - err = gpiochip_irqchip_add(&idi48gpio->chip, &idi_48_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, irq[id], idi_48_irq_handler, IRQF_SHARED, name, idi48gpio); if (err) { diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 5752d9dab148..50ad0280fd78 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -224,10 +224,22 @@ static const char *idio_16_names[IDIO_16_NGPIO] = { "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" }; +static int idio_16_irq_init_hw(struct gpio_chip *gc) +{ + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); + + /* Disable IRQ by default */ + outb(0, idio16gpio->base + 2); + outb(0, idio16gpio->base + 1); + + return 0; +} + static int idio_16_probe(struct device *dev, unsigned int id) { struct idio_16_gpio *idio16gpio; const char *const name = dev_name(dev); + struct gpio_irq_chip *girq; int err; idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); @@ -256,6 +268,16 @@ static int idio_16_probe(struct device *dev, unsigned int id) idio16gpio->base = base[id]; idio16gpio->out_state = 0xFFFF; + girq = &idio16gpio->chip.irq; + girq->chip = &idio_16_irqchip; + /* 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_edge_irq; + girq->init_hw = idio_16_irq_init_hw; + raw_spin_lock_init(&idio16gpio->lock); err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); @@ -264,17 +286,6 @@ static int idio_16_probe(struct device *dev, unsigned int id) return err; } - /* Disable IRQ by default */ - outb(0, base[id] + 2); - outb(0, base[id] + 1); - - err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name, idio16gpio); if (err) { diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index b9fcaab2a931..8eedfc6451df 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -238,36 +238,6 @@ 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) { struct adnp *adnp = data; @@ -464,18 +434,54 @@ 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->of_node = chip->parent->of_node; + 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; + girq->chip = &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; } @@ -503,16 +509,11 @@ static int adnp_i2c_probe(struct i2c_client *client, mutex_init(&adnp->i2c_lock); adnp->client = client; - err = adnp_gpio_setup(adnp, num_gpios); + err = adnp_gpio_setup(adnp, num_gpios, + of_property_read_bool(np, "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; diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index 49f423d7beba..f1e4ac90e7d3 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -272,13 +272,24 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid) return IRQ_HANDLED; } + +static int adp5588_irq_init_hw(struct gpio_chip *gc) +{ + struct adp5588_gpio *dev = gpiochip_get_data(gc); + /* Enable IRQs after registering chip */ + adp5588_gpio_write(dev->client, CFG, + ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN); + + return 0; +} + static int adp5588_irq_setup(struct adp5588_gpio *dev) { struct i2c_client *client = dev->client; int ret; struct adp5588_gpio_platform_data *pdata = dev_get_platdata(&client->dev); - int irq_base = pdata ? pdata->irq_base : 0; + struct gpio_irq_chip *girq; adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ @@ -294,21 +305,19 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) client->irq); return ret; } - ret = gpiochip_irqchip_add_nested(&dev->gpio_chip, - &adp5588_irq_chip, irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } - gpiochip_set_nested_irqchip(&dev->gpio_chip, - &adp5588_irq_chip, - client->irq); - adp5588_gpio_write(client, CFG, - ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN); + /* This will be registered in the call to devm_gpiochip_add_data() */ + girq = &dev->gpio_chip.irq; + girq->chip = &adp5588_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->first = pdata ? pdata->irq_base : 0; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->init_hw = adp5588_irq_init_hw; + girq->threaded = true; return 0; } diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 9b0adbdddbfc..424a3d25350b 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -10,6 +10,7 @@ #include <linux/bitmap.h> #include <linux/bitops.h> #include <linux/ctype.h> +#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> @@ -38,9 +39,9 @@ static DEFINE_IDR(gpio_aggregator_idr); static char *get_arg(char **args) { - char *start = *args, *end; + char *start, *end; - start = skip_spaces(start); + start = skip_spaces(*args); if (!*start) return NULL; @@ -111,55 +112,45 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, static int aggr_parse(struct gpio_aggregator *aggr) { - unsigned int first_index, last_index, i, n = 0; - char *name, *offsets, *first, *last, *next; char *args = aggr->args; - int error; + unsigned long *bitmap; + unsigned int i, n = 0; + char *name, *offsets; + int error = 0; + + bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL); + if (!bitmap) + return -ENOMEM; for (name = get_arg(&args), offsets = get_arg(&args); name; offsets = get_arg(&args)) { if (IS_ERR(name)) { pr_err("Cannot get GPIO specifier: %pe\n", name); - return PTR_ERR(name); + error = PTR_ERR(name); + goto free_bitmap; } if (!isrange(offsets)) { /* Named GPIO line */ error = aggr_add_gpio(aggr, name, U16_MAX, &n); if (error) - return error; + goto free_bitmap; name = offsets; continue; } /* GPIO chip + offset(s) */ - for (first = offsets; *first; first = next) { - next = strchrnul(first, ','); - if (*next) - *next++ = '\0'; - - last = strchr(first, '-'); - if (last) - *last++ = '\0'; - - if (kstrtouint(first, 10, &first_index)) { - pr_err("Cannot parse GPIO index %s\n", first); - return -EINVAL; - } - - if (!last) { - last_index = first_index; - } else if (kstrtouint(last, 10, &last_index)) { - pr_err("Cannot parse GPIO index %s\n", last); - return -EINVAL; - } - - for (i = first_index; i <= last_index; i++) { - error = aggr_add_gpio(aggr, name, i, &n); - if (error) - return error; - } + error = bitmap_parselist(offsets, bitmap, ARCH_NR_GPIOS); + if (error) { + pr_err("Cannot parse %s: %d\n", offsets, error); + goto free_bitmap; + } + + for_each_set_bit(i, bitmap, ARCH_NR_GPIOS) { + error = aggr_add_gpio(aggr, name, i, &n); + if (error) + goto free_bitmap; } name = get_arg(&args); @@ -167,10 +158,12 @@ static int aggr_parse(struct gpio_aggregator *aggr) if (!n) { pr_err("No GPIOs specified\n"); - return -EINVAL; + error = -EINVAL; } - return 0; +free_bitmap: + bitmap_free(bitmap); + return error; } static ssize_t new_device_store(struct device_driver *driver, const char *buf, diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index cc4ba71e4fe3..b7932ecc3b61 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -24,6 +24,7 @@ * @interrupt_trigger : specifies the hardware configured IRQ trigger type * (rising, falling, both, high) * @mapped_irq : kernel mapped irq number. +* @irq_chip : IRQ chip configuration */ struct altera_gpio_chip { struct of_mm_gpio_chip mmchip; @@ -69,7 +70,7 @@ static void altera_gpio_irq_mask(struct irq_data *d) raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); } -/** +/* * This controller's IRQ type is synthesized in hardware, so this function * just checks if the requested set_type matches the synthesized IRQ type */ diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 14d1f4c933b6..2ba225720086 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -129,7 +129,7 @@ static void crystalcove_update_irq_ctrl(struct crystalcove_gpio *cg, int gpio) regmap_update_bits(cg->regmap, reg, CTLI_INTCNT_BE, cg->intcnt_value); } -static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio) +static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); @@ -140,7 +140,7 @@ static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio) return regmap_write(cg->regmap, reg, CTLO_INPUT_SET); } -static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio, +static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, int value) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); @@ -152,7 +152,7 @@ static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio, return regmap_write(cg->regmap, reg, CTLO_OUTPUT_SET | value); } -static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio) +static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); unsigned int val; @@ -169,7 +169,7 @@ static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio) } static void crystalcove_gpio_set(struct gpio_chip *chip, - unsigned gpio, int value) + unsigned int gpio, int value) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); @@ -183,7 +183,7 @@ static void crystalcove_gpio_set(struct gpio_chip *chip, regmap_update_bits(cg->regmap, reg, 1, 0); } -static int crystalcove_irq_type(struct irq_data *data, unsigned type) +static int crystalcove_irq_type(struct irq_data *data, unsigned int type) { struct crystalcove_gpio *cg = gpiochip_get_data(irq_data_get_irq_chip_data(data)); @@ -330,6 +330,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) int retval; struct device *dev = pdev->dev.parent; struct intel_soc_pmic *pmic = dev_get_drvdata(dev); + struct gpio_irq_chip *girq; if (irq < 0) return irq; @@ -353,46 +354,39 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) cg->chip.dbg_show = crystalcove_gpio_dbg_show; cg->regmap = pmic->regmap; - retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg); + girq = &cg->chip.irq; + girq->chip = &crystalcove_irqchip; + /* 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; + + retval = devm_request_threaded_irq(&pdev->dev, irq, NULL, + crystalcove_gpio_irq_handler, + IRQF_ONESHOT, KBUILD_MODNAME, cg); if (retval) { - dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval); + dev_warn(&pdev->dev, "request irq failed: %d\n", retval); return retval; } - gpiochip_irqchip_add_nested(&cg->chip, &crystalcove_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); - - retval = request_threaded_irq(irq, NULL, crystalcove_gpio_irq_handler, - IRQF_ONESHOT, KBUILD_MODNAME, cg); - + retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg); if (retval) { - dev_warn(&pdev->dev, "request irq failed: %d\n", retval); + dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval); return retval; } - gpiochip_set_nested_irqchip(&cg->chip, &crystalcove_irqchip, irq); - - return 0; -} - -static int crystalcove_gpio_remove(struct platform_device *pdev) -{ - struct crystalcove_gpio *cg = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - - if (irq >= 0) - free_irq(irq, cg); return 0; } static struct platform_driver crystalcove_gpio_driver = { .probe = crystalcove_gpio_probe, - .remove = crystalcove_gpio_remove, .driver = { .name = "crystal_cove_gpio", }, }; - module_platform_driver(crystalcove_gpio_driver); MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>"); diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 26b40c8b8a12..4c5f6d0c8d74 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -440,6 +440,7 @@ static int dln2_gpio_probe(struct platform_device *pdev) { struct dln2_gpio *dln2; struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; int pins; int ret; @@ -476,6 +477,15 @@ static int dln2_gpio_probe(struct platform_device *pdev) dln2->gpio.direction_output = dln2_gpio_direction_output; dln2->gpio.set_config = dln2_gpio_set_config; + girq = &dln2->gpio.irq; + girq->chip = &dln2_gpio_irqchip; + /* The event comes from the outside so no parent handler */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + platform_set_drvdata(pdev, dln2); ret = devm_gpiochip_add_data(dev, &dln2->gpio, dln2); @@ -484,13 +494,6 @@ static int dln2_gpio_probe(struct platform_device *pdev) return ret; } - ret = gpiochip_irqchip_add(&dln2->gpio, &dln2_gpio_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); - if (ret < 0) { - dev_err(dev, "failed to add irq chip: %d\n", ret); - return ret; - } - ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV, dln2_gpio_event); if (ret) { diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index 9960bb8b0f5b..de56c013a658 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -74,8 +74,8 @@ struct ichx_desc { u32 use_sel_ignore[3]; /* Some chipsets have quirks, let these use their own request/get */ - int (*request)(struct gpio_chip *chip, unsigned offset); - int (*get)(struct gpio_chip *chip, unsigned offset); + int (*request)(struct gpio_chip *chip, unsigned int offset); + int (*get)(struct gpio_chip *chip, unsigned int offset); /* * Some chipsets don't let reading output values on GPIO_LVL register @@ -100,7 +100,7 @@ static int modparam_gpiobase = -1; /* dynamic */ module_param_named(gpiobase, modparam_gpiobase, int, 0444); MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); -static int ichx_write_bit(int reg, unsigned nr, int val, int verify) +static int ichx_write_bit(int reg, unsigned int nr, int val, int verify) { unsigned long flags; u32 data, tmp; @@ -132,7 +132,7 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify) return (verify && data != tmp) ? -EPERM : 0; } -static int ichx_read_bit(int reg, unsigned nr) +static int ichx_read_bit(int reg, unsigned int nr) { unsigned long flags; u32 data; @@ -152,12 +152,12 @@ static int ichx_read_bit(int reg, unsigned nr) return !!(data & BIT(bit)); } -static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr) +static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned int nr) { return !!(ichx_priv.use_gpio & BIT(nr / 32)); } -static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr) +static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned int nr) { if (ichx_read_bit(GPIO_IO_SEL, nr)) return GPIO_LINE_DIRECTION_IN; @@ -165,7 +165,7 @@ static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr) return GPIO_LINE_DIRECTION_OUT; } -static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) { /* * Try setting pin as an input and verify it worked since many pins @@ -174,7 +174,7 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) return ichx_write_bit(GPIO_IO_SEL, nr, 1, 1); } -static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, +static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, int val) { /* Disable blink hardware which is available for GPIOs from 0 to 31. */ @@ -191,12 +191,12 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, return ichx_write_bit(GPIO_IO_SEL, nr, 0, 1); } -static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr) +static int ichx_gpio_get(struct gpio_chip *chip, unsigned int nr) { return ichx_read_bit(GPIO_LVL, nr); } -static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) +static int ich6_gpio_get(struct gpio_chip *chip, unsigned int nr) { unsigned long flags; u32 data; @@ -223,7 +223,7 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) } } -static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr) +static int ichx_gpio_request(struct gpio_chip *chip, unsigned int nr) { if (!ichx_gpio_check_available(chip, nr)) return -ENXIO; @@ -240,7 +240,7 @@ static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr) return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV; } -static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr) +static int ich6_gpio_request(struct gpio_chip *chip, unsigned int nr) { /* * Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100 @@ -254,7 +254,7 @@ static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr) return ichx_gpio_request(chip, nr); } -static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val) +static void ichx_gpio_set(struct gpio_chip *chip, unsigned int nr, int val) { ichx_write_bit(GPIO_LVL, nr, val, 0); } diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index b497a1d18ca9..8f1be34953ce 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -47,13 +47,13 @@ /** * struct it87_gpio - it87-specific GPIO chip - * @chip the underlying gpio_chip structure - * @lock a lock to avoid races between operations - * @io_base base address for gpio ports - * @io_size size of the port rage starting from io_base. - * @output_base Super I/O register address for Output Enable register - * @simple_base Super I/O 'Simple I/O' Enable register - * @simple_size Super IO 'Simple I/O' Enable register size; this is + * @chip: the underlying gpio_chip structure + * @lock: a lock to avoid races between operations + * @io_base: base address for gpio ports + * @io_size: size of the port rage starting from io_base. + * @output_base: Super I/O register address for Output Enable register + * @simple_base: Super I/O 'Simple I/O' Enable register + * @simple_size: Super IO 'Simple I/O' Enable register size; this is * required because IT87xx chips might only provide Simple I/O * switches on a subset of lines, whereas the others keep the * same status all time. diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 5fb0bcf31142..238cbe926b9f 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -503,6 +503,8 @@ static int max732x_irq_setup(struct max732x_chip *chip, if (((pdata && pdata->irq_base) || client->irq) && has_irq != INT_NONE) { + struct gpio_irq_chip *girq; + if (pdata) irq_base = pdata->irq_base; chip->irq_features = has_irq; @@ -517,19 +519,17 @@ static int max732x_irq_setup(struct max732x_chip *chip, client->irq); return ret; } - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, - &max732x_irq_chip, - irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } - gpiochip_set_nested_irqchip(&chip->gpio_chip, - &max732x_irq_chip, - client->irq); + + girq = &chip->gpio_chip.irq; + girq->chip = &max732x_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; + girq->first = irq_base; /* FIXME: get rid of this */ } return 0; @@ -695,15 +695,15 @@ static int max732x_probe(struct i2c_client *client, return ret; } - ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); + ret = max732x_irq_setup(chip, id); if (ret) return ret; - ret = max732x_irq_setup(chip, id); + ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); if (ret) return ret; - if (pdata && pdata->setup) { + if (pdata->setup) { ret = pdata->setup(client, chip->gpio_chip.base, chip->gpio_chip.ngpio, pdata->context); if (ret < 0) diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 313bd02dd893..7c0a9ef0b500 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -19,8 +19,8 @@ struct max77620_gpio { struct regmap *rmap; struct device *dev; struct mutex buslock; /* irq_bus_lock */ - unsigned int irq_type[8]; - bool irq_enabled[8]; + unsigned int irq_type[MAX77620_GPIO_NR]; + bool irq_enabled[MAX77620_GPIO_NR]; }; static irqreturn_t max77620_gpio_irqhandler(int irq, void *data) @@ -38,7 +38,7 @@ static irqreturn_t max77620_gpio_irqhandler(int irq, void *data) pending = value; - for_each_set_bit(offset, &pending, 8) { + for_each_set_bit(offset, &pending, MAX77620_GPIO_NR) { unsigned int virq; virq = irq_find_mapping(gpio->gpio_chip.irq.domain, offset); @@ -260,26 +260,54 @@ static int max77620_gpio_set_config(struct gpio_chip *gc, unsigned int offset, return -ENOTSUPP; } +static int max77620_gpio_irq_init_hw(struct gpio_chip *gc) +{ + struct max77620_gpio *gpio = gpiochip_get_data(gc); + unsigned int i; + int err; + + /* + * GPIO interrupts may be left ON after bootloader, hence let's + * pre-initialize hardware to the expected state by disabling all + * the interrupts. + */ + for (i = 0; i < MAX77620_GPIO_NR; i++) { + err = regmap_update_bits(gpio->rmap, GPIO_REG_ADDR(i), + MAX77620_CNFG_GPIO_INT_MASK, 0); + if (err < 0) { + dev_err(gpio->dev, + "failed to disable interrupt: %d\n", err); + return err; + } + } + + return 0; +} + static int max77620_gpio_probe(struct platform_device *pdev) { struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max77620_gpio *mgpio; - int gpio_irq; + struct gpio_irq_chip *girq; + unsigned int gpio_irq; int ret; - gpio_irq = platform_get_irq(pdev, 0); - if (gpio_irq <= 0) - return -ENODEV; + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + + gpio_irq = ret; mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL); if (!mgpio) return -ENOMEM; + mutex_init(&mgpio->buslock); mgpio->rmap = chip->rmap; mgpio->dev = &pdev->dev; mgpio->gpio_chip.label = pdev->name; - mgpio->gpio_chip.parent = &pdev->dev; + mgpio->gpio_chip.parent = pdev->dev.parent; mgpio->gpio_chip.direction_input = max77620_gpio_dir_input; mgpio->gpio_chip.get = max77620_gpio_get; mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; @@ -288,9 +316,17 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; mgpio->gpio_chip.can_sleep = 1; mgpio->gpio_chip.base = -1; -#ifdef CONFIG_OF_GPIO - mgpio->gpio_chip.of_node = pdev->dev.parent->of_node; -#endif + + girq = &mgpio->gpio_chip.irq; + girq->chip = &max77620_gpio_irqchip; + /* 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_edge_irq; + girq->init_hw = max77620_gpio_irq_init_hw, + girq->threaded = true; platform_set_drvdata(pdev, mgpio); @@ -300,21 +336,14 @@ static int max77620_gpio_probe(struct platform_device *pdev) return ret; } - mutex_init(&mgpio->buslock); - - gpiochip_irqchip_add_nested(&mgpio->gpio_chip, &max77620_gpio_irqchip, - 0, handle_edge_irq, IRQ_TYPE_NONE); - - ret = request_threaded_irq(gpio_irq, NULL, max77620_gpio_irqhandler, - IRQF_ONESHOT, "max77620-gpio", mgpio); + ret = devm_request_threaded_irq(&pdev->dev, gpio_irq, NULL, + max77620_gpio_irqhandler, IRQF_ONESHOT, + "max77620-gpio", mgpio); if (ret < 0) { dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); return ret; } - gpiochip_set_nested_irqchip(&mgpio->gpio_chip, &max77620_gpio_irqchip, - gpio_irq); - return 0; } diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 92b6e958cfed..53d4abefa6ff 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -44,7 +44,7 @@ struct ioh_regs { /** * struct ioh_gpio_reg_data - The register store data. - * @ien_reg To store contents of interrupt enable register. + * @ien_reg: To store contents of interrupt enable register. * @imask_reg: To store contents of interrupt mask regist * @po_reg: To store contents of PO register. * @pm_reg: To store contents of PM register. diff --git a/drivers/gpio/gpio-mlxbf.c b/drivers/gpio/gpio-mlxbf.c index 894aaf55fc96..1fa9973f55b9 100644 --- a/drivers/gpio/gpio-mlxbf.c +++ b/drivers/gpio/gpio-mlxbf.c @@ -127,7 +127,7 @@ static int mlxbf_gpio_resume(struct platform_device *pdev) } #endif -static const struct acpi_device_id mlxbf_gpio_acpi_match[] = { +static const struct acpi_device_id __maybe_unused mlxbf_gpio_acpi_match[] = { { "MLNXBF02", 0 }, {} }; diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 94d5efce1721..befa5e109943 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -149,6 +149,8 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs) * Release the YU arm_gpio_lock after changing the direction mode. */ static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs) + __releases(&gs->gc.bgpio_lock) + __releases(yu_arm_gpio_lock_param.lock) { writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io); spin_unlock(&gs->gc.bgpio_lock); @@ -309,7 +311,7 @@ static int mlxbf2_gpio_resume(struct platform_device *pdev) } #endif -static const struct acpi_device_id mlxbf2_gpio_acpi_match[] = { +static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = { { "MLNXBF22", 0 }, {}, }; diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index b778f33cc6af..c335a0309ba3 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -195,8 +195,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, *bits &= ~*mask; /* Create a mirrored mask */ - bit = -1; - while ((bit = find_next_bit(mask, gc->ngpio, bit + 1)) < gc->ngpio) + for_each_set_bit(bit, mask, gc->ngpio) readmask |= bgpio_line2mask(gc, bit); /* Read the register */ @@ -206,8 +205,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, * Mirror the result into the "bits" result, this will give line 0 * in bit 0 ... line 31 in bit 31 for a 32bit register. */ - bit = -1; - while ((bit = find_next_bit(&val, gc->ngpio, bit + 1)) < gc->ngpio) + for_each_set_bit(bit, &val, gc->ngpio) *bits |= bgpio_line2mask(gc, bit); return 0; @@ -272,15 +270,11 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc, *set_mask = 0; *clear_mask = 0; - for (i = 0; i < gc->bgpio_bits; i++) { - if (*mask == 0) - break; - if (__test_and_clear_bit(i, mask)) { - if (test_bit(i, bits)) - *set_mask |= bgpio_line2mask(gc, i); - else - *clear_mask |= bgpio_line2mask(gc, i); - } + for_each_set_bit(i, mask, gc->bgpio_bits) { + if (test_bit(i, bits)) + *set_mask |= bgpio_line2mask(gc, i); + else + *clear_mask |= bgpio_line2mask(gc, i); } } diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 604dfec353a1..1e866524a4bd 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -417,7 +417,7 @@ static int mpc8xxx_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn, mpc8xxx_gpio_irq_cascade, - IRQF_NO_THREAD | IRQF_SHARED, "gpio-cascade", + IRQF_SHARED, "gpio-cascade", mpc8xxx_gc); if (ret) { dev_err(&pdev->dev, "%s: failed to devm_request_irq(%d), ret = %d\n", diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index bd65114eb170..433e2c3f3fd5 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -846,6 +846,7 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; + const char *label; int i; regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out); @@ -857,15 +858,10 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) edg_msk = mvebu_gpio_read_edge_mask(mvchip); lvl_msk = mvebu_gpio_read_level_mask(mvchip); - for (i = 0; i < chip->ngpio; i++) { - const char *label; + for_each_requested_gpio(chip, i, label) { u32 msk; bool is_out; - label = gpiochip_is_requested(chip, i); - if (!label) - continue; - msk = BIT(i); is_out = !(io_conf & msk); diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index b8e2ecc3eade..7fbe0c9e1fc1 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -60,6 +60,7 @@ struct gpio_bank { struct clk *dbck; struct notifier_block nb; unsigned int is_suspended:1; + unsigned int needs_resume:1; u32 mod_usage; u32 irq_usage; u32 dbck_enable_mask; @@ -896,12 +897,23 @@ static int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset, unsigned long config) { u32 debounce; + int ret = -ENOTSUPP; - if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) - return -ENOTSUPP; + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = gpiochip_generic_config(chip, offset, config); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + debounce = pinconf_to_config_argument(config); + ret = omap_gpio_debounce(chip, offset, debounce); + break; + default: + break; + } - debounce = pinconf_to_config_argument(config); - return omap_gpio_debounce(chip, offset, debounce); + return ret; } static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value) @@ -1504,9 +1516,34 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) return 0; } +static int omap_gpio_suspend(struct device *dev) +{ + struct gpio_bank *bank = dev_get_drvdata(dev); + + if (bank->is_suspended) + return 0; + + bank->needs_resume = 1; + + return omap_gpio_runtime_suspend(dev); +} + +static int omap_gpio_resume(struct device *dev) +{ + struct gpio_bank *bank = dev_get_drvdata(dev); + + if (!bank->needs_resume) + return 0; + + bank->needs_resume = 0; + + return omap_gpio_runtime_resume(dev); +} + static const struct dev_pm_ops gpio_pm_ops = { SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) }; static struct platform_driver omap_gpio_driver = { diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index a3b9bdedbe44..bd2e96c34f82 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -89,6 +89,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "max7310", 8 | PCA953X_TYPE, }, @@ -833,6 +834,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) struct irq_chip *irq_chip = &chip->irq_chip; DECLARE_BITMAP(reg_direction, MAX_LINE); DECLARE_BITMAP(irq_stat, MAX_LINE); + struct gpio_irq_chip *girq; int ret; if (dmi_first_match(pca953x_dmi_acpi_irq_info)) { @@ -863,17 +865,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) bitmap_and(chip->irq_stat, irq_stat, reg_direction, chip->gpio_chip.ngpio); mutex_init(&chip->irq_lock); - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, pca953x_irq_handler, - IRQF_ONESHOT | IRQF_SHARED, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - return ret; - } - - irq_chip->name = dev_name(&chip->client->dev); + irq_chip->name = dev_name(&client->dev); irq_chip->irq_mask = pca953x_irq_mask; irq_chip->irq_unmask = pca953x_irq_unmask; irq_chip->irq_set_wake = pca953x_irq_set_wake; @@ -882,17 +874,27 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) irq_chip->irq_set_type = pca953x_irq_set_type; irq_chip->irq_shutdown = pca953x_irq_shutdown; - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, irq_chip, - irq_base, handle_simple_irq, - IRQ_TYPE_NONE); + girq = &chip->gpio_chip.irq; + girq->chip = 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; + girq->first = irq_base; /* FIXME: get rid of this */ + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pca953x_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(&client->dev), chip); if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); return ret; } - gpiochip_set_nested_irqchip(&chip->gpio_chip, irq_chip, client->irq); - return 0; } @@ -1079,11 +1081,11 @@ static int pca953x_probe(struct i2c_client *client, if (ret) goto err_exit; - ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); + ret = pca953x_irq_setup(chip, irq_base); if (ret) goto err_exit; - ret = pca953x_irq_setup(chip, irq_base); + ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); if (ret) goto err_exit; @@ -1234,6 +1236,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "maxim,max7310", .data = OF_953X( 8, 0), }, diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c new file mode 100644 index 000000000000..cb2b2f735c15 --- /dev/null +++ b/drivers/gpio/gpio-pca9570.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for PCA9570 I2C GPO expander + * + * Copyright (C) 2020 Sungbo Eo <mans0n@gorani.run> + * + * Based on gpio-tpic2810.c + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis <afd@ti.com> + */ + +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/property.h> + +/** + * struct pca9570 - GPIO driver data + * @chip: GPIO controller chip + * @lock: Protects write sequences + * @out: Buffer for device register + */ +struct pca9570 { + struct gpio_chip chip; + struct mutex lock; + u8 out; +}; + +static int pca9570_read(struct pca9570 *gpio, u8 *value) +{ + struct i2c_client *client = to_i2c_client(gpio->chip.parent); + int ret; + + ret = i2c_smbus_read_byte(client); + if (ret < 0) + return ret; + + *value = ret; + return 0; +} + +static int pca9570_write(struct pca9570 *gpio, u8 value) +{ + struct i2c_client *client = to_i2c_client(gpio->chip.parent); + + return i2c_smbus_write_byte(client, value); +} + +static int pca9570_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + /* This device always output */ + return GPIO_LINE_DIRECTION_OUT; +} + +static int pca9570_get(struct gpio_chip *chip, unsigned offset) +{ + struct pca9570 *gpio = gpiochip_get_data(chip); + u8 buffer; + int ret; + + ret = pca9570_read(gpio, &buffer); + if (ret) + return ret; + + return !!(buffer & BIT(offset)); +} + +static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct pca9570 *gpio = gpiochip_get_data(chip); + u8 buffer; + int ret; + + mutex_lock(&gpio->lock); + + buffer = gpio->out; + if (value) + buffer |= BIT(offset); + else + buffer &= ~BIT(offset); + + ret = pca9570_write(gpio, buffer); + if (ret) + goto out; + + gpio->out = buffer; + +out: + mutex_unlock(&gpio->lock); +} + +static int pca9570_probe(struct i2c_client *client) +{ + struct pca9570 *gpio; + + gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->chip.label = client->name; + gpio->chip.parent = &client->dev; + gpio->chip.owner = THIS_MODULE; + gpio->chip.get_direction = pca9570_get_direction; + gpio->chip.get = pca9570_get; + gpio->chip.set = pca9570_set; + gpio->chip.base = -1; + gpio->chip.ngpio = (uintptr_t)device_get_match_data(&client->dev); + gpio->chip.can_sleep = true; + + mutex_init(&gpio->lock); + + /* Read the current output level */ + pca9570_read(gpio, &gpio->out); + + i2c_set_clientdata(client, gpio); + + return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); +} + +static const struct i2c_device_id pca9570_id_table[] = { + { "pca9570", 4 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, pca9570_id_table); + +static const struct of_device_id pca9570_of_match_table[] = { + { .compatible = "nxp,pca9570", .data = (void *)4 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pca9570_of_match_table); + +static struct i2c_driver pca9570_driver = { + .driver = { + .name = "pca9570", + .of_match_table = pca9570_of_match_table, + }, + .probe_new = pca9570_probe, + .id_table = pca9570_id_table, +}; +module_i2c_driver(pca9570_driver); + +MODULE_AUTHOR("Sungbo Eo <mans0n@gorani.run>"); +MODULE_DESCRIPTION("GPIO expander driver for PCA9570"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 14fb8f6a1ad2..a2a8d155c75e 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -334,29 +334,19 @@ static int pcf857x_probe(struct i2c_client *client, gpio->out = ~n_latch; gpio->status = gpio->out; - status = devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); - if (status < 0) - goto fail; - /* Enable irqchip if we have an interrupt */ if (client->irq) { - gpio->irqchip.name = "pcf857x", - gpio->irqchip.irq_enable = pcf857x_irq_enable, - gpio->irqchip.irq_disable = pcf857x_irq_disable, - gpio->irqchip.irq_ack = noop, - gpio->irqchip.irq_mask = noop, - gpio->irqchip.irq_unmask = noop, - gpio->irqchip.irq_set_wake = pcf857x_irq_set_wake, - gpio->irqchip.irq_bus_lock = pcf857x_irq_bus_lock, - gpio->irqchip.irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock, - status = gpiochip_irqchip_add_nested(&gpio->chip, - &gpio->irqchip, - 0, handle_level_irq, - IRQ_TYPE_NONE); - if (status) { - dev_err(&client->dev, "cannot add irqchip\n"); - goto fail; - } + struct gpio_irq_chip *girq; + + gpio->irqchip.name = "pcf857x"; + gpio->irqchip.irq_enable = pcf857x_irq_enable; + gpio->irqchip.irq_disable = pcf857x_irq_disable; + gpio->irqchip.irq_ack = noop; + gpio->irqchip.irq_mask = noop; + gpio->irqchip.irq_unmask = noop; + gpio->irqchip.irq_set_wake = pcf857x_irq_set_wake; + gpio->irqchip.irq_bus_lock = pcf857x_irq_bus_lock; + gpio->irqchip.irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock; status = devm_request_threaded_irq(&client->dev, client->irq, NULL, pcf857x_irq, IRQF_ONESHOT | @@ -365,10 +355,21 @@ static int pcf857x_probe(struct i2c_client *client, if (status) goto fail; - gpiochip_set_nested_irqchip(&gpio->chip, &gpio->irqchip, - client->irq); + girq = &gpio->chip.irq; + girq->chip = &gpio->irqchip; + /* 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_level_irq; + girq->threaded = true; } + status = devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); + if (status < 0) + goto fail; + /* Let platform code set up the GPIOs and their users. * Now is the first time anyone could use them. */ diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index e96d28bf43b4..a552df298a97 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -95,7 +95,7 @@ struct pch_gpio { spinlock_t spinlock; }; -static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { u32 reg_val; struct pch_gpio *chip = gpiochip_get_data(gpio); @@ -112,14 +112,14 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) spin_unlock_irqrestore(&chip->spinlock, flags); } -static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr) +static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) { struct pch_gpio *chip = gpiochip_get_data(gpio); return !!(ioread32(&chip->reg->pi) & BIT(nr)); } -static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, +static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, int val) { struct pch_gpio *chip = gpiochip_get_data(gpio); @@ -146,7 +146,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, return 0; } -static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) { struct pch_gpio *chip = gpiochip_get_data(gpio); u32 pm; @@ -196,9 +196,10 @@ static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip) iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, &chip->reg->gpio_use_sel); } -static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) +static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned int offset) { struct pch_gpio *chip = gpiochip_get_data(gpio); + return chip->irq_base + offset; } @@ -304,9 +305,10 @@ static irqreturn_t pch_gpio_handler(int irq, void *dev_id) unsigned long reg_val = ioread32(&chip->reg->istatus); int i; - dev_dbg(chip->dev, "irq=%d status=0x%lx\n", irq, reg_val); + dev_vdbg(chip->dev, "irq=%d status=0x%lx\n", irq, reg_val); reg_val &= BIT(gpio_pins[chip->ioh]) - 1; + for_each_set_bit(i, ®_val, gpio_pins[chip->ioh]) generic_handle_irq(chip->irq_base + i); diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 638d6656ce73..9acec76e0b51 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -280,6 +280,17 @@ static const char *idio_16_names[IDIO_16_NGPIO] = { "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" }; +static int idio_16_irq_init_hw(struct gpio_chip *gc) +{ + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); + + /* Disable IRQ by default and clear any pending interrupt */ + iowrite8(0, &idio16gpio->reg->irq_ctl); + iowrite8(0, &idio16gpio->reg->in0_7); + + return 0; +} + static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *const dev = &pdev->dev; @@ -287,6 +298,7 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) int err; const size_t pci_bar_index = 2; const char *const name = pci_name(pdev); + struct gpio_irq_chip *girq; idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); if (!idio16gpio) @@ -323,6 +335,16 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) idio16gpio->chip.set = idio_16_gpio_set; idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; + girq = &idio16gpio->chip.irq; + girq->chip = &idio_16_irqchip; + /* 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_edge_irq; + girq->init_hw = idio_16_irq_init_hw; + raw_spin_lock_init(&idio16gpio->lock); err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); @@ -331,17 +353,6 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - /* Disable IRQ by default and clear any pending interrupt */ - iowrite8(0, &idio16gpio->reg->irq_ctl); - iowrite8(0, &idio16gpio->reg->in0_7); - - err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED, name, idio16gpio); if (err) { diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c index 1d475794a50f..a68941d19ac6 100644 --- a/drivers/gpio/gpio-pcie-idio-24.c +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -457,6 +457,7 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) int err; const size_t pci_bar_index = 2; const char *const name = pci_name(pdev); + struct gpio_irq_chip *girq; idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL); if (!idio24gpio) @@ -490,6 +491,15 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) idio24gpio->chip.set = idio_24_gpio_set; idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple; + girq = &idio24gpio->chip.irq; + girq->chip = &idio_24_irqchip; + /* 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_edge_irq; + raw_spin_lock_init(&idio24gpio->lock); /* Software board reset */ @@ -501,13 +511,6 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED, name, idio24gpio); if (err) { diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 05000cace9b2..938285190566 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -48,7 +48,7 @@ enum { * struct sprd_pmic_eic - PMIC EIC controller * @chip: the gpio_chip structure. * @intc: the irq_chip structure. - * @regmap: the regmap from the parent device. + * @map: the regmap from the parent device. * @offset: the EIC controller's offset address of the PMIC. * @reg: the array to cache the EIC registers. * @buslock: for bus lock/sync and unlock. diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index eac1582c70da..3ef19cef8da9 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -438,6 +438,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) struct resource *irq; struct gpio_chip *gpio_chip; struct irq_chip *irq_chip; + struct gpio_irq_chip *girq; struct device *dev = &pdev->dev; const char *name = dev_name(dev); unsigned int npins; @@ -496,19 +497,21 @@ static int gpio_rcar_probe(struct platform_device *pdev) irq_chip->irq_set_wake = gpio_rcar_irq_set_wake; irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND; + girq = &gpio_chip->irq; + girq->chip = 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_level_irq; + ret = gpiochip_add_data(gpio_chip, p); if (ret) { dev_err(dev, "failed to add GPIO controller\n"); goto err0; } - ret = gpiochip_irqchip_add(gpio_chip, irq_chip, 0, handle_level_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "cannot add irqchip\n"); - goto err1; - } - p->irq_parent = irq->start; if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler, IRQF_SHARED, name, p)) { diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index 4d47b2c41186..b7c950658170 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -49,7 +49,7 @@ struct sama5d2_piobu { struct regmap *regmap; }; -/** +/* * sama5d2_piobu_setup_pin() - prepares a pin for set_direction call * * Do not consider pin for tamper detection (normal and backup modes) @@ -73,7 +73,7 @@ static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin) return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0); } -/** +/* * sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register */ static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin, @@ -88,7 +88,7 @@ static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin, return regmap_update_bits(piobu->regmap, reg, mask, value); } -/** +/* * sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU * register */ @@ -108,7 +108,7 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin, return val & mask; } -/** +/* * sama5d2_piobu_get_direction() - gpiochip get_direction */ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, @@ -123,7 +123,7 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, GPIO_LINE_DIRECTION_OUT; } -/** +/* * sama5d2_piobu_direction_input() - gpiochip direction_input */ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, @@ -132,7 +132,7 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN); } -/** +/* * sama5d2_piobu_direction_output() - gpiochip direction_output */ static int sama5d2_piobu_direction_output(struct gpio_chip *chip, @@ -147,7 +147,7 @@ static int sama5d2_piobu_direction_output(struct gpio_chip *chip, val); } -/** +/* * sama5d2_piobu_get() - gpiochip get */ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) @@ -166,7 +166,7 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) return !!ret; } -/** +/* * sama5d2_piobu_set() - gpiochip set */ static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index c65f35b68202..3a1b1adb08c6 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -26,10 +26,10 @@ struct sch_gpio { unsigned short resume_base; }; -static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio, - unsigned reg) +static unsigned int sch_gpio_offset(struct sch_gpio *sch, unsigned int gpio, + unsigned int reg) { - unsigned base = 0; + unsigned int base = 0; if (gpio >= sch->resume_base) { gpio -= sch->resume_base; @@ -39,14 +39,14 @@ static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio, return base + reg + gpio / 8; } -static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio) +static unsigned int sch_gpio_bit(struct sch_gpio *sch, unsigned int gpio) { if (gpio >= sch->resume_base) gpio -= sch->resume_base; return gpio % 8; } -static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned gpio, unsigned reg) +static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned int gpio, unsigned int reg) { unsigned short offset, bit; u8 reg_val; @@ -59,7 +59,7 @@ static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned gpio, unsigned reg) return reg_val; } -static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned gpio, unsigned reg, +static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned int gpio, unsigned int reg, int val) { unsigned short offset, bit; @@ -76,7 +76,7 @@ static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned gpio, unsigned reg, outb((reg_val & ~BIT(bit)), sch->iobase + offset); } -static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num) +static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); @@ -86,13 +86,14 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num) return 0; } -static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num) +static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); + return sch_gpio_reg_get(sch, gpio_num, GLV); } -static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val) +static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); @@ -101,7 +102,7 @@ static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val) spin_unlock(&sch->lock); } -static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num, +static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); @@ -123,7 +124,7 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num, return 0; } -static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned gpio_num) +static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 542706a852e6..6c48809d0505 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -500,13 +500,9 @@ static int stmpe_gpio_probe(struct platform_device *pdev) if (ret) goto out_free; - ret = gpiochip_add_data(&stmpe_gpio->chip, stmpe_gpio); - if (ret) { - dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); - goto out_disable; - } - if (irq > 0) { + struct gpio_irq_chip *girq; + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT, "stmpe-gpio", stmpe_gpio); @@ -514,20 +510,22 @@ static int stmpe_gpio_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to get irq: %d\n", ret); goto out_disable; } - ret = gpiochip_irqchip_add_nested(&stmpe_gpio->chip, - &stmpe_gpio_irq_chip, - 0, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&pdev->dev, - "could not connect irqchip to gpiochip\n"); - goto out_disable; - } - gpiochip_set_nested_irqchip(&stmpe_gpio->chip, - &stmpe_gpio_irq_chip, - irq); + girq = &stmpe_gpio->chip.irq; + girq->chip = &stmpe_gpio_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; + } + + ret = gpiochip_add_data(&stmpe_gpio->chip, stmpe_gpio); + if (ret) { + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); + goto out_disable; } platform_set_drvdata(pdev, stmpe_gpio); diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 31f332074d7d..fdd3d497b535 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -24,16 +24,16 @@ /** * struct syscon_gpio_data - Configuration for the device. - * compatible: SYSCON driver compatible string. - * flags: Set of GPIO_SYSCON_FEAT_ flags: + * @compatible: SYSCON driver compatible string. + * @flags: Set of GPIO_SYSCON_FEAT_ flags: * GPIO_SYSCON_FEAT_IN: GPIOs supports input, * GPIO_SYSCON_FEAT_OUT: GPIOs supports output, * GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction. - * bit_count: Number of bits used as GPIOs. - * dat_bit_offset: Offset (in bits) to the first GPIO bit. - * dir_bit_offset: Optional offset (in bits) to the first bit to switch + * @bit_count: Number of bits used as GPIOs. + * @dat_bit_offset: Offset (in bits) to the first GPIO bit. + * @dir_bit_offset: Optional offset (in bits) to the first bit to switch * GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag). - * set: HW specific callback to assigns output value + * @set: HW specific callback to assigns output value * for signal "offset" */ diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 6be0684cfa49..58b0da9eb76f 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -289,6 +289,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); struct device_node *np = pdev->dev.of_node; struct tc3589x_gpio *tc3589x_gpio; + struct gpio_irq_chip *girq; int ret; int irq; @@ -317,6 +318,16 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) tc3589x_gpio->chip.base = -1; tc3589x_gpio->chip.of_node = np; + girq = &tc3589x_gpio->chip.irq; + girq->chip = &tc3589x_gpio_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; + /* Bring the GPIO module out of reset */ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_RSTCTRL_GPIRST, 0); @@ -339,21 +350,6 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) return ret; } - ret = gpiochip_irqchip_add_nested(&tc3589x_gpio->chip, - &tc3589x_gpio_irq_chip, - 0, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&pdev->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } - - gpiochip_set_nested_irqchip(&tc3589x_gpio->chip, - &tc3589x_gpio_irq_chip, - irq); - platform_set_drvdata(pdev, tc3589x_gpio); return 0; diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 8b481b3c1ebe..b5fbba5a783a 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -400,6 +400,7 @@ static int wcove_gpio_probe(struct platform_device *pdev) struct wcove_gpio *wg; int virq, ret, irq; struct device *dev; + struct gpio_irq_chip *girq; /* * This gpio platform device is created by a mfd device (see @@ -442,33 +443,34 @@ static int wcove_gpio_probe(struct platform_device *pdev) wg->dev = dev; wg->regmap = pmic->regmap; - ret = devm_gpiochip_add_data(dev, &wg->chip, wg); - if (ret) { - dev_err(dev, "Failed to add gpiochip: %d\n", ret); - return ret; - } - - ret = gpiochip_irqchip_add_nested(&wg->chip, &wcove_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "Failed to add irqchip: %d\n", ret); - return ret; - } - virq = regmap_irq_get_virq(wg->regmap_irq_chip, irq); if (virq < 0) { dev_err(dev, "Failed to get virq by irq %d\n", irq); return virq; } - ret = devm_request_threaded_irq(dev, virq, NULL, - wcove_gpio_irq_handler, IRQF_ONESHOT, pdev->name, wg); + girq = &wg->chip.irq; + girq->chip = &wcove_irqchip; + /* 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; + + ret = devm_request_threaded_irq(dev, virq, NULL, wcove_gpio_irq_handler, + IRQF_ONESHOT, pdev->name, wg); if (ret) { dev_err(dev, "Failed to request irq %d\n", virq); return ret; } - gpiochip_set_nested_irqchip(&wg->chip, &wcove_irqchip, virq); + ret = devm_gpiochip_add_data(dev, &wg->chip, wg); + if (ret) { + dev_err(dev, "Failed to add gpiochip: %d\n", ret); + return ret; + } /* Enable GPIO0 interrupts */ ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE, GPIO_IRQ0_MASK, diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index cb510df2b014..2d89d0529135 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -365,10 +365,25 @@ static const char *ws16c48_names[WS16C48_NGPIO] = { "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7" }; +static int ws16c48_irq_init_hw(struct gpio_chip *gc) +{ + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); + + /* Disable IRQ by default */ + outb(0x80, ws16c48gpio->base + 7); + outb(0, ws16c48gpio->base + 8); + outb(0, ws16c48gpio->base + 9); + outb(0, ws16c48gpio->base + 10); + outb(0xC0, ws16c48gpio->base + 7); + + return 0; +} + static int ws16c48_probe(struct device *dev, unsigned int id) { struct ws16c48_gpio *ws16c48gpio; const char *const name = dev_name(dev); + struct gpio_irq_chip *girq; int err; ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); @@ -396,6 +411,16 @@ static int ws16c48_probe(struct device *dev, unsigned int id) ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple; ws16c48gpio->base = base[id]; + girq = &ws16c48gpio->chip.irq; + girq->chip = &ws16c48_irqchip; + /* 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_edge_irq; + girq->init_hw = ws16c48_irq_init_hw; + raw_spin_lock_init(&ws16c48gpio->lock); err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio); @@ -404,20 +429,6 @@ static int ws16c48_probe(struct device *dev, unsigned int id) return err; } - /* Disable IRQ by default */ - outb(0x80, base[id] + 7); - outb(0, base[id] + 8); - outb(0, base[id] + 9); - outb(0, base[id] + 10); - outb(0xC0, base[id] + 7); - - err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED, name, ws16c48gpio); if (err) { diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 31b5072b2df0..e2cac12092af 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -121,6 +121,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) struct xra1403 *xra = gpiochip_get_data(chip); int value[XRA_LAST]; int i; + const char *label; unsigned int gcr; unsigned int gsr; @@ -136,12 +137,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) gcr = value[XRA_GCR + 1] << 8 | value[XRA_GCR]; gsr = value[XRA_GSR + 1] << 8 | value[XRA_GSR]; - for (i = 0; i < chip->ngpio; i++) { - const char *label = gpiochip_is_requested(chip, i); - - if (!label) - continue; - + for_each_requested_gpio(chip, i, label) { seq_printf(s, " gpio-%-3d (%-12s) %s %s\n", chip->base + i, label, (gcr & BIT(i)) ? "in" : "out", diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 05ba16fffdad..53d1387592fd 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -10,6 +10,7 @@ #include <linux/gpio/driver.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/spinlock.h> #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -21,6 +22,9 @@ /* Maximum banks */ #define ZYNQ_GPIO_MAX_BANK 4 #define ZYNQMP_GPIO_MAX_BANK 6 +#define VERSAL_GPIO_MAX_BANK 4 +#define PMC_GPIO_MAX_BANK 5 +#define VERSAL_UNUSED_BANKS 2 #define ZYNQ_GPIO_BANK0_NGPIO 32 #define ZYNQ_GPIO_BANK1_NGPIO 22 @@ -95,6 +99,7 @@ /* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */ #define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0) #define GPIO_QUIRK_DATA_RO_BUG BIT(1) +#define GPIO_QUIRK_VERSAL BIT(2) struct gpio_regs { u32 datamsw[ZYNQMP_GPIO_MAX_BANK]; @@ -116,6 +121,7 @@ struct gpio_regs { * @irq: interrupt for the GPIO device * @p_data: pointer to platform data * @context: context registers + * @dirlock: lock used for direction in/out synchronization */ struct zynq_gpio { struct gpio_chip chip; @@ -124,6 +130,7 @@ struct zynq_gpio { int irq; const struct zynq_platform_data *p_data; struct gpio_regs context; + spinlock_t dirlock; /* lock */ }; /** @@ -196,6 +203,8 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num, gpio->p_data->bank_min[bank]; return; } + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank = bank + VERSAL_UNUSED_BANKS; } /* default */ @@ -297,6 +306,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) { u32 reg; unsigned int bank_num, bank_pin_num; + unsigned long flags; struct zynq_gpio *gpio = gpiochip_get_data(chip); zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); @@ -310,9 +320,11 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) return -EINVAL; /* clear the bit in direction mode reg to set the pin as input */ + spin_lock_irqsave(&gpio->dirlock, flags); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); reg &= ~BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + spin_unlock_irqrestore(&gpio->dirlock, flags); return 0; } @@ -334,11 +346,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, { u32 reg; unsigned int bank_num, bank_pin_num; + unsigned long flags; struct zynq_gpio *gpio = gpiochip_get_data(chip); zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); /* set the GPIO pin as output */ + spin_lock_irqsave(&gpio->dirlock, flags); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); reg |= BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); @@ -347,6 +361,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); reg |= BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); + spin_unlock_irqrestore(&gpio->dirlock, flags); /* set the state of the pin */ zynq_gpio_set_value(chip, pin, state); @@ -647,6 +662,8 @@ static void zynq_gpio_irqhandler(struct irq_desc *desc) int_enb = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_INTMASK_OFFSET(bank_num)); zynq_gpio_handle_bank_irq(gpio, bank_num, int_sts & ~int_enb); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } chained_irq_exit(irqchip, desc); @@ -676,6 +693,8 @@ static void zynq_gpio_save_context(struct zynq_gpio *gpio) gpio->context.int_any[bank_num] = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_INTANY_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } } @@ -707,6 +726,8 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio) writel_relaxed(~(gpio->context.int_en[bank_num]), gpio->base_addr + ZYNQ_GPIO_INTEN_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } } @@ -715,6 +736,9 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev) struct zynq_gpio *gpio = dev_get_drvdata(dev); struct irq_data *data = irq_get_irq_data(gpio->irq); + if (!device_may_wakeup(dev)) + disable_irq(gpio->irq); + if (!irqd_is_wakeup_set(data)) { zynq_gpio_save_context(gpio); return pm_runtime_force_suspend(dev); @@ -729,6 +753,9 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev) struct irq_data *data = irq_get_irq_data(gpio->irq); int ret; + if (!device_may_wakeup(dev)) + enable_irq(gpio->irq); + if (!irqd_is_wakeup_set(data)) { ret = pm_runtime_force_resume(dev); zynq_gpio_restore_context(gpio); @@ -778,6 +805,31 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { zynq_gpio_runtime_resume, NULL) }; +static const struct zynq_platform_data versal_gpio_def = { + .label = "versal_gpio", + .quirks = GPIO_QUIRK_VERSAL, + .ngpio = 58, + .max_bank = VERSAL_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[3] = 26, + .bank_max[3] = 57, /* Bank 3 is connected to FMIOs (32 pins) */ +}; + +static const struct zynq_platform_data pmc_gpio_def = { + .label = "pmc_gpio", + .ngpio = 116, + .max_bank = PMC_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[1] = 26, + .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */ + .bank_min[3] = 52, + .bank_max[3] = 83, /* Bank 3 is connected to EMIOs (32 pins) */ + .bank_min[4] = 84, + .bank_max[4] = 115, /* Bank 4 is connected to EMIOs (32 pins) */ +}; + static const struct zynq_platform_data zynqmp_gpio_def = { .label = "zynqmp_gpio", .quirks = GPIO_QUIRK_DATA_RO_BUG, @@ -815,6 +867,8 @@ static const struct zynq_platform_data zynq_gpio_def = { static const struct of_device_id zynq_gpio_of_match[] = { { .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def }, { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, + { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def }, + { .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); @@ -876,7 +930,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) /* Retrieve GPIO clock */ gpio->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(gpio->clk)) { - dev_err(&pdev->dev, "input clock not found.\n"); + if (PTR_ERR(gpio->clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "input clock not found.\n"); return PTR_ERR(gpio->clk); } ret = clk_prepare_enable(gpio->clk); @@ -885,6 +940,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) return ret; } + spin_lock_init(&gpio->dirlock); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); @@ -892,9 +949,12 @@ static int zynq_gpio_probe(struct platform_device *pdev) goto err_pm_dis; /* disable interrupts for all banks */ - for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) + for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) { writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; + } /* Set up the GPIO irqchip */ girq = &chip->irq; @@ -919,6 +979,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) goto err_pm_put; } + irq_set_status_flags(gpio->irq, IRQ_DISABLE_UNLAZY); + device_init_wakeup(&pdev->dev, 1); pm_runtime_put(&pdev->dev); return 0; diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c new file mode 100644 index 000000000000..e6c9b78adfc2 --- /dev/null +++ b/drivers/gpio/gpiolib-cdev.c @@ -0,0 +1,1121 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/anon_inodes.h> +#include <linux/bitmap.h> +#include <linux/cdev.h> +#include <linux/compat.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/file.h> +#include <linux/gpio.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/kernel.h> +#include <linux/kfifo.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> +#include <linux/poll.h> +#include <linux/spinlock.h> +#include <linux/timekeeping.h> +#include <linux/uaccess.h> +#include <uapi/linux/gpio.h> + +#include "gpiolib.h" +#include "gpiolib-cdev.h" + +/* Character device interface to GPIO. + * + * The GPIO character device, /dev/gpiochipN, provides userspace an + * interface to gpiolib GPIOs via ioctl()s. + */ + +/* + * GPIO line handle management + */ + +/** + * struct linehandle_state - contains the state of a userspace handle + * @gdev: the GPIO device the handle pertains to + * @label: consumer label used to tag descriptors + * @descs: the GPIO descriptors held by this handle + * @num_descs: the number of descriptors held in the descs array + */ +struct linehandle_state { + struct gpio_device *gdev; + const char *label; + struct gpio_desc *descs[GPIOHANDLES_MAX]; + u32 num_descs; +}; + +#define GPIOHANDLE_REQUEST_VALID_FLAGS \ + (GPIOHANDLE_REQUEST_INPUT | \ + GPIOHANDLE_REQUEST_OUTPUT | \ + GPIOHANDLE_REQUEST_ACTIVE_LOW | \ + GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ + GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ + GPIOHANDLE_REQUEST_BIAS_DISABLE | \ + GPIOHANDLE_REQUEST_OPEN_DRAIN | \ + GPIOHANDLE_REQUEST_OPEN_SOURCE) + +static int linehandle_validate_flags(u32 flags) +{ + /* Return an error if an unknown flag is set */ + if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) + return -EINVAL; + + /* + * Do not allow both INPUT & OUTPUT flags to be set as they are + * contradictory. + */ + if ((flags & GPIOHANDLE_REQUEST_INPUT) && + (flags & GPIOHANDLE_REQUEST_OUTPUT)) + return -EINVAL; + + /* + * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If + * the hardware actually supports enabling both at the same time the + * electrical result would be disastrous. + */ + if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ + if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) && + ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) + return -EINVAL; + + /* Bias flags only allowed for input or output mode. */ + if (!((flags & GPIOHANDLE_REQUEST_INPUT) || + (flags & GPIOHANDLE_REQUEST_OUTPUT)) && + ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + + return 0; +} + +static void linehandle_flags_to_desc_flags(u32 lflags, unsigned long *flagsp) +{ + assign_bit(FLAG_ACTIVE_LOW, flagsp, + lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); + assign_bit(FLAG_OPEN_DRAIN, flagsp, + lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); + assign_bit(FLAG_OPEN_SOURCE, flagsp, + lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); + assign_bit(FLAG_PULL_UP, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); + assign_bit(FLAG_PULL_DOWN, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); + assign_bit(FLAG_BIAS_DISABLE, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); +} + +static long linehandle_set_config(struct linehandle_state *lh, + void __user *ip) +{ + struct gpiohandle_config gcnf; + struct gpio_desc *desc; + int i, ret; + u32 lflags; + + if (copy_from_user(&gcnf, ip, sizeof(gcnf))) + return -EFAULT; + + lflags = gcnf.flags; + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; + + for (i = 0; i < lh->num_descs; i++) { + desc = lh->descs[i]; + linehandle_flags_to_desc_flags(gcnf.flags, &desc->flags); + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!gcnf.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + return ret; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + return ret; + } + + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_CONFIG, desc); + } + return 0; +} + +static long linehandle_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct linehandle_state *lh = file->private_data; + void __user *ip = (void __user *)arg; + struct gpiohandle_data ghd; + DECLARE_BITMAP(vals, GPIOHANDLES_MAX); + int i; + + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + /* NOTE: It's ok to read values of output lines. */ + int ret = gpiod_get_array_value_complex(false, + true, + lh->num_descs, + lh->descs, + NULL, + vals); + if (ret) + return ret; + + memset(&ghd, 0, sizeof(ghd)); + for (i = 0; i < lh->num_descs; i++) + ghd.values[i] = test_bit(i, vals); + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { + /* + * 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; + + /* Clamp all values to [0,1] */ + for (i = 0; i < lh->num_descs; i++) + __assign_bit(i, vals, ghd.values[i]); + + /* Reuse the array setting function */ + return gpiod_set_array_value_complex(false, + true, + lh->num_descs, + lh->descs, + NULL, + vals); + } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { + return linehandle_set_config(lh, ip); + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long linehandle_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return linehandle_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static void linehandle_free(struct linehandle_state *lh) +{ + int i; + + for (i = 0; i < lh->num_descs; i++) + if (lh->descs[i]) + gpiod_free(lh->descs[i]); + kfree(lh->label); + put_device(&lh->gdev->dev); + kfree(lh); +} + +static int linehandle_release(struct inode *inode, struct file *file) +{ + linehandle_free(file->private_data); + return 0; +} + +static const struct file_operations linehandle_fileops = { + .release = linehandle_release, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = linehandle_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = linehandle_ioctl_compat, +#endif +}; + +static int linehandle_create(struct gpio_device *gdev, void __user *ip) +{ + struct gpiohandle_request handlereq; + struct linehandle_state *lh; + struct file *file; + int fd, i, ret; + u32 lflags; + + if (copy_from_user(&handlereq, ip, sizeof(handlereq))) + return -EFAULT; + if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX)) + return -EINVAL; + + lflags = handlereq.flags; + + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; + + lh = kzalloc(sizeof(*lh), GFP_KERNEL); + if (!lh) + return -ENOMEM; + lh->gdev = gdev; + get_device(&gdev->dev); + + /* Make sure this is terminated */ + handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0'; + if (strlen(handlereq.consumer_label)) { + lh->label = kstrdup(handlereq.consumer_label, + GFP_KERNEL); + if (!lh->label) { + ret = -ENOMEM; + goto out_free_lh; + } + } + + lh->num_descs = handlereq.lines; + + /* Request each GPIO */ + for (i = 0; i < handlereq.lines; i++) { + u32 offset = handlereq.lineoffsets[i]; + struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); + goto out_free_lh; + } + + ret = gpiod_request(desc, lh->label); + if (ret) + goto out_free_lh; + lh->descs[i] = desc; + linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags); + + ret = gpiod_set_transitory(desc, false); + if (ret < 0) + goto out_free_lh; + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!handlereq.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + goto out_free_lh; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_lh; + } + + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); + + dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", + offset); + } + + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_lh; + } + + file = anon_inode_getfile("gpio-linehandle", + &linehandle_fileops, + lh, + O_RDONLY | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto out_put_unused_fd; + } + + handlereq.fd = fd; + if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { + /* + * fput() will trigger the release() callback, so do not go onto + * the regular error cleanup path here. + */ + fput(file); + put_unused_fd(fd); + return -EFAULT; + } + + fd_install(fd, file); + + dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", + lh->num_descs); + + return 0; + +out_put_unused_fd: + put_unused_fd(fd); +out_free_lh: + linehandle_free(lh); + return ret; +} + +/* + * GPIO line event management + */ + +/** + * struct lineevent_state - contains the state of a userspace event + * @gdev: the GPIO device the event pertains to + * @label: consumer label used to tag descriptors + * @desc: the GPIO descriptor held by this event + * @eflags: the event flags this line was requested with + * @irq: the interrupt that trigger in response to events on this GPIO + * @wait: wait queue that handles blocking reads of events + * @events: KFIFO for the GPIO events + * @timestamp: cache for the timestamp storing it between hardirq + * and IRQ thread, used to bring the timestamp close to the actual + * event + */ +struct lineevent_state { + struct gpio_device *gdev; + const char *label; + struct gpio_desc *desc; + u32 eflags; + int irq; + wait_queue_head_t wait; + DECLARE_KFIFO(events, struct gpioevent_data, 16); + u64 timestamp; +}; + +#define GPIOEVENT_REQUEST_VALID_FLAGS \ + (GPIOEVENT_REQUEST_RISING_EDGE | \ + GPIOEVENT_REQUEST_FALLING_EDGE) + +static __poll_t lineevent_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct lineevent_state *le = file->private_data; + __poll_t events = 0; + + poll_wait(file, &le->wait, wait); + + if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + + +static ssize_t lineevent_read(struct file *file, + char __user *buf, + size_t count, + loff_t *f_ps) +{ + struct lineevent_state *le = file->private_data; + struct gpioevent_data ge; + ssize_t bytes_read = 0; + int ret; + + if (count < sizeof(ge)) + return -EINVAL; + + do { + spin_lock(&le->wait.lock); + if (kfifo_is_empty(&le->events)) { + if (bytes_read) { + spin_unlock(&le->wait.lock); + return bytes_read; + } + + if (file->f_flags & O_NONBLOCK) { + spin_unlock(&le->wait.lock); + return -EAGAIN; + } + + ret = wait_event_interruptible_locked(le->wait, + !kfifo_is_empty(&le->events)); + if (ret) { + spin_unlock(&le->wait.lock); + return ret; + } + } + + ret = kfifo_out(&le->events, &ge, 1); + spin_unlock(&le->wait.lock); + if (ret != 1) { + /* + * This should never happen - we were holding the lock + * from the moment we learned the fifo is no longer + * empty until now. + */ + ret = -EIO; + break; + } + + if (copy_to_user(buf + bytes_read, &ge, sizeof(ge))) + return -EFAULT; + bytes_read += sizeof(ge); + } while (count >= bytes_read + sizeof(ge)); + + return bytes_read; +} + +static void lineevent_free(struct lineevent_state *le) +{ + if (le->irq) + free_irq(le->irq, le); + if (le->desc) + gpiod_free(le->desc); + kfree(le->label); + put_device(&le->gdev->dev); + kfree(le); +} + +static int lineevent_release(struct inode *inode, struct file *file) +{ + lineevent_free(file->private_data); + return 0; +} + +static long lineevent_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct lineevent_state *le = file->private_data; + void __user *ip = (void __user *)arg; + struct gpiohandle_data ghd; + + /* + * We can get the value for an event line but not set it, + * because it is input by definition. + */ + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + int val; + + memset(&ghd, 0, sizeof(ghd)); + + val = gpiod_get_value_cansleep(le->desc); + if (val < 0) + return val; + ghd.values[0] = val; + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long lineevent_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return lineevent_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations lineevent_fileops = { + .release = lineevent_release, + .read = lineevent_read, + .poll = lineevent_poll, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = lineevent_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lineevent_ioctl_compat, +#endif +}; + +static irqreturn_t lineevent_irq_thread(int irq, void *p) +{ + struct lineevent_state *le = p; + struct gpioevent_data ge; + int ret; + + /* Do not leak kernel stack to userspace */ + memset(&ge, 0, sizeof(ge)); + + /* + * We may be running from a nested threaded interrupt in which case + * we didn't get the timestamp from lineevent_irq_handler(). + */ + if (!le->timestamp) + ge.timestamp = ktime_get_ns(); + else + ge.timestamp = le->timestamp; + + if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE + && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + int level = gpiod_get_value_cansleep(le->desc); + + if (level) + /* Emit low-to-high event */ + ge.id = GPIOEVENT_EVENT_RISING_EDGE; + else + /* Emit high-to-low event */ + ge.id = GPIOEVENT_EVENT_FALLING_EDGE; + } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { + /* Emit low-to-high event */ + ge.id = GPIOEVENT_EVENT_RISING_EDGE; + } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + /* Emit high-to-low event */ + ge.id = GPIOEVENT_EVENT_FALLING_EDGE; + } else { + return IRQ_NONE; + } + + ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge, + 1, &le->wait.lock); + if (ret) + wake_up_poll(&le->wait, EPOLLIN); + else + pr_debug_ratelimited("event FIFO is full - event dropped\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t lineevent_irq_handler(int irq, void *p) +{ + struct lineevent_state *le = p; + + /* + * Just store the timestamp in hardirq context so we get it as + * close in time as possible to the actual event. + */ + le->timestamp = ktime_get_ns(); + + return IRQ_WAKE_THREAD; +} + +static int lineevent_create(struct gpio_device *gdev, void __user *ip) +{ + struct gpioevent_request eventreq; + struct lineevent_state *le; + struct gpio_desc *desc; + struct file *file; + u32 offset; + u32 lflags; + u32 eflags; + int fd; + int ret; + int irq, irqflags = 0; + + if (copy_from_user(&eventreq, ip, sizeof(eventreq))) + return -EFAULT; + + offset = eventreq.lineoffset; + lflags = eventreq.handleflags; + eflags = eventreq.eventflags; + + desc = gpiochip_get_desc(gdev->chip, offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + /* Return an error if a unknown flag is set */ + if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || + (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) + return -EINVAL; + + /* This is just wrong: we don't look for events on output lines */ + if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || + (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + + le = kzalloc(sizeof(*le), GFP_KERNEL); + if (!le) + return -ENOMEM; + le->gdev = gdev; + get_device(&gdev->dev); + + /* Make sure this is terminated */ + eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0'; + if (strlen(eventreq.consumer_label)) { + le->label = kstrdup(eventreq.consumer_label, + GFP_KERNEL); + if (!le->label) { + ret = -ENOMEM; + goto out_free_le; + } + } + + ret = gpiod_request(desc, le->label); + if (ret) + goto out_free_le; + le->desc = desc; + le->eflags = eflags; + + linehandle_flags_to_desc_flags(lflags, &desc->flags); + + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_le; + + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); + + irq = gpiod_to_irq(desc); + if (irq <= 0) { + ret = -ENODEV; + goto out_free_le; + } + le->irq = irq; + + if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) + irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? + IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE) + irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + irqflags |= IRQF_ONESHOT; + + INIT_KFIFO(le->events); + init_waitqueue_head(&le->wait); + + /* Request a thread to read the events */ + ret = request_threaded_irq(le->irq, + lineevent_irq_handler, + lineevent_irq_thread, + irqflags, + le->label, + le); + if (ret) + goto out_free_le; + + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_le; + } + + file = anon_inode_getfile("gpio-event", + &lineevent_fileops, + le, + O_RDONLY | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto out_put_unused_fd; + } + + eventreq.fd = fd; + if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { + /* + * fput() will trigger the release() callback, so do not go onto + * the regular error cleanup path here. + */ + fput(file); + put_unused_fd(fd); + return -EFAULT; + } + + fd_install(fd, file); + + return 0; + +out_put_unused_fd: + put_unused_fd(fd); +out_free_le: + lineevent_free(le); + return ret; +} + +static void gpio_desc_to_lineinfo(struct gpio_desc *desc, + struct gpioline_info *info) +{ + struct gpio_chip *gc = desc->gdev->chip; + bool ok_for_pinctrl; + unsigned long flags; + + /* + * This function takes a mutex so we must check this before taking + * the spinlock. + * + * FIXME: find a non-racy way to retrieve this information. Maybe a + * lock common to both frameworks? + */ + ok_for_pinctrl = + pinctrl_gpio_can_use_line(gc->base + info->line_offset); + + spin_lock_irqsave(&gpio_lock, flags); + + if (desc->name) { + strncpy(info->name, desc->name, sizeof(info->name)); + info->name[sizeof(info->name) - 1] = '\0'; + } else { + info->name[0] = '\0'; + } + + if (desc->label) { + strncpy(info->consumer, desc->label, sizeof(info->consumer)); + info->consumer[sizeof(info->consumer) - 1] = '\0'; + } else { + info->consumer[0] = '\0'; + } + + /* + * Userspace only need to know that the kernel is using this GPIO so + * it can't use it. + */ + info->flags = 0; + if (test_bit(FLAG_REQUESTED, &desc->flags) || + test_bit(FLAG_IS_HOGGED, &desc->flags) || + test_bit(FLAG_USED_AS_IRQ, &desc->flags) || + test_bit(FLAG_EXPORT, &desc->flags) || + test_bit(FLAG_SYSFS, &desc->flags) || + !ok_for_pinctrl) + info->flags |= GPIOLINE_FLAG_KERNEL; + if (test_bit(FLAG_IS_OUT, &desc->flags)) + info->flags |= GPIOLINE_FLAG_IS_OUT; + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + info->flags |= GPIOLINE_FLAG_ACTIVE_LOW; + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN | + GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE | + GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_DISABLE; + if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; + if (test_bit(FLAG_PULL_UP, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_PULL_UP; + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +struct gpio_chardev_data { + struct gpio_device *gdev; + wait_queue_head_t wait; + DECLARE_KFIFO(events, struct gpioline_info_changed, 32); + struct notifier_block lineinfo_changed_nb; + unsigned long *watched_lines; +}; + +/* + * gpio_ioctl() - ioctl handler for the GPIO chardev + */ +static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct gpio_chardev_data *cdev = file->private_data; + struct gpio_device *gdev = cdev->gdev; + struct gpio_chip *gc = gdev->chip; + void __user *ip = (void __user *)arg; + struct gpio_desc *desc; + __u32 offset; + + /* We fail any subsequent ioctl():s when the chip is gone */ + if (!gc) + return -ENODEV; + + /* Fill in the struct and pass to userspace */ + if (cmd == GPIO_GET_CHIPINFO_IOCTL) { + struct gpiochip_info chipinfo; + + memset(&chipinfo, 0, sizeof(chipinfo)); + + strncpy(chipinfo.name, dev_name(&gdev->dev), + sizeof(chipinfo.name)); + chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; + strncpy(chipinfo.label, gdev->label, + sizeof(chipinfo.label)); + chipinfo.label[sizeof(chipinfo.label)-1] = '\0'; + chipinfo.lines = gdev->ngpio; + if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) + return -EFAULT; + return 0; + } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { + struct gpioline_info lineinfo; + + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + + /* this doubles as a range check on line_offset */ + desc = gpiochip_get_desc(gc, lineinfo.line_offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + gpio_desc_to_lineinfo(desc, &lineinfo); + + if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) + return -EFAULT; + return 0; + } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { + return linehandle_create(gdev, ip); + } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { + return lineevent_create(gdev, ip); + } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) { + struct gpioline_info lineinfo; + + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + + /* this doubles as a range check on line_offset */ + desc = gpiochip_get_desc(gc, lineinfo.line_offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines)) + return -EBUSY; + + gpio_desc_to_lineinfo(desc, &lineinfo); + + if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) { + clear_bit(lineinfo.line_offset, cdev->watched_lines); + return -EFAULT; + } + + return 0; + } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { + if (copy_from_user(&offset, ip, sizeof(offset))) + return -EFAULT; + + if (offset >= cdev->gdev->ngpio) + return -EINVAL; + + if (!test_and_clear_bit(offset, cdev->watched_lines)) + return -EBUSY; + + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long gpio_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return gpio_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static struct gpio_chardev_data * +to_gpio_chardev_data(struct notifier_block *nb) +{ + return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); +} + +static int lineinfo_changed_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct gpio_chardev_data *cdev = to_gpio_chardev_data(nb); + struct gpioline_info_changed chg; + struct gpio_desc *desc = data; + int ret; + + if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines)) + return NOTIFY_DONE; + + memset(&chg, 0, sizeof(chg)); + chg.info.line_offset = gpio_chip_hwgpio(desc); + chg.event_type = action; + chg.timestamp = ktime_get_ns(); + gpio_desc_to_lineinfo(desc, &chg.info); + + ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock); + if (ret) + wake_up_poll(&cdev->wait, EPOLLIN); + else + pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n"); + + return NOTIFY_OK; +} + +static __poll_t lineinfo_watch_poll(struct file *file, + struct poll_table_struct *pollt) +{ + struct gpio_chardev_data *cdev = file->private_data; + __poll_t events = 0; + + poll_wait(file, &cdev->wait, pollt); + + if (!kfifo_is_empty_spinlocked_noirqsave(&cdev->events, + &cdev->wait.lock)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + +static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_chardev_data *cdev = file->private_data; + struct gpioline_info_changed event; + ssize_t bytes_read = 0; + int ret; + + if (count < sizeof(event)) + return -EINVAL; + + do { + spin_lock(&cdev->wait.lock); + if (kfifo_is_empty(&cdev->events)) { + if (bytes_read) { + spin_unlock(&cdev->wait.lock); + return bytes_read; + } + + if (file->f_flags & O_NONBLOCK) { + spin_unlock(&cdev->wait.lock); + return -EAGAIN; + } + + ret = wait_event_interruptible_locked(cdev->wait, + !kfifo_is_empty(&cdev->events)); + if (ret) { + spin_unlock(&cdev->wait.lock); + return ret; + } + } + + ret = kfifo_out(&cdev->events, &event, 1); + spin_unlock(&cdev->wait.lock); + if (ret != 1) { + ret = -EIO; + break; + /* We should never get here. See lineevent_read(). */ + } + + if (copy_to_user(buf + bytes_read, &event, sizeof(event))) + return -EFAULT; + bytes_read += sizeof(event); + } while (count >= bytes_read + sizeof(event)); + + return bytes_read; +} + +/** + * gpio_chrdev_open() - open the chardev for ioctl operations + * @inode: inode for this chardev + * @file: file struct for storing private data + * Returns 0 on success + */ +static int gpio_chrdev_open(struct inode *inode, struct file *file) +{ + struct gpio_device *gdev = container_of(inode->i_cdev, + struct gpio_device, chrdev); + struct gpio_chardev_data *cdev; + int ret = -ENOMEM; + + /* Fail on open if the backing gpiochip is gone */ + if (!gdev->chip) + return -ENODEV; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); + if (!cdev->watched_lines) + goto out_free_cdev; + + init_waitqueue_head(&cdev->wait); + INIT_KFIFO(cdev->events); + cdev->gdev = gdev; + + cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; + ret = blocking_notifier_chain_register(&gdev->notifier, + &cdev->lineinfo_changed_nb); + if (ret) + goto out_free_bitmap; + + get_device(&gdev->dev); + file->private_data = cdev; + + ret = nonseekable_open(inode, file); + if (ret) + goto out_unregister_notifier; + + return ret; + +out_unregister_notifier: + blocking_notifier_chain_unregister(&gdev->notifier, + &cdev->lineinfo_changed_nb); +out_free_bitmap: + bitmap_free(cdev->watched_lines); +out_free_cdev: + kfree(cdev); + return ret; +} + +/** + * gpio_chrdev_release() - close chardev after ioctl operations + * @inode: inode for this chardev + * @file: file struct for storing private data + * Returns 0 on success + */ +static int gpio_chrdev_release(struct inode *inode, struct file *file) +{ + struct gpio_chardev_data *cdev = file->private_data; + struct gpio_device *gdev = cdev->gdev; + + bitmap_free(cdev->watched_lines); + blocking_notifier_chain_unregister(&gdev->notifier, + &cdev->lineinfo_changed_nb); + put_device(&gdev->dev); + kfree(cdev); + + return 0; +} + +static const struct file_operations gpio_fileops = { + .release = gpio_chrdev_release, + .open = gpio_chrdev_open, + .poll = lineinfo_watch_poll, + .read = lineinfo_watch_read, + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = gpio_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gpio_ioctl_compat, +#endif +}; + +int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) +{ + int ret; + + cdev_init(&gdev->chrdev, &gpio_fileops); + gdev->chrdev.owner = THIS_MODULE; + gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id); + + ret = cdev_device_add(&gdev->chrdev, &gdev->dev); + if (ret) + return ret; + + chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", + MAJOR(devt), gdev->id); + + return 0; +} + +void gpiolib_cdev_unregister(struct gpio_device *gdev) +{ + cdev_device_del(&gdev->chrdev, &gdev->dev); +} diff --git a/drivers/gpio/gpiolib-cdev.h b/drivers/gpio/gpiolib-cdev.h new file mode 100644 index 000000000000..973578e7ad10 --- /dev/null +++ b/drivers/gpio/gpiolib-cdev.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIOLIB_CDEV_H +#define GPIOLIB_CDEV_H + +#include <linux/device.h> + +int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt); +void gpiolib_cdev_unregister(struct gpio_device *gdev); + +#endif /* GPIOLIB_CDEV_H */ diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 5c91c4365da1..7dbce4c4ebdf 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -487,10 +487,12 @@ static void devm_gpio_chip_release(struct device *dev, void *res) } /** - * devm_gpiochip_add_data() - Resource managed gpiochip_add_data() + * devm_gpiochip_add_data_with_key() - Resource managed gpiochip_add_data_with_key() * @dev: pointer to the device that gpio_chip belongs to. * @gc: the GPIO chip to register * @data: driver-private data associated with this chip + * @lock_key: lockdep class for IRQ lock + * @request_key: lockdep class for IRQ request * * Context: potentially before irqs will work * @@ -501,8 +503,9 @@ static void devm_gpio_chip_release(struct device *dev, void *res) * gc->base is invalid or already associated with a different chip. * Otherwise it returns zero as a success code. */ -int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc, - void *data) +int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, void *data, + struct lock_class_key *lock_key, + struct lock_class_key *request_key) { struct gpio_chip **ptr; int ret; @@ -512,7 +515,7 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc, if (!ptr) return -ENOMEM; - ret = gpiochip_add_data(gc, data); + ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key); if (ret < 0) { devres_free(ptr); return ret; @@ -523,4 +526,4 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc, return 0; } -EXPORT_SYMBOL_GPL(devm_gpiochip_add_data); +EXPORT_SYMBOL_GPL(devm_gpiochip_add_data_with_key); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 219eb0054233..bd31dd3b6a75 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -25,8 +25,11 @@ /** * of_gpio_spi_cs_get_count() - special GPIO counting for SPI + * @dev: Consuming device + * @con_id: Function within the GPIO consumer + * * Some elder GPIO controllers need special quirks. Currently we handle - * the Freescale GPIO controller with bindings that doesn't use the + * the Freescale and PPC GPIO controller with bindings that doesn't use the * established "cs-gpios" for chip selects but instead rely on * "gpios" for the chip select lines. If we detect this, we redirect * the counting of "cs-gpios" to count "gpios" transparent to the @@ -41,7 +44,8 @@ static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id) if (!con_id || strcmp(con_id, "cs")) return 0; if (!of_device_is_compatible(np, "fsl,spi") && - !of_device_is_compatible(np, "aeroflexgaisler,spictrl")) + !of_device_is_compatible(np, "aeroflexgaisler,spictrl") && + !of_device_is_compatible(np, "ibm,ppc4xx-spi")) return 0; return of_gpio_named_count(np, "gpios"); } @@ -405,9 +409,10 @@ static struct gpio_desc *of_find_spi_cs_gpio(struct device *dev, if (!IS_ENABLED(CONFIG_SPI_MASTER)) return ERR_PTR(-ENOENT); - /* Allow this specifically for Freescale devices */ + /* Allow this specifically for Freescale and PPC devices */ if (!of_device_is_compatible(np, "fsl,spi") && - !of_device_is_compatible(np, "aeroflexgaisler,spictrl")) + !of_device_is_compatible(np, "aeroflexgaisler,spictrl") && + !of_device_is_compatible(np, "ibm,ppc4xx-spi")) return ERR_PTR(-ENOENT); /* Allow only if asking for "cs-gpios" */ if (!con_id || strcmp(con_id, "cs")) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 23e3d335cd54..728f6c687182 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -11,6 +11,7 @@ #include <linux/ctype.h> #include "gpiolib.h" +#include "gpiolib-sysfs.h" #define GPIO_IRQF_TRIGGER_FALLING BIT(0) #define GPIO_IRQF_TRIGGER_RISING BIT(1) @@ -365,7 +366,7 @@ static DEVICE_ATTR_RW(active_low); static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; umode_t mode = attr->mode; diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h new file mode 100644 index 000000000000..ddd0e503f8eb --- /dev/null +++ b/drivers/gpio/gpiolib-sysfs.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIOLIB_SYSFS_H +#define GPIOLIB_SYSFS_H + +#ifdef CONFIG_GPIO_SYSFS + +int gpiochip_sysfs_register(struct gpio_device *gdev); +void gpiochip_sysfs_unregister(struct gpio_device *gdev); + +#else + +static inline int gpiochip_sysfs_register(struct gpio_device *gdev) +{ + return 0; +} + +static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) +{ +} + +#endif /* CONFIG_GPIO_SYSFS */ + +#endif /* GPIOLIB_SYSFS_H */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4fa075d49fbc..80137c1b3cdc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -17,20 +17,16 @@ #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> #include <linux/pinctrl/consumer.h> -#include <linux/cdev.h> #include <linux/fs.h> -#include <linux/uaccess.h> #include <linux/compat.h> -#include <linux/anon_inodes.h> #include <linux/file.h> -#include <linux/kfifo.h> -#include <linux/poll.h> -#include <linux/timekeeping.h> #include <uapi/linux/gpio.h> #include "gpiolib.h" #include "gpiolib-of.h" #include "gpiolib-acpi.h" +#include "gpiolib-cdev.h" +#include "gpiolib-sysfs.h" #define CREATE_TRACE_POINTS #include <trace/events/gpio.h> @@ -425,1105 +421,6 @@ bool gpiochip_line_is_valid(const struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); -/* - * GPIO line handle management - */ - -/** - * struct linehandle_state - contains the state of a userspace handle - * @gdev: the GPIO device the handle pertains to - * @label: consumer label used to tag descriptors - * @descs: the GPIO descriptors held by this handle - * @numdescs: the number of descriptors held in the descs array - */ -struct linehandle_state { - struct gpio_device *gdev; - const char *label; - struct gpio_desc *descs[GPIOHANDLES_MAX]; - u32 numdescs; -}; - -#define GPIOHANDLE_REQUEST_VALID_FLAGS \ - (GPIOHANDLE_REQUEST_INPUT | \ - GPIOHANDLE_REQUEST_OUTPUT | \ - GPIOHANDLE_REQUEST_ACTIVE_LOW | \ - GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ - GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ - GPIOHANDLE_REQUEST_BIAS_DISABLE | \ - GPIOHANDLE_REQUEST_OPEN_DRAIN | \ - GPIOHANDLE_REQUEST_OPEN_SOURCE) - -static int linehandle_validate_flags(u32 flags) -{ - /* Return an error if an unknown flag is set */ - if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) - return -EINVAL; - - /* - * Do not allow both INPUT & OUTPUT flags to be set as they are - * contradictory. - */ - if ((flags & GPIOHANDLE_REQUEST_INPUT) && - (flags & GPIOHANDLE_REQUEST_OUTPUT)) - return -EINVAL; - - /* - * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If - * the hardware actually supports enabling both at the same time the - * electrical result would be disastrous. - */ - if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && - (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) - return -EINVAL; - - /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ - if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) && - ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) - return -EINVAL; - - /* Bias flags only allowed for input or output mode. */ - if (!((flags & GPIOHANDLE_REQUEST_INPUT) || - (flags & GPIOHANDLE_REQUEST_OUTPUT)) && - ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) - return -EINVAL; - - /* Only one bias flag can be set. */ - if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && - (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | - GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || - ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) - return -EINVAL; - - return 0; -} - -static long linehandle_set_config(struct linehandle_state *lh, - void __user *ip) -{ - struct gpiohandle_config gcnf; - struct gpio_desc *desc; - int i, ret; - u32 lflags; - unsigned long *flagsp; - - if (copy_from_user(&gcnf, ip, sizeof(gcnf))) - return -EFAULT; - - lflags = gcnf.flags; - ret = linehandle_validate_flags(lflags); - if (ret) - return ret; - - for (i = 0; i < lh->numdescs; i++) { - desc = lh->descs[i]; - flagsp = &desc->flags; - - assign_bit(FLAG_ACTIVE_LOW, flagsp, - lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); - - assign_bit(FLAG_OPEN_DRAIN, flagsp, - lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); - - assign_bit(FLAG_OPEN_SOURCE, flagsp, - lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); - - assign_bit(FLAG_PULL_UP, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); - - assign_bit(FLAG_PULL_DOWN, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); - - assign_bit(FLAG_BIAS_DISABLE, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); - - /* - * Lines have to be requested explicitly for input - * or output, else the line will be treated "as is". - */ - if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { - int val = !!gcnf.default_values[i]; - - ret = gpiod_direction_output(desc, val); - if (ret) - return ret; - } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { - ret = gpiod_direction_input(desc); - if (ret) - return ret; - } - - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_CONFIG, desc); - } - return 0; -} - -static long linehandle_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - struct linehandle_state *lh = filep->private_data; - void __user *ip = (void __user *)arg; - struct gpiohandle_data ghd; - DECLARE_BITMAP(vals, GPIOHANDLES_MAX); - int i; - - if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { - /* NOTE: It's ok to read values of output lines. */ - int ret = gpiod_get_array_value_complex(false, - true, - lh->numdescs, - lh->descs, - NULL, - vals); - if (ret) - return ret; - - memset(&ghd, 0, sizeof(ghd)); - for (i = 0; i < lh->numdescs; i++) - ghd.values[i] = test_bit(i, vals); - - if (copy_to_user(ip, &ghd, sizeof(ghd))) - return -EFAULT; - - return 0; - } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { - /* - * 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; - - /* Clamp all values to [0,1] */ - for (i = 0; i < lh->numdescs; i++) - __assign_bit(i, vals, ghd.values[i]); - - /* Reuse the array setting function */ - return gpiod_set_array_value_complex(false, - true, - lh->numdescs, - lh->descs, - NULL, - vals); - } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { - return linehandle_set_config(lh, ip); - } - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static int linehandle_release(struct inode *inode, struct file *filep) -{ - struct linehandle_state *lh = filep->private_data; - struct gpio_device *gdev = lh->gdev; - int i; - - for (i = 0; i < lh->numdescs; i++) - gpiod_free(lh->descs[i]); - kfree(lh->label); - kfree(lh); - put_device(&gdev->dev); - return 0; -} - -static const struct file_operations linehandle_fileops = { - .release = linehandle_release, - .owner = THIS_MODULE, - .llseek = noop_llseek, - .unlocked_ioctl = linehandle_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = linehandle_ioctl_compat, -#endif -}; - -static int linehandle_create(struct gpio_device *gdev, void __user *ip) -{ - struct gpiohandle_request handlereq; - struct linehandle_state *lh; - struct file *file; - int fd, i, count = 0, ret; - u32 lflags; - - if (copy_from_user(&handlereq, ip, sizeof(handlereq))) - return -EFAULT; - if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX)) - return -EINVAL; - - lflags = handlereq.flags; - - ret = linehandle_validate_flags(lflags); - if (ret) - return ret; - - lh = kzalloc(sizeof(*lh), GFP_KERNEL); - if (!lh) - return -ENOMEM; - lh->gdev = gdev; - get_device(&gdev->dev); - - /* Make sure this is terminated */ - handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0'; - if (strlen(handlereq.consumer_label)) { - lh->label = kstrdup(handlereq.consumer_label, - GFP_KERNEL); - if (!lh->label) { - ret = -ENOMEM; - goto out_free_lh; - } - } - - /* Request each GPIO */ - for (i = 0; i < handlereq.lines; i++) { - u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); - - if (IS_ERR(desc)) { - ret = PTR_ERR(desc); - goto out_free_descs; - } - - ret = gpiod_request(desc, lh->label); - if (ret) - goto out_free_descs; - lh->descs[i] = desc; - count = i + 1; - - if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) - set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) - set_bit(FLAG_OPEN_DRAIN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) - set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) - set_bit(FLAG_BIAS_DISABLE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) - set_bit(FLAG_PULL_DOWN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) - set_bit(FLAG_PULL_UP, &desc->flags); - - ret = gpiod_set_transitory(desc, false); - if (ret < 0) - goto out_free_descs; - - /* - * Lines have to be requested explicitly for input - * or output, else the line will be treated "as is". - */ - if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { - int val = !!handlereq.default_values[i]; - - ret = gpiod_direction_output(desc, val); - if (ret) - goto out_free_descs; - } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { - ret = gpiod_direction_input(desc); - if (ret) - goto out_free_descs; - } - - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); - - dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", - offset); - } - /* Let i point at the last handle */ - i--; - lh->numdescs = handlereq.lines; - - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto out_free_descs; - } - - file = anon_inode_getfile("gpio-linehandle", - &linehandle_fileops, - lh, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - handlereq.fd = fd; - if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); - return -EFAULT; - } - - fd_install(fd, file); - - dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", - lh->numdescs); - - return 0; - -out_put_unused_fd: - put_unused_fd(fd); -out_free_descs: - for (i = 0; i < count; i++) - gpiod_free(lh->descs[i]); - kfree(lh->label); -out_free_lh: - kfree(lh); - put_device(&gdev->dev); - return ret; -} - -/* - * GPIO line event management - */ - -/** - * struct lineevent_state - contains the state of a userspace event - * @gdev: the GPIO device the event pertains to - * @label: consumer label used to tag descriptors - * @desc: the GPIO descriptor held by this event - * @eflags: the event flags this line was requested with - * @irq: the interrupt that trigger in response to events on this GPIO - * @wait: wait queue that handles blocking reads of events - * @events: KFIFO for the GPIO events - * @timestamp: cache for the timestamp storing it between hardirq - * and IRQ thread, used to bring the timestamp close to the actual - * event - */ -struct lineevent_state { - struct gpio_device *gdev; - const char *label; - struct gpio_desc *desc; - u32 eflags; - int irq; - wait_queue_head_t wait; - DECLARE_KFIFO(events, struct gpioevent_data, 16); - u64 timestamp; -}; - -#define GPIOEVENT_REQUEST_VALID_FLAGS \ - (GPIOEVENT_REQUEST_RISING_EDGE | \ - GPIOEVENT_REQUEST_FALLING_EDGE) - -static __poll_t lineevent_poll(struct file *filep, - struct poll_table_struct *wait) -{ - struct lineevent_state *le = filep->private_data; - __poll_t events = 0; - - poll_wait(filep, &le->wait, wait); - - if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock)) - events = EPOLLIN | EPOLLRDNORM; - - return events; -} - - -static ssize_t lineevent_read(struct file *filep, - char __user *buf, - size_t count, - loff_t *f_ps) -{ - struct lineevent_state *le = filep->private_data; - struct gpioevent_data ge; - ssize_t bytes_read = 0; - int ret; - - if (count < sizeof(ge)) - return -EINVAL; - - do { - spin_lock(&le->wait.lock); - if (kfifo_is_empty(&le->events)) { - if (bytes_read) { - spin_unlock(&le->wait.lock); - return bytes_read; - } - - if (filep->f_flags & O_NONBLOCK) { - spin_unlock(&le->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(le->wait, - !kfifo_is_empty(&le->events)); - if (ret) { - spin_unlock(&le->wait.lock); - return ret; - } - } - - ret = kfifo_out(&le->events, &ge, 1); - spin_unlock(&le->wait.lock); - if (ret != 1) { - /* - * This should never happen - we were holding the lock - * from the moment we learned the fifo is no longer - * empty until now. - */ - ret = -EIO; - break; - } - - if (copy_to_user(buf + bytes_read, &ge, sizeof(ge))) - return -EFAULT; - bytes_read += sizeof(ge); - } while (count >= bytes_read + sizeof(ge)); - - return bytes_read; -} - -static int lineevent_release(struct inode *inode, struct file *filep) -{ - struct lineevent_state *le = filep->private_data; - struct gpio_device *gdev = le->gdev; - - free_irq(le->irq, le); - gpiod_free(le->desc); - kfree(le->label); - kfree(le); - put_device(&gdev->dev); - return 0; -} - -static long lineevent_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - struct lineevent_state *le = filep->private_data; - void __user *ip = (void __user *)arg; - struct gpiohandle_data ghd; - - /* - * We can get the value for an event line but not set it, - * because it is input by definition. - */ - if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { - int val; - - memset(&ghd, 0, sizeof(ghd)); - - val = gpiod_get_value_cansleep(le->desc); - if (val < 0) - return val; - ghd.values[0] = val; - - if (copy_to_user(ip, &ghd, sizeof(ghd))) - return -EFAULT; - - return 0; - } - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static const struct file_operations lineevent_fileops = { - .release = lineevent_release, - .read = lineevent_read, - .poll = lineevent_poll, - .owner = THIS_MODULE, - .llseek = noop_llseek, - .unlocked_ioctl = lineevent_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = lineevent_ioctl_compat, -#endif -}; - -static irqreturn_t lineevent_irq_thread(int irq, void *p) -{ - struct lineevent_state *le = p; - struct gpioevent_data ge; - int ret; - - /* Do not leak kernel stack to userspace */ - memset(&ge, 0, sizeof(ge)); - - /* - * We may be running from a nested threaded interrupt in which case - * we didn't get the timestamp from lineevent_irq_handler(). - */ - if (!le->timestamp) - ge.timestamp = ktime_get_ns(); - else - ge.timestamp = le->timestamp; - - if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE - && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { - int level = gpiod_get_value_cansleep(le->desc); - if (level) - /* Emit low-to-high event */ - ge.id = GPIOEVENT_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - ge.id = GPIOEVENT_EVENT_FALLING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { - /* Emit low-to-high event */ - ge.id = GPIOEVENT_EVENT_RISING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { - /* Emit high-to-low event */ - ge.id = GPIOEVENT_EVENT_FALLING_EDGE; - } else { - return IRQ_NONE; - } - - ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge, - 1, &le->wait.lock); - if (ret) - wake_up_poll(&le->wait, EPOLLIN); - else - pr_debug_ratelimited("event FIFO is full - event dropped\n"); - - return IRQ_HANDLED; -} - -static irqreturn_t lineevent_irq_handler(int irq, void *p) -{ - struct lineevent_state *le = p; - - /* - * Just store the timestamp in hardirq context so we get it as - * close in time as possible to the actual event. - */ - le->timestamp = ktime_get_ns(); - - return IRQ_WAKE_THREAD; -} - -static int lineevent_create(struct gpio_device *gdev, void __user *ip) -{ - struct gpioevent_request eventreq; - struct lineevent_state *le; - struct gpio_desc *desc; - struct file *file; - u32 offset; - u32 lflags; - u32 eflags; - int fd; - int ret; - int irqflags = 0; - - if (copy_from_user(&eventreq, ip, sizeof(eventreq))) - return -EFAULT; - - offset = eventreq.lineoffset; - lflags = eventreq.handleflags; - eflags = eventreq.eventflags; - - desc = gpiochip_get_desc(gdev->chip, offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - /* Return an error if a unknown flag is set */ - if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || - (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) - return -EINVAL; - - /* This is just wrong: we don't look for events on output lines */ - if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || - (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) - return -EINVAL; - - /* Only one bias flag can be set. */ - if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && - (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | - GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || - ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && - (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) - return -EINVAL; - - le = kzalloc(sizeof(*le), GFP_KERNEL); - if (!le) - return -ENOMEM; - le->gdev = gdev; - get_device(&gdev->dev); - - /* Make sure this is terminated */ - eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0'; - if (strlen(eventreq.consumer_label)) { - le->label = kstrdup(eventreq.consumer_label, - GFP_KERNEL); - if (!le->label) { - ret = -ENOMEM; - goto out_free_le; - } - } - - ret = gpiod_request(desc, le->label); - if (ret) - goto out_free_label; - le->desc = desc; - le->eflags = eflags; - - if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) - set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) - set_bit(FLAG_BIAS_DISABLE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) - set_bit(FLAG_PULL_DOWN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) - set_bit(FLAG_PULL_UP, &desc->flags); - - ret = gpiod_direction_input(desc); - if (ret) - goto out_free_desc; - - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); - - le->irq = gpiod_to_irq(desc); - if (le->irq <= 0) { - ret = -ENODEV; - goto out_free_desc; - } - - if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) - irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; - if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE) - irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; - irqflags |= IRQF_ONESHOT; - - INIT_KFIFO(le->events); - init_waitqueue_head(&le->wait); - - /* Request a thread to read the events */ - ret = request_threaded_irq(le->irq, - lineevent_irq_handler, - lineevent_irq_thread, - irqflags, - le->label, - le); - if (ret) - goto out_free_desc; - - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto out_free_irq; - } - - file = anon_inode_getfile("gpio-event", - &lineevent_fileops, - le, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - eventreq.fd = fd; - if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); - return -EFAULT; - } - - fd_install(fd, file); - - return 0; - -out_put_unused_fd: - put_unused_fd(fd); -out_free_irq: - free_irq(le->irq, le); -out_free_desc: - gpiod_free(le->desc); -out_free_label: - kfree(le->label); -out_free_le: - kfree(le); - put_device(&gdev->dev); - return ret; -} - -static void gpio_desc_to_lineinfo(struct gpio_desc *desc, - struct gpioline_info *info) -{ - struct gpio_chip *gc = desc->gdev->chip; - bool ok_for_pinctrl; - unsigned long flags; - - /* - * This function takes a mutex so we must check this before taking - * the spinlock. - * - * FIXME: find a non-racy way to retrieve this information. Maybe a - * lock common to both frameworks? - */ - ok_for_pinctrl = - pinctrl_gpio_can_use_line(gc->base + info->line_offset); - - spin_lock_irqsave(&gpio_lock, flags); - - if (desc->name) { - strncpy(info->name, desc->name, sizeof(info->name)); - info->name[sizeof(info->name) - 1] = '\0'; - } else { - info->name[0] = '\0'; - } - - if (desc->label) { - strncpy(info->consumer, desc->label, sizeof(info->consumer)); - info->consumer[sizeof(info->consumer) - 1] = '\0'; - } else { - info->consumer[0] = '\0'; - } - - /* - * Userspace only need to know that the kernel is using this GPIO so - * it can't use it. - */ - info->flags = 0; - if (test_bit(FLAG_REQUESTED, &desc->flags) || - test_bit(FLAG_IS_HOGGED, &desc->flags) || - test_bit(FLAG_USED_AS_IRQ, &desc->flags) || - test_bit(FLAG_EXPORT, &desc->flags) || - test_bit(FLAG_SYSFS, &desc->flags) || - !ok_for_pinctrl) - info->flags |= GPIOLINE_FLAG_KERNEL; - if (test_bit(FLAG_IS_OUT, &desc->flags)) - info->flags |= GPIOLINE_FLAG_IS_OUT; - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) - info->flags |= GPIOLINE_FLAG_ACTIVE_LOW; - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN | - GPIOLINE_FLAG_IS_OUT); - if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE | - GPIOLINE_FLAG_IS_OUT); - if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_DISABLE; - if (test_bit(FLAG_PULL_DOWN, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; - if (test_bit(FLAG_PULL_UP, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_PULL_UP; - - spin_unlock_irqrestore(&gpio_lock, flags); -} - -struct gpio_chardev_data { - struct gpio_device *gdev; - wait_queue_head_t wait; - DECLARE_KFIFO(events, struct gpioline_info_changed, 32); - struct notifier_block lineinfo_changed_nb; - unsigned long *watched_lines; -}; - -/* - * gpio_ioctl() - ioctl handler for the GPIO chardev - */ -static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct gpio_chardev_data *priv = filp->private_data; - struct gpio_device *gdev = priv->gdev; - struct gpio_chip *gc = gdev->chip; - void __user *ip = (void __user *)arg; - struct gpio_desc *desc; - __u32 offset; - int hwgpio; - - /* We fail any subsequent ioctl():s when the chip is gone */ - if (!gc) - return -ENODEV; - - /* Fill in the struct and pass to userspace */ - if (cmd == GPIO_GET_CHIPINFO_IOCTL) { - struct gpiochip_info chipinfo; - - memset(&chipinfo, 0, sizeof(chipinfo)); - - strncpy(chipinfo.name, dev_name(&gdev->dev), - sizeof(chipinfo.name)); - chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; - strncpy(chipinfo.label, gdev->label, - sizeof(chipinfo.label)); - chipinfo.label[sizeof(chipinfo.label)-1] = '\0'; - chipinfo.lines = gdev->ngpio; - if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) - return -EFAULT; - return 0; - } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { - struct gpioline_info lineinfo; - - if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) - return -EFAULT; - - desc = gpiochip_get_desc(gc, lineinfo.line_offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - hwgpio = gpio_chip_hwgpio(desc); - - gpio_desc_to_lineinfo(desc, &lineinfo); - - if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) - return -EFAULT; - return 0; - } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { - return linehandle_create(gdev, ip); - } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { - return lineevent_create(gdev, ip); - } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) { - struct gpioline_info lineinfo; - - if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) - return -EFAULT; - - desc = gpiochip_get_desc(gc, lineinfo.line_offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - hwgpio = gpio_chip_hwgpio(desc); - - if (test_bit(hwgpio, priv->watched_lines)) - return -EBUSY; - - gpio_desc_to_lineinfo(desc, &lineinfo); - - if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) - return -EFAULT; - - set_bit(hwgpio, priv->watched_lines); - return 0; - } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { - if (copy_from_user(&offset, ip, sizeof(offset))) - return -EFAULT; - - desc = gpiochip_get_desc(gc, offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - hwgpio = gpio_chip_hwgpio(desc); - - if (!test_bit(hwgpio, priv->watched_lines)) - return -EBUSY; - - clear_bit(hwgpio, priv->watched_lines); - return 0; - } - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static long gpio_ioctl_compat(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static struct gpio_chardev_data * -to_gpio_chardev_data(struct notifier_block *nb) -{ - return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); -} - -static int lineinfo_changed_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct gpio_chardev_data *priv = to_gpio_chardev_data(nb); - struct gpioline_info_changed chg; - struct gpio_desc *desc = data; - int ret; - - if (!test_bit(gpio_chip_hwgpio(desc), priv->watched_lines)) - return NOTIFY_DONE; - - memset(&chg, 0, sizeof(chg)); - chg.info.line_offset = gpio_chip_hwgpio(desc); - chg.event_type = action; - chg.timestamp = ktime_get_ns(); - gpio_desc_to_lineinfo(desc, &chg.info); - - ret = kfifo_in_spinlocked(&priv->events, &chg, 1, &priv->wait.lock); - if (ret) - wake_up_poll(&priv->wait, EPOLLIN); - else - pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n"); - - return NOTIFY_OK; -} - -static __poll_t lineinfo_watch_poll(struct file *filep, - struct poll_table_struct *pollt) -{ - struct gpio_chardev_data *priv = filep->private_data; - __poll_t events = 0; - - poll_wait(filep, &priv->wait, pollt); - - if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events, - &priv->wait.lock)) - events = EPOLLIN | EPOLLRDNORM; - - return events; -} - -static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, - size_t count, loff_t *off) -{ - struct gpio_chardev_data *priv = filep->private_data; - struct gpioline_info_changed event; - ssize_t bytes_read = 0; - int ret; - - if (count < sizeof(event)) - return -EINVAL; - - do { - spin_lock(&priv->wait.lock); - if (kfifo_is_empty(&priv->events)) { - if (bytes_read) { - spin_unlock(&priv->wait.lock); - return bytes_read; - } - - if (filep->f_flags & O_NONBLOCK) { - spin_unlock(&priv->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(priv->wait, - !kfifo_is_empty(&priv->events)); - if (ret) { - spin_unlock(&priv->wait.lock); - return ret; - } - } - - ret = kfifo_out(&priv->events, &event, 1); - spin_unlock(&priv->wait.lock); - if (ret != 1) { - ret = -EIO; - break; - /* We should never get here. See lineevent_read(). */ - } - - if (copy_to_user(buf + bytes_read, &event, sizeof(event))) - return -EFAULT; - bytes_read += sizeof(event); - } while (count >= bytes_read + sizeof(event)); - - return bytes_read; -} - -/** - * gpio_chrdev_open() - open the chardev for ioctl operations - * @inode: inode for this chardev - * @filp: file struct for storing private data - * Returns 0 on success - */ -static int gpio_chrdev_open(struct inode *inode, struct file *filp) -{ - struct gpio_device *gdev = container_of(inode->i_cdev, - struct gpio_device, chrdev); - struct gpio_chardev_data *priv; - int ret = -ENOMEM; - - /* Fail on open if the backing gpiochip is gone */ - if (!gdev->chip) - return -ENODEV; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); - if (!priv->watched_lines) - goto out_free_priv; - - init_waitqueue_head(&priv->wait); - INIT_KFIFO(priv->events); - priv->gdev = gdev; - - priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; - ret = atomic_notifier_chain_register(&gdev->notifier, - &priv->lineinfo_changed_nb); - if (ret) - goto out_free_bitmap; - - get_device(&gdev->dev); - filp->private_data = priv; - - ret = nonseekable_open(inode, filp); - if (ret) - goto out_unregister_notifier; - - return ret; - -out_unregister_notifier: - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); -out_free_bitmap: - bitmap_free(priv->watched_lines); -out_free_priv: - kfree(priv); - return ret; -} - -/** - * gpio_chrdev_release() - close chardev after ioctl operations - * @inode: inode for this chardev - * @filp: file struct for storing private data - * Returns 0 on success - */ -static int gpio_chrdev_release(struct inode *inode, struct file *filp) -{ - struct gpio_chardev_data *priv = filp->private_data; - struct gpio_device *gdev = priv->gdev; - - bitmap_free(priv->watched_lines); - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); - put_device(&gdev->dev); - kfree(priv); - - return 0; -} - -static const struct file_operations gpio_fileops = { - .release = gpio_chrdev_release, - .open = gpio_chrdev_open, - .poll = lineinfo_watch_poll, - .read = lineinfo_watch_read, - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = gpio_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = gpio_ioctl_compat, -#endif -}; - static void gpiodevice_release(struct device *dev) { struct gpio_device *gdev = dev_get_drvdata(dev); @@ -1539,17 +436,10 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) { int ret; - cdev_init(&gdev->chrdev, &gpio_fileops); - gdev->chrdev.owner = THIS_MODULE; - gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); - - ret = cdev_device_add(&gdev->chrdev, &gdev->dev); + ret = gpiolib_cdev_register(gdev, gpio_devt); if (ret) return ret; - chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", - MAJOR(gpio_devt), gdev->id); - ret = gpiochip_sysfs_register(gdev); if (ret) goto err_remove_device; @@ -1562,7 +452,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) return 0; err_remove_device: - cdev_device_del(&gdev->chrdev, &gdev->dev); + gpiolib_cdev_unregister(gdev); return ret; } @@ -1725,7 +615,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, spin_unlock_irqrestore(&gpio_lock, flags); - ATOMIC_INIT_NOTIFIER_HEAD(&gdev->notifier); + BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); @@ -1884,7 +774,7 @@ void gpiochip_remove(struct gpio_chip *gc) * be removed, else it will be dangling until the last user is * gone. */ - cdev_device_del(&gdev->chrdev, &gdev->dev); + gpiolib_cdev_unregister(gdev); put_device(&gdev->dev); } EXPORT_SYMBOL_GPL(gpiochip_remove); @@ -3159,8 +2049,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) } spin_unlock_irqrestore(&gpio_lock, flags); - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_RELEASED, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_RELEASED, desc); return ret; } @@ -3705,10 +2595,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, bitmap_xor(value_bitmap, value_bitmap, array_info->invert_mask, array_size); - if (bitmap_full(array_info->get_mask, array_size)) - return 0; - i = find_first_zero_bit(array_info->get_mask, array_size); + if (i == array_size) + return 0; } else { array_info = NULL; } @@ -3989,10 +2878,9 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, gpio_chip_set_multiple(array_info->chip, array_info->set_mask, value_bitmap); - if (bitmap_full(array_info->set_mask, array_size)) - return 0; - i = find_first_zero_bit(array_info->set_mask, array_size); + if (i == array_size) + return 0; } else { array_info = NULL; } @@ -5039,8 +3927,8 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, return ERR_PTR(ret); } - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); return desc; } @@ -5107,8 +3995,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, return ERR_PTR(ret); } - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); return desc; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 9ed242316414..6709f79c02dd 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -56,7 +56,7 @@ struct gpio_device { const char *label; void *data; struct list_head list; - struct atomic_notifier_head notifier; + struct blocking_notifier_head notifier; #ifdef CONFIG_PINCTRL /* @@ -175,22 +175,4 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) #define chip_dbg(gc, fmt, ...) \ dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#ifdef CONFIG_GPIO_SYSFS - -int gpiochip_sysfs_register(struct gpio_device *gdev); -void gpiochip_sysfs_unregister(struct gpio_device *gdev); - -#else - -static inline int gpiochip_sysfs_register(struct gpio_device *gdev) -{ - return 0; -} - -static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) -{ -} - -#endif /* CONFIG_GPIO_SYSFS */ - #endif /* GPIOLIB_H */ |