summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-24 16:21:27 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-24 16:21:27 +0200
commit8f9aa2c90530ab92301a82231ae44f3722becd93 (patch)
treefb282e955b0a880b07131a135257fe3ec764e928 /drivers/gpio
parent93467b31bec6da512b51544e5e4584f2745e995e (diff)
parent155b42bec9cbb6b8cdc47dd9bd09503a81fbe493 (diff)
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/gpio-davinci.c4
-rw-r--r--drivers/gpio/gpio-dwapb.c79
-rw-r--r--drivers/gpio/gpio-f7188x.c6
-rw-r--r--drivers/gpio/gpio-htc-egpio.c6
-rw-r--r--drivers/gpio/gpio-mlxbf3.c3
-rw-r--r--drivers/gpio/gpio-mt7621.c283
-rw-r--r--drivers/gpio/gpio-mvebu.c6
-rw-r--r--drivers/gpio/gpio-palmas.c19
-rw-r--r--drivers/gpio/gpio-shared-proxy.c136
-rw-r--r--drivers/gpio/gpio-tegra.c18
-rw-r--r--drivers/gpio/gpio-timberdale.c2
-rw-r--r--drivers/gpio/gpiolib-acpi-core.c19
-rw-r--r--drivers/gpio/gpiolib-shared.c9
-rw-r--r--drivers/gpio/gpiolib-shared.h31
-rw-r--r--drivers/gpio/gpiolib.c2
15 files changed, 404 insertions, 219 deletions
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index 97780f27ce5b..270cd7c88812 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -568,8 +568,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
sizeof(struct
davinci_gpio_irq_data),
GFP_KERNEL);
- if (!irqdata)
+ if (!irqdata) {
+ irq_domain_remove(chips->irq_domain);
return -ENOMEM;
+ }
irqdata->regs = g;
irqdata->bank_num = bank;
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index 15cebc8b5d66..0259c6597332 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -118,6 +118,7 @@ struct dwapb_gpio {
unsigned int flags;
struct reset_control *rst;
struct clk_bulk_data clks[DWAPB_NR_CLOCKS];
+ bool clocks_on_for_wake;
struct dwapb_gpio_port ports[] __counted_by(nr_ports);
};
@@ -365,11 +366,24 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
struct dwapb_context *ctx = gpio->ports[0].ctx;
irq_hw_number_t bit = irqd_to_hwirq(d);
+ u32 wake_en = ctx->wake_en;
if (enable)
- ctx->wake_en |= BIT(bit);
+ wake_en |= BIT(bit);
else
- ctx->wake_en &= ~BIT(bit);
+ wake_en &= ~BIT(bit);
+
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ if (d->parent_data && !!ctx->wake_en != !!wake_en) {
+ int err;
+
+ err = irq_chip_set_wake_parent(d, enable);
+ if (err)
+ return err;
+ }
+#endif
+
+ ctx->wake_en = wake_en;
return 0;
}
@@ -749,6 +763,8 @@ static int dwapb_gpio_suspend(struct device *dev)
int i;
scoped_guard(gpio_generic_lock_irqsave, gen_gc) {
+ gpio->clocks_on_for_wake = false;
+
for (i = 0; i < gpio->nr_ports; i++) {
unsigned int offset;
unsigned int idx = gpio->ports[i].idx;
@@ -770,11 +786,38 @@ static int dwapb_gpio_suspend(struct device *dev)
ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY);
ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int dwapb_gpio_suspend_noirq(struct device *dev)
+{
+ struct dwapb_gpio *gpio = dev_get_drvdata(dev);
+ struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip;
+ bool wake_enabled = false;
+ int i;
+
+ scoped_guard(gpio_generic_lock_irqsave, gen_gc) {
+ for (i = 0; i < gpio->nr_ports; i++) {
+ unsigned int idx = gpio->ports[i].idx;
+ struct dwapb_context *ctx = gpio->ports[i].ctx;
- /* Mask out interrupts */
+ if (idx == 0) {
+ wake_enabled = ctx->wake_en;
dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en);
+ break;
}
}
+
+ gpio->clocks_on_for_wake = wake_enabled;
+ }
+
+ if (wake_enabled) {
+ device_set_wakeup_path(dev);
+ return 0;
}
clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
@@ -782,18 +825,27 @@ static int dwapb_gpio_suspend(struct device *dev)
return 0;
}
-static int dwapb_gpio_resume(struct device *dev)
+static int dwapb_gpio_resume_noirq(struct device *dev)
{
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
- struct gpio_chip *gc = &gpio->ports[0].chip.gc;
- struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
- int i, err;
+ int err;
+
+ if (gpio->clocks_on_for_wake)
+ return 0;
err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks);
- if (err) {
+ if (err)
dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n");
- return err;
- }
+
+ return err;
+}
+
+static int dwapb_gpio_resume(struct device *dev)
+{
+ struct dwapb_gpio *gpio = dev_get_drvdata(dev);
+ struct gpio_chip *gc = &gpio->ports[0].chip.gc;
+ struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
+ int i;
guard(gpio_generic_lock_irqsave)(gen_gc);
@@ -827,8 +879,11 @@ static int dwapb_gpio_resume(struct device *dev)
return 0;
}
-static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops,
- dwapb_gpio_suspend, dwapb_gpio_resume);
+static const struct dev_pm_ops dwapb_gpio_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend, dwapb_gpio_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend_noirq,
+ dwapb_gpio_resume_noirq)
+};
static struct platform_driver dwapb_gpio_driver = {
.driver = {
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
index 4d5b927ad70f..fb007b978729 100644
--- a/drivers/gpio/gpio-f7188x.c
+++ b/drivers/gpio/gpio-f7188x.c
@@ -48,7 +48,8 @@
/*
* Nuvoton devices.
*/
-#define SIO_NCT6126D_ID 0xD283 /* NCT6126D chipset ID */
+#define SIO_NCT6126D_VER_A_ID 0xD283 /* NCT6126D version A chipset ID */
+#define SIO_NCT6126D_VER_B_ID 0xD284 /* NCT6126D version B chipset ID */
#define SIO_LD_GPIO_NUVOTON 0x07 /* GPIO logical device */
@@ -564,7 +565,8 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio)
case SIO_F81865_ID:
sio->type = f81865;
break;
- case SIO_NCT6126D_ID:
+ case SIO_NCT6126D_VER_A_ID:
+ case SIO_NCT6126D_VER_B_ID:
sio->device = SIO_LD_GPIO_NUVOTON;
sio->type = nct6126d;
break;
diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c
index d15423c718d0..25a4d4494f3c 100644
--- a/drivers/gpio/gpio-htc-egpio.c
+++ b/drivers/gpio/gpio-htc-egpio.c
@@ -268,6 +268,7 @@ static int __init egpio_probe(struct platform_device *pdev)
struct gpio_chip *chip;
unsigned int irq, irq_end;
int i;
+ int ret;
/* Initialize ei data structure. */
ei = devm_kzalloc(&pdev->dev, struct_size(ei, chip, pdata->num_chips), GFP_KERNEL);
@@ -326,7 +327,10 @@ static int __init egpio_probe(struct platform_device *pdev)
chip->base = pdata->chip[i].gpio_base;
chip->ngpio = pdata->chip[i].num_gpios;
- gpiochip_add_data(chip, &ei->chip[i]);
+ ret = devm_gpiochip_add_data(&pdev->dev, chip, &ei->chip[i]);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to register gpiochip %d\n", i);
}
/* Set initial pin values */
diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c
index 4770578269ba..566326644a2c 100644
--- a/drivers/gpio/gpio-mlxbf3.c
+++ b/drivers/gpio/gpio-mlxbf3.c
@@ -255,7 +255,8 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
ret = devm_gpiochip_add_data(dev, gc, gs);
if (ret)
- dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n");
+ return dev_err_probe(dev, ret,
+ "Failed adding memory mapped gpiochip\n");
return 0;
}
diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c
index 91230be51587..1b0b5247d3c9 100644
--- a/drivers/gpio/gpio-mt7621.c
+++ b/drivers/gpio/gpio-mt7621.c
@@ -29,8 +29,8 @@
#define GPIO_REG_EDGE 0xA0
struct mtk_gc {
- struct irq_chip irq_chip;
struct gpio_generic_chip chip;
+ struct mtk *parent_priv;
int bank;
u32 rising;
u32 falling;
@@ -41,20 +41,32 @@ struct mtk_gc {
/**
* struct mtk - state container for
* data of the platform driver. It is 3
- * separate gpio-chip each one with its
- * own irq_chip.
- * @dev: device instance
+ * separate gpio-chip having an IRQ
+ * linear domain shared for all of them
+ * @pdev: platform device instance
* @base: memory base address
+ * @irq_domain: IRQ linear domain shared across the three gpio chips
* @gpio_irq: irq number from the device tree
+ * @num_gpios: total number of gpio pins on the three gpio chips
* @gc_map: array of the gpio chips
*/
struct mtk {
- struct device *dev;
+ struct platform_device *pdev;
void __iomem *base;
+ struct irq_domain *irq_domain;
int gpio_irq;
+ int num_gpios;
struct mtk_gc gc_map[MTK_BANK_CNT];
};
+static inline struct mtk *
+mt7621_gpio_gc_to_priv(struct gpio_chip *gc)
+{
+ struct mtk_gc *bank = gpiochip_get_data(gc);
+
+ return bank->parent_priv;
+}
+
static inline struct mtk_gc *
to_mediatek_gpio(struct gpio_chip *chip)
{
@@ -67,7 +79,7 @@ static inline void
mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val)
{
struct gpio_chip *gc = &rg->chip.gc;
- struct mtk *mtk = gpiochip_get_data(gc);
+ struct mtk *mtk = mt7621_gpio_gc_to_priv(gc);
offset = (rg->bank * GPIO_BANK_STRIDE) + offset;
gpio_generic_write_reg(&rg->chip, mtk->base + offset, val);
@@ -77,41 +89,62 @@ static inline u32
mtk_gpio_r32(struct mtk_gc *rg, u32 offset)
{
struct gpio_chip *gc = &rg->chip.gc;
- struct mtk *mtk = gpiochip_get_data(gc);
+ struct mtk *mtk = mt7621_gpio_gc_to_priv(gc);
offset = (rg->bank * GPIO_BANK_STRIDE) + offset;
return gpio_generic_read_reg(&rg->chip, mtk->base + offset);
}
-static irqreturn_t
-mediatek_gpio_irq_handler(int irq, void *data)
+static void
+mt7621_gpio_irq_bank_handler(struct mtk_gc *bank)
{
- struct gpio_chip *gc = data;
- struct mtk_gc *rg = to_mediatek_gpio(gc);
- irqreturn_t ret = IRQ_NONE;
+ struct mtk *priv = bank->parent_priv;
+ struct irq_domain *domain = priv->irq_domain;
+ int hwbase = bank->chip.gc.offset;
unsigned long pending;
- int bit;
+ unsigned int offset;
+
+ pending = mtk_gpio_r32(bank, GPIO_REG_STAT);
+ if (!pending)
+ return;
+
+ mtk_gpio_w32(bank, GPIO_REG_STAT, pending);
+
+ for_each_set_bit(offset, &pending, MTK_BANK_WIDTH)
+ generic_handle_domain_irq(domain, hwbase + offset);
+}
+
+static void
+mt7621_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct mtk *priv = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ int i;
- pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
+ chained_irq_enter(chip, desc);
+ for (i = 0; i < MTK_BANK_CNT; i++) {
+ struct mtk_gc *bank = &priv->gc_map[i];
- for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
- generic_handle_domain_irq(gc->irq.domain, bit);
- mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
- ret |= IRQ_HANDLED;
+ mt7621_gpio_irq_bank_handler(bank);
}
+ chained_irq_exit(chip, desc);
+}
- return ret;
+static int
+mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank)
+{
+ return hwirq - bank->chip.gc.offset;
}
static void
mediatek_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct mtk_gc *rg = to_mediatek_gpio(gc);
- int pin = d->hwirq;
+ struct mtk_gc *rg = gpiochip_get_data(gc);
+ u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
u32 rise, fall, high, low;
- gpiochip_enable_irq(gc, d->hwirq);
+ gpiochip_enable_irq(gc, mask);
guard(gpio_generic_lock_irqsave)(&rg->chip);
@@ -119,18 +152,18 @@ mediatek_gpio_irq_unmask(struct irq_data *d)
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
low = mtk_gpio_r32(rg, GPIO_REG_LLVL);
- mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(pin) & rg->rising));
- mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling));
- mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel));
- mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel));
+ mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(mask) & rg->rising));
+ mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(mask) & rg->falling));
+ mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(mask) & rg->hlevel));
+ mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(mask) & rg->llevel));
}
static void
mediatek_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct mtk_gc *rg = to_mediatek_gpio(gc);
- int pin = d->hwirq;
+ struct mtk_gc *rg = gpiochip_get_data(gc);
+ u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
u32 rise, fall, high, low;
scoped_guard(gpio_generic_lock_irqsave, &rg->chip) {
@@ -138,22 +171,23 @@ mediatek_gpio_irq_mask(struct irq_data *d)
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
low = mtk_gpio_r32(rg, GPIO_REG_LLVL);
- mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin));
- mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin));
- mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin));
- mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin));
+ mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(mask));
+ mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(mask));
+ mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(mask));
+ mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(mask));
}
- gpiochip_disable_irq(gc, d->hwirq);
+ gpiochip_disable_irq(gc, mask);
}
static int
mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct mtk_gc *rg = to_mediatek_gpio(gc);
- int pin = d->hwirq;
- u32 mask = BIT(pin);
+ struct mtk_gc *rg = gpiochip_get_data(gc);
+ u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg));
+
+ guard(gpio_generic_lock_irqsave)(&rg->chip);
if (type == IRQ_TYPE_PROBE) {
if ((rg->rising | rg->falling |
@@ -191,6 +225,26 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
}
static int
+mt7621_gpio_irq_reqres(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mtk_gc *rg = gpiochip_get_data(gc);
+ unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
+
+ return gpiochip_reqres_irq(gc, irq);
+}
+
+static void
+mt7621_gpio_irq_relres(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mtk_gc *rg = gpiochip_get_data(gc);
+ unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
+
+ gpiochip_relres_irq(gc, irq);
+}
+
+static int
mediatek_gpio_xlate(struct gpio_chip *chip,
const struct of_phandle_args *spec, u32 *flags)
{
@@ -208,14 +262,123 @@ mediatek_gpio_xlate(struct gpio_chip *chip,
static const struct irq_chip mt7621_irq_chip = {
.name = "mt7621-gpio",
+ .irq_request_resources = mt7621_gpio_irq_reqres,
+ .irq_release_resources = mt7621_gpio_irq_relres,
.irq_mask_ack = mediatek_gpio_irq_mask,
.irq_mask = mediatek_gpio_irq_mask,
.irq_unmask = mediatek_gpio_irq_unmask,
.irq_set_type = mediatek_gpio_irq_type,
.flags = IRQCHIP_IMMUTABLE,
- GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
+static void
+mt7621_gpio_remove(void *data)
+{
+ struct mtk *priv = data;
+ int offset, virq;
+
+ if (priv->gpio_irq > 0)
+ irq_set_chained_handler_and_data(priv->gpio_irq, NULL, NULL);
+
+ /* Remove all IRQ mappings and delete the domain */
+ if (priv->irq_domain) {
+ for (offset = 0; offset < priv->num_gpios; offset++) {
+ virq = irq_find_mapping(priv->irq_domain, offset);
+ irq_dispose_mapping(virq);
+ }
+ irq_domain_remove(priv->irq_domain);
+ }
+}
+
+static struct mtk_gc *
+mt7621_gpio_hwirq_to_bank(struct mtk *priv, irq_hw_number_t hwirq)
+{
+ int i;
+
+ for (i = 0; i < MTK_BANK_CNT; i++) {
+ struct mtk_gc *bank = &priv->gc_map[i];
+
+ if (hwirq >= bank->chip.gc.offset &&
+ hwirq < (bank->chip.gc.offset + bank->chip.gc.ngpio))
+ return bank;
+ }
+
+ return NULL;
+}
+
+static int
+mt7621_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct mtk *priv = d->host_data;
+ struct mtk_gc *bank = mt7621_gpio_hwirq_to_bank(priv, hwirq);
+ struct platform_device *pdev = priv->pdev;
+ int ret;
+
+ if (!bank)
+ return -EINVAL;
+
+ dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n",
+ irq, (int)hwirq, bank->bank);
+
+ ret = irq_set_chip_data(irq, &bank->chip.gc);
+ if (ret < 0)
+ return ret;
+
+ irq_set_chip_and_handler(irq, &mt7621_irq_chip, handle_simple_irq);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static void
+mt7621_gpio_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops mt7621_gpio_irq_domain_ops = {
+ .map = mt7621_gpio_irq_map,
+ .unmap = mt7621_gpio_irq_unmap,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int
+mt7621_gpio_irq_setup(struct platform_device *pdev,
+ struct mtk *priv)
+{
+ struct device *dev = &pdev->dev;
+
+ priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev),
+ priv->num_gpios,
+ &mt7621_gpio_irq_domain_ops,
+ priv);
+ if (!priv->irq_domain) {
+ dev_err(dev, "Couldn't allocate IRQ domain\n");
+ return -ENXIO;
+ }
+
+ irq_set_chained_handler_and_data(priv->gpio_irq,
+ mt7621_gpio_irq_handler, priv);
+ irq_set_status_flags(priv->gpio_irq, IRQ_DISABLE_UNLAZY);
+
+ return 0;
+}
+
+static int
+mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+ struct mtk *priv = mt7621_gpio_gc_to_priv(gc);
+ /* gc_offset is relative to this gpio_chip; want real offset */
+ int hwirq = offset + gc->offset;
+
+ if (hwirq >= priv->num_gpios)
+ return -ENXIO;
+
+ return irq_create_mapping(priv->irq_domain, hwirq);
+}
+
static int
mediatek_gpio_bank_probe(struct device *dev, int bank)
{
@@ -228,6 +391,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
rg = &mtk->gc_map[bank];
memset(rg, 0, sizeof(*rg));
+ rg->parent_priv = mtk;
rg->bank = bank;
dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE);
@@ -253,41 +417,17 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
rg->chip.gc.of_gpio_n_cells = 2;
rg->chip.gc.of_xlate = mediatek_gpio_xlate;
+ rg->chip.gc.ngpio = MTK_BANK_WIDTH;
rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
dev_name(dev), bank);
if (!rg->chip.gc.label)
return -ENOMEM;
rg->chip.gc.offset = bank * MTK_BANK_WIDTH;
+ if (mtk->gpio_irq > 0)
+ rg->chip.gc.to_irq = mt7621_gpio_to_irq;
- if (mtk->gpio_irq) {
- struct gpio_irq_chip *girq;
-
- /*
- * Directly request the irq here instead of passing
- * a flow-handler because the irq is shared.
- */
- ret = devm_request_irq(dev, mtk->gpio_irq,
- mediatek_gpio_irq_handler, IRQF_SHARED,
- rg->chip.gc.label, &rg->chip.gc);
-
- if (ret) {
- dev_err(dev, "Error requesting IRQ %d: %d\n",
- mtk->gpio_irq, ret);
- return ret;
- }
-
- girq = &rg->chip.gc.irq;
- gpio_irq_chip_set_chip(girq, &mt7621_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;
- }
-
- ret = devm_gpiochip_add_data(dev, &rg->chip.gc, mtk);
+ ret = devm_gpiochip_add_data(dev, &rg->chip.gc, rg);
if (ret < 0) {
dev_err(dev, "Could not register gpio %d, ret=%d\n",
rg->chip.gc.ngpio, ret);
@@ -322,9 +462,20 @@ mediatek_gpio_probe(struct platform_device *pdev)
if (mtk->gpio_irq < 0)
return mtk->gpio_irq;
- mtk->dev = dev;
+ mtk->pdev = pdev;
+ mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT;
platform_set_drvdata(pdev, mtk);
+ if (mtk->gpio_irq > 0) {
+ ret = mt7621_gpio_irq_setup(pdev, mtk);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, mt7621_gpio_remove, mtk);
+ if (ret)
+ return ret;
+
for (i = 0; i < MTK_BANK_CNT; i++) {
ret = mediatek_gpio_bank_probe(dev, i);
if (ret)
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index c030d1f00abc..a010604e5ff7 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -1110,6 +1110,7 @@ static void mvebu_gpio_remove_irq_domain(void *data)
{
struct irq_domain *domain = data;
+ irq_domain_remove_generic_chips(domain);
irq_domain_remove(domain);
}
@@ -1221,7 +1222,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
BUG();
}
- devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
+ err = devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
+ if (err)
+ return dev_err_probe(&pdev->dev, err,
+ "failed to register gpiochip\n");
/* Some MVEBU SoCs have simple PWM support for GPIO lines */
if (IS_REACHABLE(CONFIG_PWM)) {
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index e377f6dd4ccf..e64ee0487718 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -116,6 +116,24 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
return ret;
}
+static int palmas_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ struct palmas_gpio *pg = gpiochip_get_data(gc);
+ struct palmas *palmas = pg->palmas;
+ unsigned int val;
+ unsigned int reg;
+ int ret;
+ int gpio16 = (offset/8);
+
+ offset %= 8;
+ reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
+ ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val);
+ if (ret)
+ return ret;
+
+ return (val & BIT(offset)) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct palmas_gpio *pg = gpiochip_get_data(gc);
@@ -165,6 +183,7 @@ static int palmas_gpio_probe(struct platform_device *pdev)
palmas_gpio->gpio_chip.can_sleep = true;
palmas_gpio->gpio_chip.direction_input = palmas_gpio_input;
palmas_gpio->gpio_chip.direction_output = palmas_gpio_output;
+ palmas_gpio->gpio_chip.get_direction = palmas_gpio_get_direction;
palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq;
palmas_gpio->gpio_chip.set = palmas_gpio_set;
palmas_gpio->gpio_chip.get = palmas_gpio_get;
diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c
index 6941e4be6cf1..52a366f0ec4d 100644
--- a/drivers/gpio/gpio-shared-proxy.c
+++ b/drivers/gpio/gpio-shared-proxy.c
@@ -9,8 +9,10 @@
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
+#include <linux/lockdep.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/string_choices.h>
#include <linux/types.h>
@@ -20,66 +22,66 @@ struct gpio_shared_proxy_data {
struct gpio_chip gc;
struct gpio_shared_desc *shared_desc;
struct device *dev;
- bool voted_high;
+ bool voted_change;
};
static int
-gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy,
- int (*set_func)(struct gpio_desc *desc, int value),
- int value)
+gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, int value)
{
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
struct gpio_desc *desc = shared_desc->desc;
int ret = 0;
- gpio_shared_lockdep_assert(shared_desc);
+ lockdep_assert_held(&shared_desc->mutex);
- if (value) {
- /* User wants to set value to high. */
- if (proxy->voted_high)
- /* Already voted for high, nothing to do. */
+ if (value != shared_desc->def_val) {
+ /* User wants to vote for a value change. */
+ if (proxy->voted_change)
+ /* Already voted for a change, nothing to do. */
goto out;
- /* Haven't voted for high yet. */
- if (!shared_desc->highcnt) {
+ /* Haven't voted for a value change yet. */
+ if (!shared_desc->votecnt) {
/*
- * Current value is low, need to actually set value
- * to high.
+ * Current value is default, need to actually set value
+ * to the opposite.
*/
- ret = set_func(desc, 1);
+ ret = gpiod_set_value_cansleep(desc, value);
if (ret)
goto out;
}
- shared_desc->highcnt++;
- proxy->voted_high = true;
+ shared_desc->votecnt++;
+ proxy->voted_change = true;
goto out;
}
- /* Desired value is low. */
- if (!proxy->voted_high)
- /* We didn't vote for high, nothing to do. */
+ /* Desired value is the default. */
+ if (!proxy->voted_change)
+ /* We didn't vote for change previously, nothing to do. */
goto out;
- /* We previously voted for high. */
- if (shared_desc->highcnt == 1) {
- /* This is the last remaining vote for high, set value to low. */
- ret = set_func(desc, 0);
+ /* We previously voted for change. */
+ if (shared_desc->votecnt == 1) {
+ /* This is the last remaining vote for change, set value to default. */
+ ret = gpiod_set_value_cansleep(desc, shared_desc->def_val);
if (ret)
goto out;
}
- shared_desc->highcnt--;
- proxy->voted_high = false;
+ shared_desc->votecnt--;
+ proxy->voted_change = false;
out:
- if (shared_desc->highcnt)
+ if (shared_desc->votecnt)
dev_dbg(proxy->dev,
- "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n",
- str_high_low(value), shared_desc->highcnt);
+ "Voted for value '%s', effective value is '%s', number of votes: %u\n",
+ str_high_low(value), str_high_low(!shared_desc->def_val),
+ shared_desc->votecnt);
else
- dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n");
+ dev_dbg(proxy->dev, "Voted for value '%s', effective value is '%s'\n",
+ str_high_low(value), str_high_low(shared_desc->def_val));
return ret;
}
@@ -89,7 +91,7 @@ static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int offset)
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
- guard(gpio_shared_desc_lock)(shared_desc);
+ guard(mutex)(&shared_desc->mutex);
proxy->shared_desc->usecnt++;
@@ -105,11 +107,10 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset)
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
int ret;
- guard(gpio_shared_desc_lock)(shared_desc);
+ guard(mutex)(&shared_desc->mutex);
- if (proxy->voted_high) {
- ret = gpio_shared_proxy_set_unlocked(proxy,
- shared_desc->can_sleep ? gpiod_set_value_cansleep : gpiod_set_value, 0);
+ if (proxy->voted_change) {
+ ret = gpio_shared_proxy_set_unlocked(proxy, shared_desc->def_val);
if (ret)
dev_err(proxy->dev,
"Failed to unset the shared GPIO value on release: %d\n", ret);
@@ -129,7 +130,7 @@ static int gpio_shared_proxy_set_config(struct gpio_chip *gc,
struct gpio_desc *desc = shared_desc->desc;
int ret;
- guard(gpio_shared_desc_lock)(shared_desc);
+ guard(mutex)(&shared_desc->mutex);
if (shared_desc->usecnt > 1) {
if (shared_desc->cfg != cfg) {
@@ -157,7 +158,7 @@ static int gpio_shared_proxy_direction_input(struct gpio_chip *gc,
struct gpio_desc *desc = shared_desc->desc;
int dir;
- guard(gpio_shared_desc_lock)(shared_desc);
+ guard(mutex)(&shared_desc->mutex);
if (shared_desc->usecnt == 1) {
dev_dbg(proxy->dev,
@@ -187,7 +188,7 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
struct gpio_desc *desc = shared_desc->desc;
int ret, dir;
- guard(gpio_shared_desc_lock)(shared_desc);
+ guard(mutex)(&shared_desc->mutex);
if (shared_desc->usecnt == 1) {
dev_dbg(proxy->dev,
@@ -198,13 +199,9 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
if (ret)
return ret;
- if (value) {
- proxy->voted_high = true;
- shared_desc->highcnt = 1;
- } else {
- proxy->voted_high = false;
- shared_desc->highcnt = 0;
- }
+ shared_desc->def_val = value;
+ shared_desc->votecnt = 0;
+ proxy->voted_change = false;
return 0;
}
@@ -219,14 +216,7 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
return -EPERM;
}
- return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value);
-}
-
-static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset)
-{
- struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
-
- return gpiod_get_value(proxy->shared_desc->desc);
+ return gpio_shared_proxy_set_unlocked(proxy, value);
}
static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc,
@@ -237,29 +227,14 @@ static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc,
return gpiod_get_value_cansleep(proxy->shared_desc->desc);
}
-static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy,
- int (*set_func)(struct gpio_desc *desc, int value),
- int value)
-{
- guard(gpio_shared_desc_lock)(proxy->shared_desc);
-
- return gpio_shared_proxy_set_unlocked(proxy, set_func, value);
-}
-
-static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset,
- int value)
-{
- struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
-
- return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value);
-}
-
static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc,
unsigned int offset, int value)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
- return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value);
+ guard(mutex)(&proxy->shared_desc->mutex);
+
+ return gpio_shared_proxy_set_unlocked(proxy, value);
}
static int gpio_shared_proxy_get_direction(struct gpio_chip *gc,
@@ -302,20 +277,25 @@ static int gpio_shared_proxy_probe(struct auxiliary_device *adev,
gc->label = dev_name(dev);
gc->parent = dev;
gc->owner = THIS_MODULE;
- gc->can_sleep = shared_desc->can_sleep;
+ /*
+ * Under the descriptor mutex the proxy may call
+ * gpiod_set_config()/gpiod_direction_*(), which can reach pinctrl
+ * paths that take a mutex (e.g. gpiod_set_config() ->
+ * gpiochip_generic_config() -> pinctrl_gpio_set_config()), independent
+ * of the underlying chip's can_sleep. So the descriptor lock must be a
+ * mutex and the proxy gpiochip is therefore always sleeping; drive the
+ * underlying GPIO through the cansleep value accessors, which are valid
+ * for both sleeping and non-sleeping chips.
+ */
+ gc->can_sleep = true;
gc->request = gpio_shared_proxy_request;
gc->free = gpio_shared_proxy_free;
gc->set_config = gpio_shared_proxy_set_config;
gc->direction_input = gpio_shared_proxy_direction_input;
gc->direction_output = gpio_shared_proxy_direction_output;
- if (gc->can_sleep) {
- gc->set = gpio_shared_proxy_set_cansleep;
- gc->get = gpio_shared_proxy_get_cansleep;
- } else {
- gc->set = gpio_shared_proxy_set;
- gc->get = gpio_shared_proxy_get;
- }
+ gc->set = gpio_shared_proxy_set_cansleep;
+ gc->get = gpio_shared_proxy_get_cansleep;
gc->get_direction = gpio_shared_proxy_get_direction;
gc->to_irq = gpio_shared_proxy_to_irq;
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index df06b56a2ade..fa6c8ee92093 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -172,18 +172,11 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
- int ret;
tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0);
tegra_gpio_enable(tgi, offset);
- ret = pinctrl_gpio_direction_input(chip, offset);
- if (ret < 0)
- dev_err(tgi->dev,
- "Failed to set pinctrl input direction of GPIO %d: %d",
- chip->base + offset, ret);
-
- return ret;
+ return 0;
}
static int tegra_gpio_direction_output(struct gpio_chip *chip,
@@ -191,19 +184,12 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip,
int value)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
- int ret;
tegra_gpio_set(chip, offset, value);
tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1);
tegra_gpio_enable(tgi, offset);
- ret = pinctrl_gpio_direction_output(chip, offset);
- if (ret < 0)
- dev_err(tgi->dev,
- "Failed to set pinctrl output direction of GPIO %d: %d",
- chip->base + offset, ret);
-
- return ret;
+ return 0;
}
static int tegra_gpio_get_direction(struct gpio_chip *chip,
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c
index f488939dd00a..b77c9d16fd16 100644
--- a/drivers/gpio/gpio-timberdale.c
+++ b/drivers/gpio/gpio-timberdale.c
@@ -235,7 +235,7 @@ static int timbgpio_probe(struct platform_device *pdev)
tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL);
if (!tgpio)
- return -EINVAL;
+ return -ENOMEM;
tgpio->irq_base = pdata->irq_base;
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index 09f860200a05..eb8a40cfb7a9 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -233,12 +233,23 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,
event->irq_requested = true;
- /* Make sure we trigger the initial state of edge-triggered IRQs */
+ /*
+ * Make sure we trigger the initial state of ActiveBoth IRQs.
+ *
+ * According to the Microsoft GPIO documentation, triggering GPIO
+ * interrupts marked as ActiveBoth during initialization is correct
+ * as long as the associated GPIO line is already "asserted"
+ * (logic level low). We should not trigger edge-based GPIO
+ * interrupts not marked as ActiveBoth.
+ *
+ * See: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio-
+ * Section: "GPIO controllers and ActiveBoth interrupts"
+ */
if (acpi_gpio_need_run_edge_events_on_boot() &&
- (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) {
+ ((event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) ==
+ (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) {
value = gpiod_get_raw_value_cansleep(event->desc);
- if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) ||
- ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0))
+ if (value == 0)
event->handler(event->irq, event);
}
}
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index de72776fb154..495bd3d0ddf0 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -627,8 +627,7 @@ static void gpio_shared_release(struct kref *kref)
shared_desc = entry->shared_desc;
gpio_device_put(shared_desc->desc->gdev);
- if (shared_desc->can_sleep)
- mutex_destroy(&shared_desc->mutex);
+ mutex_destroy(&shared_desc->mutex);
kfree(shared_desc);
entry->shared_desc = NULL;
}
@@ -659,11 +658,7 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry)
}
shared_desc->desc = &gdev->descs[entry->offset];
- shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
- if (shared_desc->can_sleep)
- mutex_init(&shared_desc->mutex);
- else
- spin_lock_init(&shared_desc->spinlock);
+ mutex_init(&shared_desc->mutex);
return shared_desc;
}
diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h
index 15e72a8dcdb1..618756f6c6aa 100644
--- a/drivers/gpio/gpiolib-shared.h
+++ b/drivers/gpio/gpiolib-shared.h
@@ -3,10 +3,7 @@
#ifndef __LINUX_GPIO_SHARED_H
#define __LINUX_GPIO_SHARED_H
-#include <linux/cleanup.h>
-#include <linux/lockdep.h>
#include <linux/mutex.h>
-#include <linux/spinlock.h>
struct gpio_device;
struct gpio_desc;
@@ -42,35 +39,13 @@ static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
struct gpio_shared_desc {
struct gpio_desc *desc;
- bool can_sleep;
unsigned long cfg;
unsigned int usecnt;
- unsigned int highcnt;
- union {
- struct mutex mutex;
- spinlock_t spinlock;
- };
+ unsigned int votecnt;
+ int def_val;
+ struct mutex mutex; /* serializes all proxy operations on this descriptor */
};
struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev);
-DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc,
- if (_T->lock->can_sleep)
- mutex_lock(&_T->lock->mutex);
- else
- spin_lock_irqsave(&_T->lock->spinlock, _T->flags),
- if (_T->lock->can_sleep)
- mutex_unlock(&_T->lock->mutex);
- else
- spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags),
- unsigned long flags)
-
-static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc)
-{
- if (shared_desc->can_sleep)
- lockdep_assert_held(&shared_desc->mutex);
- else
- lockdep_assert_held(&shared_desc->spinlock);
-}
-
#endif /* __LINUX_GPIO_SHARED_H */
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c1f9c0d367d5..58479931a690 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -3786,7 +3786,7 @@ static int gpiochip_set_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
unsigned int i;
- int ret;
+ int ret = 0;
lockdep_assert_held(&gc->gpiodev->srcu);