aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/gpio-amd8111.c4
-rw-r--r--drivers/gpio/gpio-arizona.c5
-rw-r--r--drivers/gpio/gpio-aspeed.c5
-rw-r--r--drivers/gpio/gpio-da9052.c9
-rw-r--r--drivers/gpio/gpio-mockup.c1
-rw-r--r--drivers/gpio/gpio-mxc.c92
-rw-r--r--drivers/gpio/gpio-pca953x.c3
-rw-r--r--drivers/gpio/gpio-pl061.c15
-rw-r--r--drivers/gpio/gpio-rockchip.c1
-rw-r--r--drivers/gpio/gpio-tegra.c60
-rw-r--r--drivers/gpio/gpio-tegra186.c3
-rw-r--r--drivers/gpio/gpio-wm8350.c7
-rw-r--r--drivers/gpio/gpiolib-acpi.h12
-rw-r--r--drivers/gpio/gpiolib-cdev.c4
-rw-r--r--drivers/gpio/gpiolib-of.h11
-rw-r--r--drivers/gpio/gpiolib-sysfs.h2
-rw-r--r--drivers/gpio/gpiolib.c42
17 files changed, 218 insertions, 58 deletions
diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c
index 14e6b3e64add..6f3ded619c8b 100644
--- a/drivers/gpio/gpio-amd8111.c
+++ b/drivers/gpio/gpio-amd8111.c
@@ -226,7 +226,10 @@ found:
ioport_unmap(gp.pm);
goto out;
}
+ return 0;
+
out:
+ pci_dev_put(pdev);
return err;
}
@@ -234,6 +237,7 @@ static void __exit amd_gpio_exit(void)
{
gpiochip_remove(&gp.chip);
ioport_unmap(gp.pm);
+ pci_dev_put(gp.pdev);
}
module_init(amd_gpio_init);
diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c
index 02f9ae19cd44..c15fda99120a 100644
--- a/drivers/gpio/gpio-arizona.c
+++ b/drivers/gpio/gpio-arizona.c
@@ -7,13 +7,12 @@
* Author: Mark Brown <[email protected]>
*/
+#include <linux/gpio/driver.h>
#include <linux/kernel.h>
-#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <linux/mfd/arizona/core.h>
#include <linux/mfd/arizona/pdata.h>
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 318a7d95a1a8..a94da80d3a95 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -5,10 +5,9 @@
* Joel Stanley <[email protected]>
*/
-#include <asm/div64.h>
#include <linux/clk.h>
-#include <linux/gpio/driver.h>
#include <linux/gpio/aspeed.h>
+#include <linux/gpio/driver.h>
#include <linux/hashtable.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -19,6 +18,8 @@
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <asm/div64.h>
+
/*
* These two headers aren't meant to be used by GPIO drivers. We need
* them in order to access gpio_chip_hwgpio() which we need to implement
diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c
index 559188d80c2b..6f3905f1b8f5 100644
--- a/drivers/gpio/gpio-da9052.c
+++ b/drivers/gpio/gpio-da9052.c
@@ -6,17 +6,16 @@
*
* Author: David Dajun Chen <[email protected]>
*/
-#include <linux/module.h>
#include <linux/fs.h>
-#include <linux/uaccess.h>
-#include <linux/platform_device.h>
#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/syscalls.h>
-#include <linux/seq_file.h>
+#include <linux/uaccess.h>
#include <linux/mfd/da9052/da9052.h>
-#include <linux/mfd/da9052/reg.h>
#include <linux/mfd/da9052/pdata.h>
+#include <linux/mfd/da9052/reg.h>
#define DA9052_INPUT 1
#define DA9052_OUTPUT_OPENDRAIN 2
diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c
index 523dfd17dd92..e6a7049bef64 100644
--- a/drivers/gpio/gpio-mockup.c
+++ b/drivers/gpio/gpio-mockup.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/string_helpers.h>
#include <linux/uaccess.h>
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index c871602fc5ba..d5626c572d24 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -24,6 +24,12 @@
#include <linux/of_device.h>
#include <linux/bug.h>
+#define IMX_SCU_WAKEUP_OFF 0
+#define IMX_SCU_WAKEUP_LOW_LVL 4
+#define IMX_SCU_WAKEUP_FALL_EDGE 5
+#define IMX_SCU_WAKEUP_RISE_EDGE 6
+#define IMX_SCU_WAKEUP_HIGH_LVL 7
+
/* device type dependent stuff */
struct mxc_gpio_hwdata {
unsigned dr_reg;
@@ -61,6 +67,9 @@ struct mxc_gpio_port {
u32 both_edges;
struct mxc_gpio_reg_saved gpio_saved_reg;
bool power_off;
+ u32 wakeup_pads;
+ bool is_pad_wakeup;
+ u32 pad_type[32];
const struct mxc_gpio_hwdata *hwdata;
};
@@ -130,6 +139,9 @@ static const struct of_device_id mxc_gpio_dt_ids[] = {
{ .compatible = "fsl,imx31-gpio", .data = &imx31_gpio_hwdata },
{ .compatible = "fsl,imx35-gpio", .data = &imx35_gpio_hwdata },
{ .compatible = "fsl,imx7d-gpio", .data = &imx35_gpio_hwdata },
+ { .compatible = "fsl,imx8dxl-gpio", .data = &imx35_gpio_hwdata },
+ { .compatible = "fsl,imx8qm-gpio", .data = &imx35_gpio_hwdata },
+ { .compatible = "fsl,imx8qxp-gpio", .data = &imx35_gpio_hwdata },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxc_gpio_dt_ids);
@@ -203,6 +215,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
}
writel(1 << gpio_idx, port->base + GPIO_ISR);
+ port->pad_type[gpio_idx] = type;
return 0;
}
@@ -254,6 +267,9 @@ static void mx3_gpio_irq_handler(struct irq_desc *desc)
struct mxc_gpio_port *port = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
+ if (port->is_pad_wakeup)
+ return;
+
chained_irq_enter(chip, desc);
irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
@@ -306,11 +322,13 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
ret = enable_irq_wake(port->irq_high);
else
ret = enable_irq_wake(port->irq);
+ port->wakeup_pads |= (1 << gpio_idx);
} else {
if (port->irq_high && (gpio_idx >= 16))
ret = disable_irq_wake(port->irq_high);
else
ret = disable_irq_wake(port->irq);
+ port->wakeup_pads &= ~(1 << gpio_idx);
}
return ret;
@@ -365,7 +383,6 @@ static int mxc_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
port->dev = &pdev->dev;
-
port->hwdata = device_get_match_data(&pdev->dev);
port->base = devm_platform_ioremap_resource(pdev, 0);
@@ -498,6 +515,78 @@ static void mxc_gpio_restore_regs(struct mxc_gpio_port *port)
writel(port->gpio_saved_reg.dr, port->base + GPIO_DR);
}
+static bool mxc_gpio_generic_config(struct mxc_gpio_port *port,
+ unsigned int offset, unsigned long conf)
+{
+ struct device_node *np = port->dev->of_node;
+
+ if (of_device_is_compatible(np, "fsl,imx8dxl-gpio") ||
+ of_device_is_compatible(np, "fsl,imx8qxp-gpio") ||
+ of_device_is_compatible(np, "fsl,imx8qm-gpio"))
+ return (gpiochip_generic_config(&port->gc, offset, conf) == 0);
+
+ return false;
+}
+
+static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
+{
+ unsigned long config;
+ bool ret = false;
+ int i, type;
+
+ static const u32 pad_type_map[] = {
+ IMX_SCU_WAKEUP_OFF, /* 0 */
+ IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_RISING */
+ IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_FALLING */
+ IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_BOTH */
+ IMX_SCU_WAKEUP_HIGH_LVL, /* IRQ_TYPE_LEVEL_HIGH */
+ IMX_SCU_WAKEUP_OFF, /* 5 */
+ IMX_SCU_WAKEUP_OFF, /* 6 */
+ IMX_SCU_WAKEUP_OFF, /* 7 */
+ IMX_SCU_WAKEUP_LOW_LVL, /* IRQ_TYPE_LEVEL_LOW */
+ };
+
+ for (i = 0; i < 32; i++) {
+ if ((port->wakeup_pads & (1 << i))) {
+ type = port->pad_type[i];
+ if (enable)
+ config = pad_type_map[type];
+ else
+ config = IMX_SCU_WAKEUP_OFF;
+ ret |= mxc_gpio_generic_config(port, i, config);
+ }
+ }
+
+ return ret;
+}
+
+static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+
+ if (port->wakeup_pads > 0)
+ port->is_pad_wakeup = mxc_gpio_set_pad_wakeup(port, true);
+
+ return 0;
+}
+
+static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+
+ if (port->wakeup_pads > 0)
+ mxc_gpio_set_pad_wakeup(port, false);
+ port->is_pad_wakeup = false;
+
+ return 0;
+}
+
+static const struct dev_pm_ops mxc_gpio_dev_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume)
+};
+
static int mxc_gpio_syscore_suspend(void)
{
struct mxc_gpio_port *port;
@@ -537,6 +626,7 @@ static struct platform_driver mxc_gpio_driver = {
.name = "gpio-mxc",
.of_match_table = mxc_gpio_dt_ids,
.suppress_bind_attrs = true,
+ .pm = &mxc_gpio_dev_pm_ops,
},
.probe = mxc_gpio_probe,
};
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index ebe1943b85dd..6e67867e1dcd 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -10,8 +10,8 @@
#include <linux/acpi.h>
#include <linux/bitmap.h>
-#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -20,6 +20,7 @@
#include <linux/platform_data/pca953x.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 6464056cb6ae..9fc1f3dd4190 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -8,22 +8,23 @@
*
* Data sheet: ARM DDI 0190B, September 2000
*/
-#include <linux/spinlock.h>
+#include <linux/amba/bus.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/gpio/driver.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
-#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/module.h>
-#include <linux/bitops.h>
-#include <linux/gpio/driver.h>
-#include <linux/device.h>
-#include <linux/amba/bus.h>
-#include <linux/slab.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
#define GPIODIR 0x400
#define GPIOIS 0x404
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 870910bb9dd3..200e43a6f4b4 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -610,6 +610,7 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
return -ENODATA;
pctldev = of_pinctrl_get(pctlnp);
+ of_node_put(pctlnp);
if (!pctldev)
return -ENODEV;
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index e4fb4cb38a0f..5b265a6fd3c1 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -18,6 +18,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/seq_file.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/pinctrl/consumer.h>
@@ -94,7 +95,6 @@ struct tegra_gpio_info {
struct tegra_gpio_bank *bank_info;
const struct tegra_gpio_soc_config *soc;
struct gpio_chip gc;
- struct irq_chip ic;
u32 bank_count;
unsigned int *irqs;
};
@@ -288,6 +288,7 @@ static void tegra_gpio_irq_mask(struct irq_data *d)
unsigned int gpio = d->hwirq;
tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0);
+ gpiochip_disable_irq(chip, gpio);
}
static void tegra_gpio_irq_unmask(struct irq_data *d)
@@ -296,6 +297,7 @@ static void tegra_gpio_irq_unmask(struct irq_data *d)
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
+ gpiochip_enable_irq(chip, gpio);
tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1);
}
@@ -598,10 +600,47 @@ static void tegra_gpio_irq_release_resources(struct irq_data *d)
tegra_gpio_enable(tgi, d->hwirq);
}
+static void tegra_gpio_irq_print_chip(struct irq_data *d, struct seq_file *s)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ seq_printf(s, dev_name(chip->parent));
+}
+
+static const struct irq_chip tegra_gpio_irq_chip = {
+ .irq_shutdown = tegra_gpio_irq_shutdown,
+ .irq_ack = tegra_gpio_irq_ack,
+ .irq_mask = tegra_gpio_irq_mask,
+ .irq_unmask = tegra_gpio_irq_unmask,
+ .irq_set_type = tegra_gpio_irq_set_type,
+#ifdef CONFIG_PM_SLEEP
+ .irq_set_wake = tegra_gpio_irq_set_wake,
+#endif
+ .irq_print_chip = tegra_gpio_irq_print_chip,
+ .irq_request_resources = tegra_gpio_irq_request_resources,
+ .irq_release_resources = tegra_gpio_irq_release_resources,
+ .flags = IRQCHIP_IMMUTABLE,
+};
+
+static const struct irq_chip tegra210_gpio_irq_chip = {
+ .irq_shutdown = tegra_gpio_irq_shutdown,
+ .irq_ack = tegra_gpio_irq_ack,
+ .irq_mask = tegra_gpio_irq_mask,
+ .irq_unmask = tegra_gpio_irq_unmask,
+ .irq_set_affinity = tegra_gpio_irq_set_affinity,
+ .irq_set_type = tegra_gpio_irq_set_type,
+#ifdef CONFIG_PM_SLEEP
+ .irq_set_wake = tegra_gpio_irq_set_wake,
+#endif
+ .irq_print_chip = tegra_gpio_irq_print_chip,
+ .irq_request_resources = tegra_gpio_irq_request_resources,
+ .irq_release_resources = tegra_gpio_irq_release_resources,
+ .flags = IRQCHIP_IMMUTABLE,
+};
+
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
-#include <linux/seq_file.h>
static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
{
@@ -689,18 +728,6 @@ static int tegra_gpio_probe(struct platform_device *pdev)
tgi->gc.ngpio = tgi->bank_count * 32;
tgi->gc.parent = &pdev->dev;
- tgi->ic.name = "GPIO";
- tgi->ic.irq_ack = tegra_gpio_irq_ack;
- tgi->ic.irq_mask = tegra_gpio_irq_mask;
- tgi->ic.irq_unmask = tegra_gpio_irq_unmask;
- tgi->ic.irq_set_type = tegra_gpio_irq_set_type;
- tgi->ic.irq_shutdown = tegra_gpio_irq_shutdown;
-#ifdef CONFIG_PM_SLEEP
- tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake;
-#endif
- tgi->ic.irq_request_resources = tegra_gpio_irq_request_resources;
- tgi->ic.irq_release_resources = tegra_gpio_irq_release_resources;
-
platform_set_drvdata(pdev, tgi);
if (tgi->soc->debounce_supported)
@@ -733,7 +760,6 @@ static int tegra_gpio_probe(struct platform_device *pdev)
}
irq = &tgi->gc.irq;
- irq->chip = &tgi->ic;
irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
irq->child_to_parent_hwirq = tegra_gpio_child_to_parent_hwirq;
irq->populate_parent_alloc_arg = tegra_gpio_populate_parent_fwspec;
@@ -752,7 +778,9 @@ static int tegra_gpio_probe(struct platform_device *pdev)
if (!irq->parent_domain)
return -EPROBE_DEFER;
- tgi->ic.irq_set_affinity = tegra_gpio_irq_set_affinity;
+ gpio_irq_chip_set_chip(irq, &tegra210_gpio_irq_chip);
+ } else {
+ gpio_irq_chip_set_chip(irq, &tegra_gpio_irq_chip);
}
tgi->regs = devm_platform_ioremap_resource(pdev, 0);
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index 54d9fa7da9c1..fdc5bdcd5638 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -7,12 +7,13 @@
*/
#include <linux/gpio/driver.h>
+#include <linux/hte.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
-#include <linux/hte.h>
+#include <linux/seq_file.h>
#include <dt-bindings/gpio/tegra186-gpio.h>
#include <dt-bindings/gpio/tegra194-gpio.h>
diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c
index b1b131fb9804..2421cf606ed6 100644
--- a/drivers/gpio/gpio-wm8350.c
+++ b/drivers/gpio/gpio-wm8350.c
@@ -8,13 +8,12 @@
*
*/
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
#include <linux/gpio/driver.h>
+#include <linux/kernel.h>
#include <linux/mfd/core.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <linux/mfd/wm8350/core.h>
#include <linux/mfd/wm8350/gpio.h>
diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h
index 1ac6816839db..01e0cb480a00 100644
--- a/drivers/gpio/gpiolib-acpi.h
+++ b/drivers/gpio/gpiolib-acpi.h
@@ -8,7 +8,19 @@
#ifndef GPIOLIB_ACPI_H
#define GPIOLIB_ACPI_H
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+
+#include <linux/gpio/consumer.h>
+
struct acpi_device;
+struct device;
+struct fwnode_handle;
+
+struct gpio_chip;
+struct gpio_desc;
+struct gpio_device;
/**
* struct acpi_gpio_info - ACPI GPIO specific information
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 0cb6b468f364..65b2c09a4576 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -12,6 +12,7 @@
#include <linux/file.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
+#include <linux/hte.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/kernel.h>
@@ -20,11 +21,12 @@
#include <linux/mutex.h>
#include <linux/pinctrl/consumer.h>
#include <linux/poll.h>
+#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/timekeeping.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
-#include <linux/hte.h>
+
#include <uapi/linux/gpio.h>
#include "gpiolib.h"
diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
index 8af2bc899aab..1b5df39a952e 100644
--- a/drivers/gpio/gpiolib-of.h
+++ b/drivers/gpio/gpiolib-of.h
@@ -3,8 +3,17 @@
#ifndef GPIOLIB_OF_H
#define GPIOLIB_OF_H
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+
+#include <linux/notifier.h>
+
+struct device;
+
struct gpio_chip;
-enum of_gpio_flags;
+struct gpio_desc;
+struct gpio_device;
#ifdef CONFIG_OF_GPIO
struct gpio_desc *of_find_gpio(struct device *dev,
diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h
index ddd0e503f8eb..0f213bdb4732 100644
--- a/drivers/gpio/gpiolib-sysfs.h
+++ b/drivers/gpio/gpiolib-sysfs.h
@@ -5,6 +5,8 @@
#ifdef CONFIG_GPIO_SYSFS
+struct gpio_device;
+
int gpiochip_sysfs_register(struct gpio_device *gdev);
void gpiochip_sysfs_unregister(struct gpio_device *gdev);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4756ea08894f..a70522aef355 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -526,12 +526,13 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
if (ret)
return ret;
+ /* From this point, the .release() function cleans up gpio_device */
+ gdev->dev.release = gpiodevice_release;
+
ret = gpiochip_sysfs_register(gdev);
if (ret)
goto err_remove_device;
- /* From this point, the .release() function cleans up gpio_device */
- gdev->dev.release = gpiodevice_release;
dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base,
gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic");
@@ -597,10 +598,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct fwnode_handle *fwnode = NULL;
struct gpio_device *gdev;
unsigned long flags;
- int base = gc->base;
unsigned int i;
+ u32 ngpios = 0;
+ int base = 0;
int ret = 0;
- u32 ngpios;
if (gc->fwnode)
fwnode = gc->fwnode;
@@ -647,17 +648,12 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
else
gdev->owner = THIS_MODULE;
- gdev->descs = kcalloc(gc->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
- if (!gdev->descs) {
- ret = -ENOMEM;
- goto err_free_dev_name;
- }
-
/*
* Try the device properties if the driver didn't supply the number
* of GPIO lines.
*/
- if (gc->ngpio == 0) {
+ ngpios = gc->ngpio;
+ if (ngpios == 0) {
ret = device_property_read_u32(&gdev->dev, "ngpios", &ngpios);
if (ret == -ENODATA)
/*
@@ -668,7 +664,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
*/
ngpios = 0;
else if (ret)
- goto err_free_descs;
+ goto err_free_dev_name;
gc->ngpio = ngpios;
}
@@ -676,13 +672,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (gc->ngpio == 0) {
chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
ret = -EINVAL;
- goto err_free_descs;
+ goto err_free_dev_name;
}
if (gc->ngpio > FASTPATH_NGPIO)
chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
gc->ngpio, FASTPATH_NGPIO);
+ gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
+ if (!gdev->descs) {
+ ret = -ENOMEM;
+ goto err_free_dev_name;
+ }
+
gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) {
ret = -ENOMEM;
@@ -701,11 +703,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* it may be a pipe dream. It will not happen before we get rid
* of the sysfs interface anyways.
*/
+ base = gc->base;
if (base < 0) {
base = gpiochip_find_base(gc->ngpio);
if (base < 0) {
- ret = base;
spin_unlock_irqrestore(&gpio_lock, flags);
+ ret = base;
+ base = 0;
goto err_free_label;
}
/*
@@ -816,6 +820,11 @@ err_remove_of_chip:
err_free_gpiochip_mask:
gpiochip_remove_pin_ranges(gc);
gpiochip_free_valid_mask(gc);
+ if (gdev->dev.release) {
+ /* release() has been registered by gpiochip_setup_dev() */
+ put_device(&gdev->dev);
+ goto err_print_message;
+ }
err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags);
list_del(&gdev->list);
@@ -829,13 +838,14 @@ err_free_dev_name:
err_free_ida:
ida_free(&gpio_ida, gdev->id);
err_free_gdev:
+ kfree(gdev);
+err_print_message:
/* failures here can mean systems won't boot... */
if (ret != -EPROBE_DEFER) {
pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
- gdev->base, gdev->base + gdev->ngpio - 1,
+ base, base + (int)ngpios - 1,
gc->label ? : "generic", ret);
}
- kfree(gdev);
return ret;
}
EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key);