diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-altera.c | 9 | ||||
-rw-r--r-- | drivers/gpio/gpio-mpc8xxx.c | 5 | ||||
-rw-r--r-- | drivers/gpio/gpio-regmap.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-sim.c | 7 | ||||
-rw-r--r-- | drivers/gpio/gpio-tegra186.c | 27 | ||||
-rw-r--r-- | drivers/gpio/gpio-tps65219.c | 12 | ||||
-rw-r--r-- | drivers/gpio/gpio-tqmx86.c | 206 | ||||
-rw-r--r-- | drivers/gpio/gpio-twl6040.c | 6 | ||||
-rw-r--r-- | drivers/gpio/gpio-zynq.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-cdev.c | 15 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 65 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.h | 5 |
14 files changed, 212 insertions, 154 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1a66ca075ade..98b4d1633b25 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -530,9 +530,9 @@ config GPIO_OCTEON family of SOCs. config GPIO_OMAP - tristate "TI OMAP GPIO support" if ARCH_OMAP2PLUS || COMPILE_TEST + tristate "TI OMAP GPIO support" + depends on ARCH_OMAP || COMPILE_TEST default y if ARCH_OMAP - depends on ARM select GENERIC_IRQ_CHIP select GPIOLIB_IRQCHIP help diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 73e660c5e38a..17ab039c7413 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -32,14 +32,12 @@ * will be blocked until the current one completes. * @interrupt_trigger : specifies the hardware configured IRQ trigger type * (rising, falling, both, high) -* @mapped_irq : kernel mapped irq number. */ struct altera_gpio_chip { struct gpio_chip gc; void __iomem *regs; raw_spinlock_t gpio_lock; int interrupt_trigger; - int mapped_irq; }; static void altera_gpio_irq_unmask(struct irq_data *d) @@ -235,6 +233,7 @@ static int altera_gpio_probe(struct platform_device *pdev) int reg, ret; struct altera_gpio_chip *altera_gc; struct gpio_irq_chip *girq; + int mapped_irq; altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); if (!altera_gc) @@ -271,8 +270,8 @@ static int altera_gpio_probe(struct platform_device *pdev) if (IS_ERR(altera_gc->regs)) return dev_err_probe(dev, PTR_ERR(altera_gc->regs), "failed to ioremap memory resource\n"); - altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); - if (altera_gc->mapped_irq < 0) + mapped_irq = platform_get_irq_optional(pdev, 0); + if (mapped_irq < 0) goto skip_irq; if (device_property_read_u32(dev, "altr,interrupt-type", ®)) { @@ -296,7 +295,7 @@ static int altera_gpio_probe(struct platform_device *pdev) return -ENOMEM; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; - girq->parents[0] = altera_gc->mapped_irq; + girq->parents[0] = mapped_irq; skip_irq: ret = devm_gpiochip_add_data(dev, &altera_gc->gc, altera_gc); diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 24417c3247b0..541517536489 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -285,6 +285,7 @@ static const struct mpc8xxx_gpio_devtype mpc8xxx_gpio_devtype_default = { }; static const struct of_device_id mpc8xxx_gpio_ids[] = { + { .compatible = "fsl,mpc8314-gpio", }, { .compatible = "fsl,mpc8349-gpio", }, { .compatible = "fsl,mpc8572-gpio", .data = &mpc8572_gpio_devtype, }, { .compatible = "fsl,mpc8610-gpio", }, @@ -409,7 +410,9 @@ static int mpc8xxx_probe(struct platform_device *pdev) goto err; } - device_init_wakeup(dev, true); + ret = devm_device_init_wakeup(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to init wakeup\n"); return 0; err: diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 71684dee2ca5..05f8781b5204 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -262,6 +262,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->label = config->label ?: dev_name(config->parent); chip->can_sleep = regmap_might_sleep(config->regmap); + chip->request = gpiochip_generic_request; + chip->free = gpiochip_generic_free; chip->get = gpio_regmap_get; if (gpio->reg_set_base && gpio->reg_clr_base) chip->set = gpio_regmap_set_with_clear; diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 940165235db6..b6c230fab840 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -413,11 +413,6 @@ static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip); } -static int gpio_sim_dev_match_fwnode(struct device *dev, void *data) -{ - return device_match_fwnode(dev, data); -} - static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) { struct gpio_sim_chip *chip; @@ -503,7 +498,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) if (ret) return ret; - chip->dev = device_find_child(dev, swnode, gpio_sim_dev_match_fwnode); + chip->dev = device_find_child(dev, swnode, device_match_fwnode); if (!chip->dev) return -ENODEV; diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 6895b65c86af..d27bfac6c9f5 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -823,6 +823,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) struct gpio_irq_chip *irq; struct tegra_gpio *gpio; struct device_node *np; + struct resource *res; char **names; int err; @@ -842,19 +843,19 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; /* get register apertures */ - gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); - if (IS_ERR(gpio->secure)) { - gpio->secure = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(gpio->secure)) - return PTR_ERR(gpio->secure); - } - - gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio"); - if (IS_ERR(gpio->base)) { - gpio->base = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(gpio->base)) - return PTR_ERR(gpio->base); - } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "security"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gpio->secure = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpio->secure)) + return PTR_ERR(gpio->secure); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpio"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + gpio->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); err = platform_irq_count(pdev); if (err < 0) diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index cd1f17041f8c..526640c39a11 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -15,8 +15,6 @@ #define TPS65219_GPIO0_DIR_MASK BIT(3) #define TPS65219_GPIO0_OFFSET 2 #define TPS65219_GPIO0_IDX 0 -#define TPS65219_GPIO_DIR_IN 1 -#define TPS65219_GPIO_DIR_OUT 0 struct tps65219_gpio { struct gpio_chip gpio_chip; @@ -61,7 +59,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) * status bit. */ - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) return -ENOTSUPP; return ret; @@ -124,10 +122,10 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs return -ENOTSUPP; } - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_IN) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN) return 0; - return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_IN); + return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_IN); } static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) @@ -136,10 +134,10 @@ static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int off if (offset != TPS65219_GPIO0_IDX) return 0; - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) return 0; - return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_OUT); + return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_OUT); } static const struct gpio_chip tps65219_template_chip = { diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 5e26eb3adabb..18f523a15b3c 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -29,18 +29,22 @@ #define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ #define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ -#define TQMX86_GPII_NONE 0 -#define TQMX86_GPII_FALLING BIT(0) -#define TQMX86_GPII_RISING BIT(1) -/* Stored in irq_type as a trigger type, but not actually valid as a register - * value, so the name doesn't use "GPII" +/* + * NONE, FALLING and RISING use the same bit patterns that can be programmed to + * the GPII register (after passing them to the TQMX86_GPII_ macros to shift + * them to the right position) */ -#define TQMX86_INT_BOTH (BIT(0) | BIT(1)) -#define TQMX86_GPII_MASK (BIT(0) | BIT(1)) -#define TQMX86_GPII_BITS 2 +#define TQMX86_INT_TRIG_NONE 0 +#define TQMX86_INT_TRIG_FALLING BIT(0) +#define TQMX86_INT_TRIG_RISING BIT(1) +#define TQMX86_INT_TRIG_BOTH (BIT(0) | BIT(1)) +#define TQMX86_INT_TRIG_MASK (BIT(0) | BIT(1)) /* Stored in irq_type with GPII bits */ #define TQMX86_INT_UNMASKED BIT(2) +#define TQMX86_GPIIC_CONFIG(i, v) ((v) << (2 * (i))) +#define TQMX86_GPIIC_MASK(i) TQMX86_GPIIC_CONFIG(i, TQMX86_INT_TRIG_MASK) + struct tqmx86_gpio_data { struct gpio_chip chip; void __iomem *io_base; @@ -48,7 +52,7 @@ struct tqmx86_gpio_data { /* Lock must be held for accessing output and irq_type fields */ raw_spinlock_t spinlock; DECLARE_BITMAP(output, TQMX86_NGPIO); - u8 irq_type[TQMX86_NGPI]; + u8 irq_type[TQMX86_NGPIO]; }; static u8 tqmx86_gpio_read(struct tqmx86_gpio_data *gd, unsigned int reg) @@ -62,6 +66,18 @@ static void tqmx86_gpio_write(struct tqmx86_gpio_data *gd, u8 val, iowrite8(val, gd->io_base + reg); } +static void tqmx86_gpio_clrsetbits(struct tqmx86_gpio_data *gpio, + u8 clr, u8 set, unsigned int reg) + __must_hold(&gpio->spinlock) +{ + u8 val = tqmx86_gpio_read(gpio, reg); + + val &= ~clr; + val |= set; + + tqmx86_gpio_write(gpio, val, reg); +} + static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); @@ -69,127 +85,137 @@ static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(tqmx86_gpio_read(gpio, TQMX86_GPIOD) & BIT(offset)); } +static void _tqmx86_gpio_set(struct tqmx86_gpio_data *gpio, unsigned int offset, + int value) + __must_hold(&gpio->spinlock) +{ + __assign_bit(offset, gpio->output, value); + tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); +} + static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); - unsigned long flags; - raw_spin_lock_irqsave(&gpio->spinlock, flags); - __assign_bit(offset, gpio->output, value); - tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + _tqmx86_gpio_set(gpio, offset, value); } static int tqmx86_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { - /* Direction cannot be changed. Validate is an input. */ - if (BIT(offset) & TQMX86_DIR_INPUT_MASK) - return 0; - else - return -EINVAL; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + tqmx86_gpio_clrsetbits(gpio, BIT(offset), 0, TQMX86_GPIODD); + + return 0; } static int tqmx86_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { - /* Direction cannot be changed, validate is an output */ - if (BIT(offset) & TQMX86_DIR_INPUT_MASK) - return -EINVAL; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + _tqmx86_gpio_set(gpio, offset, value); + tqmx86_gpio_clrsetbits(gpio, 0, BIT(offset), TQMX86_GPIODD); - tqmx86_gpio_set(chip, offset, value); return 0; } static int tqmx86_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { - if (TQMX86_DIR_INPUT_MASK & BIT(offset)) - return GPIO_LINE_DIRECTION_IN; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + u8 val; + + val = tqmx86_gpio_read(gpio, TQMX86_GPIODD); + + if (val & BIT(offset)) + return GPIO_LINE_DIRECTION_OUT; - return GPIO_LINE_DIRECTION_OUT; + return GPIO_LINE_DIRECTION_IN; } -static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset) +static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int hwirq) __must_hold(&gpio->spinlock) { - u8 type = TQMX86_GPII_NONE, gpiic; + u8 type = TQMX86_INT_TRIG_NONE; + int gpiic_irq = hwirq - TQMX86_NGPO; - if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) { - type = gpio->irq_type[offset] & TQMX86_GPII_MASK; + if (gpio->irq_type[hwirq] & TQMX86_INT_UNMASKED) { + type = gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK; - if (type == TQMX86_INT_BOTH) - type = tqmx86_gpio_get(&gpio->chip, offset + TQMX86_NGPO) - ? TQMX86_GPII_FALLING - : TQMX86_GPII_RISING; + if (type == TQMX86_INT_TRIG_BOTH) + type = tqmx86_gpio_get(&gpio->chip, hwirq) + ? TQMX86_INT_TRIG_FALLING + : TQMX86_INT_TRIG_RISING; } - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS)); - gpiic |= type << (offset * TQMX86_GPII_BITS); - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + tqmx86_gpio_clrsetbits(gpio, + TQMX86_GPIIC_MASK(gpiic_irq), + TQMX86_GPIIC_CONFIG(gpiic_irq, type), + TQMX86_GPIIC); } static void tqmx86_gpio_irq_mask(struct irq_data *data) { - unsigned int offset = (data->hwirq - TQMX86_NGPO); struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned long flags; - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpio->irq_type[offset] &= ~TQMX86_INT_UNMASKED; - tqmx86_gpio_irq_config(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + scoped_guard(raw_spinlock_irqsave, &gpio->spinlock) { + gpio->irq_type[data->hwirq] &= ~TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, data->hwirq); + } gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(data)); } static void tqmx86_gpio_irq_unmask(struct irq_data *data) { - unsigned int offset = (data->hwirq - TQMX86_NGPO); struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned long flags; gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(data)); - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpio->irq_type[offset] |= TQMX86_INT_UNMASKED; - tqmx86_gpio_irq_config(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + gpio->irq_type[data->hwirq] |= TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, data->hwirq); } static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) { struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned int offset = (data->hwirq - TQMX86_NGPO); unsigned int edge_type = type & IRQF_TRIGGER_MASK; - unsigned long flags; u8 new_type; switch (edge_type) { case IRQ_TYPE_EDGE_RISING: - new_type = TQMX86_GPII_RISING; + new_type = TQMX86_INT_TRIG_RISING; break; case IRQ_TYPE_EDGE_FALLING: - new_type = TQMX86_GPII_FALLING; + new_type = TQMX86_INT_TRIG_FALLING; break; case IRQ_TYPE_EDGE_BOTH: - new_type = TQMX86_INT_BOTH; + new_type = TQMX86_INT_TRIG_BOTH; break; default: return -EINVAL; /* not supported */ } - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpio->irq_type[offset] &= ~TQMX86_GPII_MASK; - gpio->irq_type[offset] |= new_type; - tqmx86_gpio_irq_config(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + gpio->irq_type[data->hwirq] &= ~TQMX86_INT_TRIG_MASK; + gpio->irq_type[data->hwirq] |= new_type; + tqmx86_gpio_irq_config(gpio, data->hwirq); return 0; } @@ -199,8 +225,8 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) struct gpio_chip *chip = irq_desc_get_handler_data(desc); struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); struct irq_chip *irq_chip = irq_desc_get_chip(desc); - unsigned long irq_bits, flags; - int i; + unsigned long irq_bits; + int i, hwirq; u8 irq_status; chained_irq_enter(irq_chip, desc); @@ -210,32 +236,38 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) irq_bits = irq_status; - raw_spin_lock_irqsave(&gpio->spinlock, flags); - for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { - /* - * Edge-both triggers are implemented by flipping the edge - * trigger after each interrupt, as the controller only supports - * either rising or falling edge triggers, but not both. - * - * Internally, the TQMx86 GPIO controller has separate status - * registers for rising and falling edge interrupts. GPIIC - * configures which bits from which register are visible in the - * interrupt status register GPIIS and defines what triggers the - * parent IRQ line. Writing to GPIIS always clears both rising - * and falling interrupt flags internally, regardless of the - * currently configured trigger. - * - * In consequence, we can cleanly implement the edge-both - * trigger in software by first clearing the interrupt and then - * setting the new trigger based on the current GPIO input in - * tqmx86_gpio_irq_config() - even if an edge arrives between - * reading the input and setting the trigger, we will have a new - * interrupt pending. - */ - if ((gpio->irq_type[i] & TQMX86_GPII_MASK) == TQMX86_INT_BOTH) - tqmx86_gpio_irq_config(gpio, i); + scoped_guard(raw_spinlock_irqsave, &gpio->spinlock) { + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + hwirq = i + TQMX86_NGPO; + + /* + * Edge-both triggers are implemented by flipping the + * edge trigger after each interrupt, as the controller + * only supports either rising or falling edge triggers, + * but not both. + * + * Internally, the TQMx86 GPIO controller has separate + * status registers for rising and falling edge + * interrupts. GPIIC configures which bits from which + * register are visible in the interrupt status register + * GPIIS and defines what triggers the parent IRQ line. + * Writing to GPIIS always clears both rising and + * falling interrupt flags internally, regardless of the + * currently configured trigger. + * + * In consequence, we can cleanly implement the + * edge-both trigger in software by first clearing the + * interrupt and then setting the new trigger based on + * the current GPIO input in tqmx86_gpio_irq_config() - + * even if an edge arrives between reading the input and + * setting the trigger, we will have a new interrupt + * pending. + */ + if ((gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK) == + TQMX86_INT_TRIG_BOTH) + tqmx86_gpio_irq_config(gpio, hwirq); + } } - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); for_each_set_bit(i, &irq_bits, TQMX86_NGPI) generic_handle_domain_irq(gpio->chip.irq.domain, diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index 6c3fbf382dba..b9171bf66168 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -22,7 +22,7 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset) { - struct twl6040 *twl6040 = dev_get_drvdata(chip->parent->parent); + struct twl6040 *twl6040 = gpiochip_get_data(chip); int ret = 0; ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL); @@ -46,7 +46,7 @@ static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) { - struct twl6040 *twl6040 = dev_get_drvdata(chip->parent->parent); + struct twl6040 *twl6040 = gpiochip_get_data(chip); int ret; u8 gpoctl; @@ -91,7 +91,7 @@ static int gpo_twl6040_probe(struct platform_device *pdev) twl6040gpo_chip.parent = &pdev->dev; - ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, NULL); + ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, twl6040); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); twl6040gpo_chip.ngpio = 0; diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index be81fa2b17ab..3dae63f3ea21 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -1011,6 +1011,7 @@ static void zynq_gpio_remove(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) dev_warn(&pdev->dev, "pm_runtime_get_sync() Failed\n"); + device_init_wakeup(&pdev->dev, 0); gpiochip_remove(&gpio->chip); device_set_wakeup_capable(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 40f76a90fd7d..107d75558b5a 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2729,8 +2729,9 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) cdev->gdev = gpio_device_get(gdev); cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; - ret = atomic_notifier_chain_register(&gdev->line_state_notifier, - &cdev->lineinfo_changed_nb); + scoped_guard(write_lock_irqsave, &gdev->line_state_lock) + ret = raw_notifier_chain_register(&gdev->line_state_notifier, + &cdev->lineinfo_changed_nb); if (ret) goto out_free_bitmap; @@ -2754,8 +2755,9 @@ out_unregister_device_notifier: blocking_notifier_chain_unregister(&gdev->device_notifier, &cdev->device_unregistered_nb); out_unregister_line_notifier: - atomic_notifier_chain_unregister(&gdev->line_state_notifier, - &cdev->lineinfo_changed_nb); + scoped_guard(write_lock_irqsave, &gdev->line_state_lock) + raw_notifier_chain_unregister(&gdev->line_state_notifier, + &cdev->lineinfo_changed_nb); out_free_bitmap: gpio_device_put(gdev); bitmap_free(cdev->watched_lines); @@ -2779,8 +2781,9 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file) blocking_notifier_chain_unregister(&gdev->device_notifier, &cdev->device_unregistered_nb); - atomic_notifier_chain_unregister(&gdev->line_state_notifier, - &cdev->lineinfo_changed_nb); + scoped_guard(write_lock_irqsave, &gdev->line_state_lock) + raw_notifier_chain_unregister(&gdev->line_state_notifier, + &cdev->lineinfo_changed_nb); bitmap_free(cdev->watched_lines); gpio_device_put(gdev); kfree(cdev); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 2e537ee979f3..176e9142fd8f 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -193,6 +193,8 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np, */ { "himax,hx8357", "gpios-reset", false }, { "himax,hx8369", "gpios-reset", false }, +#endif +#if IS_ENABLED(CONFIG_MTD_NAND_JZ4780) /* * The rb-gpios semantics was undocumented and qi,lb60 (along with * the ingenic driver) got it wrong. The active state encodes the diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 19878bc75e94..0c00ed2ab431 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1025,7 +1025,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } } - ATOMIC_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); + rwlock_init(&gdev->line_state_lock); + RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); ret = init_srcu_struct(&gdev->srcu); @@ -1056,13 +1057,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, desc->gdev = gdev; - if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) { - assign_bit(FLAG_IS_OUT, - &desc->flags, !gc->get_direction(gc, desc_index)); - } else { + /* + * We would typically want to check the return value of + * get_direction() here but we must not check the return value + * and bail-out as pin controllers can have pins configured to + * alternate functions and return -EINVAL. Also: there's no + * need to take the SRCU lock here. + */ + if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) + assign_bit(FLAG_IS_OUT, &desc->flags, + !gc->get_direction(gc, desc_index)); + else assign_bit(FLAG_IS_OUT, &desc->flags, !gc->direction_input); - } } ret = of_gpiochip_add(gc); @@ -2701,7 +2708,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_input); int gpiod_direction_input_nonotify(struct gpio_desc *desc) { - int ret = 0; + int ret = 0, dir; CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) @@ -2728,13 +2735,18 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) if (guard.gc->direction_input) { ret = guard.gc->direction_input(guard.gc, gpio_chip_hwgpio(desc)); - } else if (guard.gc->get_direction && - (guard.gc->get_direction(guard.gc, - gpio_chip_hwgpio(desc)) != 1)) { - gpiod_warn(desc, - "%s: missing direction_input() operation and line is output\n", - __func__); - return -EIO; + } else if (guard.gc->get_direction) { + dir = guard.gc->get_direction(guard.gc, + gpio_chip_hwgpio(desc)); + if (dir < 0) + return dir; + + if (dir != GPIO_LINE_DIRECTION_IN) { + gpiod_warn(desc, + "%s: missing direction_input() operation and line is output\n", + __func__); + return -EIO; + } } if (ret == 0) { clear_bit(FLAG_IS_OUT, &desc->flags); @@ -2748,7 +2760,7 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { - int val = !!value, ret = 0; + int val = !!value, ret = 0, dir; CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) @@ -2771,12 +2783,18 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) gpio_chip_hwgpio(desc), val); } else { /* Check that we are in output mode if we can */ - if (guard.gc->get_direction && - guard.gc->get_direction(guard.gc, gpio_chip_hwgpio(desc))) { - gpiod_warn(desc, - "%s: missing direction_output() operation\n", - __func__); - return -EIO; + if (guard.gc->get_direction) { + dir = guard.gc->get_direction(guard.gc, + gpio_chip_hwgpio(desc)); + if (dir < 0) + return dir; + + if (dir != GPIO_LINE_DIRECTION_OUT) { + gpiod_warn(desc, + "%s: missing direction_output() operation\n", + __func__); + return -EIO; + } } /* * If we can't actively set the direction, we are some @@ -4171,8 +4189,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action) { - atomic_notifier_call_chain(&desc->gdev->line_state_notifier, - action, desc); + guard(read_lock_irqsave)(&desc->gdev->line_state_lock); + + raw_notifier_call_chain(&desc->gdev->line_state_notifier, action, desc); } /** diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 147156ec502b..c129a03e2040 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -16,6 +16,7 @@ #include <linux/gpio/driver.h> #include <linux/module.h> #include <linux/notifier.h> +#include <linux/spinlock.h> #include <linux/srcu.h> #include <linux/workqueue.h> @@ -45,6 +46,7 @@ * @list: links gpio_device:s together for traversal * @line_state_notifier: used to notify subscribers about lines being * requested, released or reconfigured + * @line_state_lock: RW-spinlock protecting the line state notifier * @line_state_wq: used to emit line state events from a separate thread in * process context * @device_notifier: used to notify character device wait queues about the GPIO @@ -72,7 +74,8 @@ struct gpio_device { const char *label; void *data; struct list_head list; - struct atomic_notifier_head line_state_notifier; + struct raw_notifier_head line_state_notifier; + rwlock_t line_state_lock; struct workqueue_struct *line_state_wq; struct blocking_notifier_head device_notifier; struct srcu_struct srcu; |