summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/gpio-davinci.c4
-rw-r--r--drivers/gpio/gpio-dwapb.c112
-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-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.c2
12 files changed, 348 insertions, 132 deletions
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index 538f27209ce7..5de42f1143da 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -550,8 +550,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 b42ff46d292b..0259c6597332 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -75,11 +75,10 @@ struct dwapb_port_property {
};
struct dwapb_platform_data {
- struct dwapb_port_property *properties;
unsigned int nports;
+ struct dwapb_port_property properties[] __counted_by(nports);
};
-#ifdef CONFIG_PM_SLEEP
/* Store GPIO context across system-wide suspend/resume transitions */
struct dwapb_context {
u32 data;
@@ -92,7 +91,6 @@ struct dwapb_context {
u32 int_deb;
u32 wake_en;
};
-#endif
struct dwapb_gpio_port_irqchip {
unsigned int nr_irqs;
@@ -103,9 +101,7 @@ struct dwapb_gpio_port {
struct gpio_generic_chip chip;
struct dwapb_gpio_port_irqchip *pirq;
struct dwapb_gpio *gpio;
-#ifdef CONFIG_PM_SLEEP
struct dwapb_context *ctx;
-#endif
unsigned int idx;
};
@@ -118,11 +114,12 @@ static inline struct dwapb_gpio *to_dwapb_gpio(struct gpio_chip *gc)
struct dwapb_gpio {
struct device *dev;
void __iomem *regs;
- struct dwapb_gpio_port *ports;
unsigned int nr_ports;
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);
};
static inline u32 gpio_reg_v2_convert(unsigned int offset)
@@ -363,24 +360,33 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
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;
}
-#else
-#define dwapb_irq_set_wake NULL
-#endif
static const struct irq_chip dwapb_irq_chip = {
.name = DWAPB_DRIVER_NAME,
@@ -390,7 +396,7 @@ static const struct irq_chip dwapb_irq_chip = {
.irq_set_type = dwapb_irq_set_type,
.irq_enable = dwapb_irq_enable,
.irq_disable = dwapb_irq_disable,
- .irq_set_wake = dwapb_irq_set_wake,
+ .irq_set_wake = pm_sleep_ptr(dwapb_irq_set_wake),
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
@@ -593,14 +599,10 @@ static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev)
if (nports == 0)
return ERR_PTR(-ENODEV);
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, struct_size(pdata, properties, nports), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
- pdata->properties = devm_kcalloc(dev, nports, sizeof(*pp), GFP_KERNEL);
- if (!pdata->properties)
- return ERR_PTR(-ENOMEM);
-
pdata->nports = nports;
i = 0;
@@ -722,22 +724,17 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
if (IS_ERR(pdata))
return PTR_ERR(pdata);
- gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ gpio = devm_kzalloc(&pdev->dev, struct_size(gpio, ports, pdata->nports), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
- gpio->dev = &pdev->dev;
gpio->nr_ports = pdata->nports;
+ gpio->dev = &pdev->dev;
err = dwapb_get_reset(gpio);
if (err)
return err;
- gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
- sizeof(*gpio->ports), GFP_KERNEL);
- if (!gpio->ports)
- return -ENOMEM;
-
gpio->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(gpio->regs))
return PTR_ERR(gpio->regs);
@@ -759,7 +756,6 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int dwapb_gpio_suspend(struct device *dev)
{
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
@@ -767,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;
@@ -788,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);
+ }
+ }
+ }
- /* Mask out interrupts */
+ 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;
+
+ 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);
@@ -800,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);
@@ -844,15 +878,17 @@ static int dwapb_gpio_resume(struct device *dev)
return 0;
}
-#endif
-static 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 = {
.name = DWAPB_DRIVER_NAME,
- .pm = &dwapb_gpio_pm_ops,
+ .pm = pm_sleep_ptr(&dwapb_gpio_pm_ops),
.of_match_table = dwapb_of_match,
.acpi_match_table = dwapb_acpi_match,
},
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 2eaed83214d8..8c583d6a7c96 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, sizeof(*ei), GFP_KERNEL);
@@ -331,7 +332,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 a7018e8ed88b..39a7ad6585f2 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -1111,6 +1111,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);
}
@@ -1222,7 +1223,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-tegra.c b/drivers/gpio/gpio-tegra.c
index b14052fe64ac..a5994f2ee431 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 8110690ea69d..8851e4866975 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.c b/drivers/gpio/gpiolib.c
index 67f362d53d53..9d3c33e1c9f9 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -3657,7 +3657,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);