diff options
Diffstat (limited to 'drivers')
29 files changed, 1958 insertions, 688 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b3a133ed31ee..1301cec94f12 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -478,6 +478,13 @@ config GPIO_MXS select GPIO_GENERIC select GENERIC_IRQ_CHIP +config GPIO_NPCM_SGPIO + bool "Nuvoton SGPIO support" + depends on ARCH_NPCM || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + Say Y here to support Nuvoton NPCM7XX/NPCM8XX SGPIO functionality. + config GPIO_OCTEON tristate "Cavium OCTEON GPIO" depends on CAVIUM_OCTEON_SOC @@ -553,6 +560,19 @@ config GPIO_ROCKCHIP help Say yes here to support GPIO on Rockchip SoCs. +config GPIO_RTD + tristate "Realtek DHC GPIO support" + depends on ARCH_REALTEK + default y + select GPIOLIB_IRQCHIP + help + This option enables support for GPIOs found on Realtek DHC(Digital + Home Center) SoCs family, including RTD1295, RTD1315E, RTD1319, + RTD1319D, RTD1395, RTD1619 and RTD1619B. + + Say yes here to support GPIO functionality and GPIO interrupt on + Realtek DHC SoCs. + config GPIO_SAMA5D2_PIOBU tristate "SAMA5D2 PIOBU GPIO support" depends on MFD_SYSCON diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index eb73b5d633eb..9e40af196aae 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -116,6 +116,7 @@ obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o +obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o @@ -137,6 +138,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o obj-$(CONFIG_GPIO_REG) += gpio-reg.o obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o +obj-$(CONFIG_GPIO_RTD) += gpio-rtd.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 8c59332429c2..798235791f70 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -416,11 +416,12 @@ static int dwapb_gpio_set_config(struct gpio_chip *gc, unsigned offset, { u32 debounce; - if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) - return -ENOTSUPP; + if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) { + debounce = pinconf_to_config_argument(config); + return dwapb_gpio_set_debounce(gc, offset, debounce); + } - debounce = pinconf_to_config_argument(config); - return dwapb_gpio_set_debounce(gc, offset, debounce); + return gpiochip_generic_config(gc, offset, config); } static int dwapb_convert_irqs(struct dwapb_gpio_port_irqchip *pirq, @@ -530,10 +531,14 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, port->gc.fwnode = pp->fwnode; port->gc.ngpio = pp->ngpio; port->gc.base = pp->gpio_base; + port->gc.request = gpiochip_generic_request; + port->gc.free = gpiochip_generic_free; /* Only port A support debounce */ if (pp->idx == 0) port->gc.set_config = dwapb_gpio_set_config; + else + port->gc.set_config = gpiochip_generic_config; /* Only port A can provide interrupts in all configurations of the IP */ if (pp->idx == 0) diff --git a/drivers/gpio/gpio-elkhartlake.c b/drivers/gpio/gpio-elkhartlake.c index a9c8b16215be..887c0fe99d39 100644 --- a/drivers/gpio/gpio-elkhartlake.c +++ b/drivers/gpio/gpio-elkhartlake.c @@ -55,18 +55,6 @@ static int ehl_gpio_probe(struct platform_device *pdev) return 0; } -static int ehl_gpio_suspend(struct device *dev) -{ - return tng_gpio_suspend(dev); -} - -static int ehl_gpio_resume(struct device *dev) -{ - return tng_gpio_resume(dev); -} - -static DEFINE_SIMPLE_DEV_PM_OPS(ehl_gpio_pm_ops, ehl_gpio_suspend, ehl_gpio_resume); - static const struct platform_device_id ehl_gpio_ids[] = { { "gpio-elkhartlake" }, { } @@ -76,7 +64,7 @@ MODULE_DEVICE_TABLE(platform, ehl_gpio_ids); static struct platform_driver ehl_gpio_driver = { .driver = { .name = "gpio-elkhartlake", - .pm = pm_sleep_ptr(&ehl_gpio_pm_ops), + .pm = pm_sleep_ptr(&tng_gpio_pm_ops), }, .probe = ehl_gpio_probe, .id_table = ehl_gpio_ids, diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index dde6cf3a5779..c5a9fa640566 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -38,6 +38,18 @@ #define IXP4XX_GPIO_STYLE_MASK GENMASK(2, 0) #define IXP4XX_GPIO_STYLE_SIZE 3 +/* + * Clock output control register defines. + */ +#define IXP4XX_GPCLK_CLK0DC_SHIFT 0 +#define IXP4XX_GPCLK_CLK0TC_SHIFT 4 +#define IXP4XX_GPCLK_CLK0_MASK GENMASK(7, 0) +#define IXP4XX_GPCLK_MUX14 BIT(8) +#define IXP4XX_GPCLK_CLK1DC_SHIFT 16 +#define IXP4XX_GPCLK_CLK1TC_SHIFT 20 +#define IXP4XX_GPCLK_CLK1_MASK GENMASK(23, 16) +#define IXP4XX_GPCLK_MUX15 BIT(24) + /** * struct ixp4xx_gpio - IXP4 GPIO state container * @dev: containing device for this instance @@ -202,6 +214,8 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) struct ixp4xx_gpio *g; struct gpio_irq_chip *girq; struct device_node *irq_parent; + bool clk_14, clk_15; + u32 val; int ret; g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); @@ -226,12 +240,47 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) g->fwnode = of_node_to_fwnode(np); /* + * If either clock output is enabled explicitly in the device tree + * we take full control of the clock by masking off all bits for + * the clock control and selectively enabling them. Otherwise + * we leave the hardware default settings. + * + * Enable clock outputs with default timings of requested clock. + * If you need control over TC and DC, add these to the device + * tree bindings and use them here. + */ + clk_14 = of_property_read_bool(np, "intel,ixp4xx-gpio14-clkout"); + clk_15 = of_property_read_bool(np, "intel,ixp4xx-gpio15-clkout"); + + /* * Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on * specific machines. */ if (of_machine_is_compatible("dlink,dsm-g600-a") || of_machine_is_compatible("iom,nas-100d")) - __raw_writel(0x0, g->base + IXP4XX_REG_GPCLK); + val = 0; + else { + val = __raw_readl(g->base + IXP4XX_REG_GPCLK); + + if (clk_14 || clk_15) { + val &= ~(IXP4XX_GPCLK_MUX14 | IXP4XX_GPCLK_MUX15); + val &= ~IXP4XX_GPCLK_CLK0_MASK; + val &= ~IXP4XX_GPCLK_CLK1_MASK; + if (clk_14) { + /* IXP4XX_GPCLK_CLK0DC implicit low */ + val |= (1 << IXP4XX_GPCLK_CLK0TC_SHIFT); + val |= IXP4XX_GPCLK_MUX14; + } + + if (clk_15) { + /* IXP4XX_GPCLK_CLK1DC implicit low */ + val |= (1 << IXP4XX_GPCLK_CLK1TC_SHIFT); + val |= IXP4XX_GPCLK_MUX15; + } + } + } + + __raw_writel(val, g->base + IXP4XX_REG_GPCLK); /* * This is a very special big-endian ARM issue: when the IXP4xx is diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c index bb5cf14ae4c8..701795b9d329 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Copyright (C) 2006 Juergen Beisert, Pengutronix * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix * Copyright (C) 2009 Wolfram Sang, Pengutronix diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 3ff0ea1e351c..71e1af7c2184 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -40,25 +40,22 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` * `.......````.``` */ -#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/compiler.h> #include <linux/err.h> -#include <linux/bug.h> -#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/log2.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/compiler.h> #include <linux/types.h> -#include <linux/errno.h> -#include <linux/log2.h> -#include <linux/ioport.h> -#include <linux/io.h> + #include <linux/gpio/driver.h> -#include <linux/slab.h> -#include <linux/bitops.h> -#include <linux/platform_device.h> -#include <linux/property.h> -#include <linux/mod_devicetable.h> -#include <linux/of.h> #include "gpiolib.h" @@ -688,7 +685,6 @@ static void __iomem *bgpio_map(struct platform_device *pdev, return devm_ioremap_resource(&pdev->dev, r); } -#ifdef CONFIG_OF static const struct of_device_id bgpio_of_match[] = { { .compatible = "brcm,bcm6345-gpio" }, { .compatible = "wd,mbl-gpio" }, @@ -697,36 +693,27 @@ static const struct of_device_id bgpio_of_match[] = { }; MODULE_DEVICE_TABLE(of, bgpio_of_match); -static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev, - unsigned long *flags) +static struct bgpio_pdata *bgpio_parse_fw(struct device *dev, unsigned long *flags) { struct bgpio_pdata *pdata; - if (!pdev->dev.of_node) + if (!dev_fwnode(dev)) return NULL; - pdata = devm_kzalloc(&pdev->dev, sizeof(struct bgpio_pdata), - GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); pdata->base = -1; - if (of_device_is_big_endian(pdev->dev.of_node)) + if (device_is_big_endian(dev)) *flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; - if (of_property_read_bool(pdev->dev.of_node, "no-output")) + if (device_property_read_bool(dev, "no-output")) *flags |= BGPIOF_NO_OUTPUT; return pdata; } -#else -static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev, - unsigned long *flags) -{ - return NULL; -} -#endif /* CONFIG_OF */ static int bgpio_pdev_probe(struct platform_device *pdev) { @@ -743,7 +730,7 @@ static int bgpio_pdev_probe(struct platform_device *pdev) struct gpio_chip *gc; struct bgpio_pdata *pdata; - pdata = bgpio_parse_dt(pdev, &flags); + pdata = bgpio_parse_fw(dev, &flags); if (IS_ERR(pdata)) return PTR_ERR(pdata); @@ -814,7 +801,7 @@ MODULE_DEVICE_TABLE(platform, bgpio_id_table); static struct platform_driver bgpio_driver = { .driver = { .name = "basic-mmio-gpio", - .of_match_table = of_match_ptr(bgpio_of_match), + .of_match_table = bgpio_of_match, }, .id_table = bgpio_id_table, .probe = bgpio_pdev_probe, diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 4870e267a402..455eecf6380e 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -354,7 +354,6 @@ static const struct file_operations gpio_mockup_debugfs_ops = { static void gpio_mockup_debugfs_setup(struct device *dev, struct gpio_mockup_chip *chip) { - struct device *child __free(put_device) = NULL; struct gpio_mockup_dbgfs_private *priv; struct gpio_chip *gc; const char *devname; @@ -367,7 +366,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, * There can only be a single GPIO device per platform device in * gpio-mockup so using device_find_any_child() is OK. */ - child = device_find_any_child(dev); + struct device *child __free(put_device) = device_find_any_child(dev); if (!child) return; diff --git a/drivers/gpio/gpio-npcm-sgpio.c b/drivers/gpio/gpio-npcm-sgpio.c new file mode 100644 index 000000000000..d31788b43abc --- /dev/null +++ b/drivers/gpio/gpio-npcm-sgpio.c @@ -0,0 +1,619 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton NPCM Serial GPIO Driver + * + * Copyright (C) 2021 Nuvoton Technologies + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/gpio/driver.h> +#include <linux/hashtable.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/units.h> + +#define MAX_NR_HW_SGPIO 64 + +#define NPCM_IOXCFG1 0x2A +#define NPCM_IOXCFG1_SFT_CLK GENMASK(3, 0) +#define NPCM_IOXCFG1_SCLK_POL BIT(4) +#define NPCM_IOXCFG1_LDSH_POL BIT(5) + +#define NPCM_IOXCTS 0x28 +#define NPCM_IOXCTS_IOXIF_EN BIT(7) +#define NPCM_IOXCTS_RD_MODE GENMASK(2, 1) +#define NPCM_IOXCTS_RD_MODE_PERIODIC BIT(2) + +#define NPCM_IOXCFG2 0x2B +#define NPCM_IOXCFG2_PORT GENMASK(3, 0) + +#define NPCM_IXOEVCFG_MASK GENMASK(1, 0) +#define NPCM_IXOEVCFG_FALLING BIT(1) +#define NPCM_IXOEVCFG_RISING BIT(0) +#define NPCM_IXOEVCFG_BOTH (NPCM_IXOEVCFG_FALLING | NPCM_IXOEVCFG_RISING) + +#define NPCM_CLK_MHZ (8 * HZ_PER_MHZ) +#define NPCM_750_OPT 6 +#define NPCM_845_OPT 5 + +#define GPIO_BANK(x) ((x) / 8) +#define GPIO_BIT(x) ((x) % 8) + +/* + * Select the frequency of shift clock. + * The shift clock is a division of the APB clock. + */ +struct npcm_clk_cfg { + unsigned int *sft_clk; + unsigned int *clk_sel; + unsigned int cfg_opt; +}; + +struct npcm_sgpio { + struct gpio_chip chip; + struct clk *pclk; + struct irq_chip intc; + raw_spinlock_t lock; + + void __iomem *base; + int irq; + u8 nin_sgpio; + u8 nout_sgpio; + u8 in_port; + u8 out_port; + u8 int_type[MAX_NR_HW_SGPIO]; +}; + +struct npcm_sgpio_bank { + u8 rdata_reg; + u8 wdata_reg; + u8 event_config; + u8 event_status; +}; + +enum npcm_sgpio_reg { + READ_DATA, + WRITE_DATA, + EVENT_CFG, + EVENT_STS, +}; + +static const struct npcm_sgpio_bank npcm_sgpio_banks[] = { + { + .wdata_reg = 0x00, + .rdata_reg = 0x08, + .event_config = 0x10, + .event_status = 0x20, + }, + { + .wdata_reg = 0x01, + .rdata_reg = 0x09, + .event_config = 0x12, + .event_status = 0x21, + }, + { + .wdata_reg = 0x02, + .rdata_reg = 0x0a, + .event_config = 0x14, + .event_status = 0x22, + }, + { + .wdata_reg = 0x03, + .rdata_reg = 0x0b, + .event_config = 0x16, + .event_status = 0x23, + }, + { + .wdata_reg = 0x04, + .rdata_reg = 0x0c, + .event_config = 0x18, + .event_status = 0x24, + }, + { + .wdata_reg = 0x05, + .rdata_reg = 0x0d, + .event_config = 0x1a, + .event_status = 0x25, + }, + { + .wdata_reg = 0x06, + .rdata_reg = 0x0e, + .event_config = 0x1c, + .event_status = 0x26, + }, + { + .wdata_reg = 0x07, + .rdata_reg = 0x0f, + .event_config = 0x1e, + .event_status = 0x27, + }, +}; + +static void __iomem *bank_reg(struct npcm_sgpio *gpio, + const struct npcm_sgpio_bank *bank, + const enum npcm_sgpio_reg reg) +{ + switch (reg) { + case READ_DATA: + return gpio->base + bank->rdata_reg; + case WRITE_DATA: + return gpio->base + bank->wdata_reg; + case EVENT_CFG: + return gpio->base + bank->event_config; + case EVENT_STS: + return gpio->base + bank->event_status; + default: + /* actually if code runs to here, it's an error case */ + dev_WARN(gpio->chip.parent, "Getting here is an error condition"); + return NULL; + } +} + +static const struct npcm_sgpio_bank *offset_to_bank(unsigned int offset) +{ + unsigned int bank = GPIO_BANK(offset); + + return &npcm_sgpio_banks[bank]; +} + +static void npcm_sgpio_irqd_to_data(struct irq_data *d, + struct npcm_sgpio **gpio, + const struct npcm_sgpio_bank **bank, + u8 *bit, unsigned int *offset) +{ + struct npcm_sgpio *internal; + + *offset = irqd_to_hwirq(d); + internal = irq_data_get_irq_chip_data(d); + + *gpio = internal; + *offset -= internal->nout_sgpio; + *bank = offset_to_bank(*offset); + *bit = GPIO_BIT(*offset); +} + +static int npcm_sgpio_init_port(struct npcm_sgpio *gpio) +{ + u8 in_port, out_port, set_port, reg; + + in_port = GPIO_BANK(gpio->nin_sgpio); + if (GPIO_BIT(gpio->nin_sgpio) > 0) + in_port += 1; + + out_port = GPIO_BANK(gpio->nout_sgpio); + if (GPIO_BIT(gpio->nout_sgpio) > 0) + out_port += 1; + + gpio->in_port = in_port; + gpio->out_port = out_port; + set_port = (out_port & NPCM_IOXCFG2_PORT) << 4 | + (in_port & NPCM_IOXCFG2_PORT); + iowrite8(set_port, gpio->base + NPCM_IOXCFG2); + + reg = ioread8(gpio->base + NPCM_IOXCFG2); + + return reg == set_port ? 0 : -EINVAL; + +} + +static int npcm_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) +{ + struct npcm_sgpio *gpio = gpiochip_get_data(gc); + + return offset < gpio->nout_sgpio ? -EINVAL : 0; + +} + +static int npcm_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) +{ + gc->set(gc, offset, val); + + return 0; +} + +static int npcm_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct npcm_sgpio *gpio = gpiochip_get_data(gc); + + if (offset < gpio->nout_sgpio) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static void npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct npcm_sgpio *gpio = gpiochip_get_data(gc); + const struct npcm_sgpio_bank *bank = offset_to_bank(offset); + void __iomem *addr; + u8 reg = 0; + + addr = bank_reg(gpio, bank, WRITE_DATA); + reg = ioread8(addr); + + if (val) + reg |= BIT(GPIO_BIT(offset)); + else + reg &= ~BIT(GPIO_BIT(offset)); + + iowrite8(reg, addr); +} + +static int npcm_sgpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct npcm_sgpio *gpio = gpiochip_get_data(gc); + const struct npcm_sgpio_bank *bank; + void __iomem *addr; + u8 reg; + + if (offset < gpio->nout_sgpio) { + bank = offset_to_bank(offset); + addr = bank_reg(gpio, bank, WRITE_DATA); + } else { + offset -= gpio->nout_sgpio; + bank = offset_to_bank(offset); + addr = bank_reg(gpio, bank, READ_DATA); + } + + reg = ioread8(addr); + + return !!(reg & BIT(GPIO_BIT(offset))); +} + +static void npcm_sgpio_setup_enable(struct npcm_sgpio *gpio, bool enable) +{ + u8 reg; + + reg = ioread8(gpio->base + NPCM_IOXCTS); + reg = (reg & ~NPCM_IOXCTS_RD_MODE) | NPCM_IOXCTS_RD_MODE_PERIODIC; + + if (enable) + reg |= NPCM_IOXCTS_IOXIF_EN; + else + reg &= ~NPCM_IOXCTS_IOXIF_EN; + + iowrite8(reg, gpio->base + NPCM_IOXCTS); +} + +static int npcm_sgpio_setup_clk(struct npcm_sgpio *gpio, + const struct npcm_clk_cfg *clk_cfg) +{ + unsigned long apb_freq; + u32 val; + u8 tmp; + int i; + + apb_freq = clk_get_rate(gpio->pclk); + tmp = ioread8(gpio->base + NPCM_IOXCFG1) & ~NPCM_IOXCFG1_SFT_CLK; + + for (i = clk_cfg->cfg_opt-1; i > 0; i--) { + val = apb_freq / clk_cfg->sft_clk[i]; + if (NPCM_CLK_MHZ > val) { + iowrite8(clk_cfg->clk_sel[i] | tmp, + gpio->base + NPCM_IOXCFG1); + return 0; + } + } + + return -EINVAL; +} + +static void npcm_sgpio_irq_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct npcm_sgpio *gpio = gpiochip_get_data(gc); + + /* input GPIOs in the high range */ + bitmap_set(valid_mask, gpio->nout_sgpio, gpio->nin_sgpio); + bitmap_clear(valid_mask, 0, gpio->nout_sgpio); +} + +static void npcm_sgpio_irq_set_mask(struct irq_data *d, bool set) +{ + const struct npcm_sgpio_bank *bank; + struct npcm_sgpio *gpio; + unsigned long flags; + void __iomem *addr; + unsigned int offset; + u16 reg, type; + u8 bit; + + npcm_sgpio_irqd_to_data(d, &gpio, &bank, &bit, &offset); + addr = bank_reg(gpio, bank, EVENT_CFG); + + reg = ioread16(addr); + if (set) { + reg &= ~(NPCM_IXOEVCFG_MASK << (bit * 2)); + } else { + type = gpio->int_type[offset]; + reg |= (type << (bit * 2)); + } + + raw_spin_lock_irqsave(&gpio->lock, flags); + + npcm_sgpio_setup_enable(gpio, false); + + iowrite16(reg, addr); + + npcm_sgpio_setup_enable(gpio, true); + + addr = bank_reg(gpio, bank, EVENT_STS); + reg = ioread8(addr); + reg |= BIT(bit); + iowrite8(reg, addr); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); +} + +static void npcm_sgpio_irq_ack(struct irq_data *d) +{ + const struct npcm_sgpio_bank *bank; + struct npcm_sgpio *gpio; + unsigned long flags; + void __iomem *status_addr; + unsigned int offset; + u8 bit; + + npcm_sgpio_irqd_to_data(d, &gpio, &bank, &bit, &offset); + status_addr = bank_reg(gpio, bank, EVENT_STS); + raw_spin_lock_irqsave(&gpio->lock, flags); + iowrite8(BIT(bit), status_addr); + raw_spin_unlock_irqrestore(&gpio->lock, flags); +} + +static void npcm_sgpio_irq_mask(struct irq_data *d) +{ + npcm_sgpio_irq_set_mask(d, true); +} + +static void npcm_sgpio_irq_unmask(struct irq_data *d) +{ + npcm_sgpio_irq_set_mask(d, false); +} + +static int npcm_sgpio_set_type(struct irq_data *d, unsigned int type) +{ + const struct npcm_sgpio_bank *bank; + irq_flow_handler_t handler; + struct npcm_sgpio *gpio; + unsigned long flags; + void __iomem *addr; + unsigned int offset; + u16 reg, val; + u8 bit; + + npcm_sgpio_irqd_to_data(d, &gpio, &bank, &bit, &offset); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_BOTH: + val = NPCM_IXOEVCFG_BOTH; + break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + val = NPCM_IXOEVCFG_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_LEVEL_LOW: + val = NPCM_IXOEVCFG_FALLING; + break; + default: + return -EINVAL; + } + + if (type & IRQ_TYPE_LEVEL_MASK) + handler = handle_level_irq; + else + handler = handle_edge_irq; + + gpio->int_type[offset] = val; + + raw_spin_lock_irqsave(&gpio->lock, flags); + npcm_sgpio_setup_enable(gpio, false); + addr = bank_reg(gpio, bank, EVENT_CFG); + reg = ioread16(addr); + + reg |= (val << (bit * 2)); + + iowrite16(reg, addr); + npcm_sgpio_setup_enable(gpio, true); + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + irq_set_handler_locked(d, handler); + + return 0; +} + +static void npcm_sgpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct irq_chip *ic = irq_desc_get_chip(desc); + struct npcm_sgpio *gpio = gpiochip_get_data(gc); + unsigned int i, j, girq; + unsigned long reg; + + chained_irq_enter(ic, desc); + + for (i = 0; i < ARRAY_SIZE(npcm_sgpio_banks); i++) { + const struct npcm_sgpio_bank *bank = &npcm_sgpio_banks[i]; + + reg = ioread8(bank_reg(gpio, bank, EVENT_STS)); + for_each_set_bit(j, ®, 8) { + girq = irq_find_mapping(gc->irq.domain, + i * 8 + gpio->nout_sgpio + j); + generic_handle_domain_irq(gc->irq.domain, girq); + } + } + + chained_irq_exit(ic, desc); +} + +static const struct irq_chip sgpio_irq_chip = { + .name = "sgpio-irq", + .irq_ack = npcm_sgpio_irq_ack, + .irq_mask = npcm_sgpio_irq_mask, + .irq_unmask = npcm_sgpio_irq_unmask, + .irq_set_type = npcm_sgpio_set_type, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static int npcm_sgpio_setup_irqs(struct npcm_sgpio *gpio, + struct platform_device *pdev) +{ + int rc, i; + struct gpio_irq_chip *irq; + + rc = platform_get_irq(pdev, 0); + if (rc < 0) + return rc; + + gpio->irq = rc; + + npcm_sgpio_setup_enable(gpio, false); + + /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ + for (i = 0; i < ARRAY_SIZE(npcm_sgpio_banks); i++) { + const struct npcm_sgpio_bank *bank = &npcm_sgpio_banks[i]; + + iowrite16(0, bank_reg(gpio, bank, EVENT_CFG)); + iowrite8(0xff, bank_reg(gpio, bank, EVENT_STS)); + } + + irq = &gpio->chip.irq; + gpio_irq_chip_set_chip(irq, &sgpio_irq_chip); + irq->init_valid_mask = npcm_sgpio_irq_init_valid_mask; + irq->handler = handle_bad_irq; + irq->default_type = IRQ_TYPE_NONE; + irq->parent_handler = npcm_sgpio_irq_handler; + irq->parent_handler_data = gpio; + irq->parents = &gpio->irq; + irq->num_parents = 1; + + return 0; +} + +static int npcm_sgpio_probe(struct platform_device *pdev) +{ + struct npcm_sgpio *gpio; + const struct npcm_clk_cfg *clk_cfg; + int rc; + u32 nin_gpios, nout_gpios; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); + + clk_cfg = device_get_match_data(&pdev->dev); + if (!clk_cfg) + return -EINVAL; + + rc = device_property_read_u32(&pdev->dev, "nuvoton,input-ngpios", + &nin_gpios); + if (rc < 0) + return dev_err_probe(&pdev->dev, rc, "Could not read ngpios property\n"); + + rc = device_property_read_u32(&pdev->dev, "nuvoton,output-ngpios", + &nout_gpios); + if (rc < 0) + return dev_err_probe(&pdev->dev, rc, "Could not read ngpios property\n"); + + gpio->nin_sgpio = nin_gpios; + gpio->nout_sgpio = nout_gpios; + if (gpio->nin_sgpio > MAX_NR_HW_SGPIO || + gpio->nout_sgpio > MAX_NR_HW_SGPIO) + return dev_err_probe(&pdev->dev, -EINVAL, "Number of GPIOs exceeds the maximum of %d: input: %d output: %d\n", MAX_NR_HW_SGPIO, nin_gpios, nout_gpios); + + gpio->pclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(gpio->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(gpio->pclk), "Could not get pclk\n"); + + rc = npcm_sgpio_setup_clk(gpio, clk_cfg); + if (rc < 0) + return dev_err_probe(&pdev->dev, rc, "Failed to setup clock\n"); + + raw_spin_lock_init(&gpio->lock); + gpio->chip.parent = &pdev->dev; + gpio->chip.ngpio = gpio->nin_sgpio + gpio->nout_sgpio; + gpio->chip.direction_input = npcm_sgpio_dir_in; + gpio->chip.direction_output = npcm_sgpio_dir_out; + gpio->chip.get_direction = npcm_sgpio_get_direction; + gpio->chip.get = npcm_sgpio_get; + gpio->chip.set = npcm_sgpio_set; + gpio->chip.label = dev_name(&pdev->dev); + gpio->chip.base = -1; + + rc = npcm_sgpio_init_port(gpio); + if (rc < 0) + return rc; + + rc = npcm_sgpio_setup_irqs(gpio, pdev); + if (rc < 0) + return rc; + + rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (rc) + return dev_err_probe(&pdev->dev, rc, "GPIO registering failed\n"); + + npcm_sgpio_setup_enable(gpio, true); + + return 0; +} + +static unsigned int npcm750_SFT_CLK[NPCM_750_OPT] = { + 1024, 32, 8, 4, 3, 2, +}; + +static unsigned int npcm750_CLK_SEL[NPCM_750_OPT] = { + 0x00, 0x05, 0x07, 0x0C, 0x0D, 0x0E, +}; + +static unsigned int npcm845_SFT_CLK[NPCM_845_OPT] = { + 1024, 32, 16, 8, 4, +}; + +static unsigned int npcm845_CLK_SEL[NPCM_845_OPT] = { + 0x00, 0x05, 0x06, 0x07, 0x0C, +}; + +static struct npcm_clk_cfg npcm750_sgpio_pdata = { + .sft_clk = npcm750_SFT_CLK, + .clk_sel = npcm750_CLK_SEL, + .cfg_opt = NPCM_750_OPT, +}; + +static const struct npcm_clk_cfg npcm845_sgpio_pdata = { + .sft_clk = npcm845_SFT_CLK, + .clk_sel = npcm845_CLK_SEL, + .cfg_opt = NPCM_845_OPT, +}; + +static const struct of_device_id npcm_sgpio_of_table[] = { + { .compatible = "nuvoton,npcm750-sgpio", .data = &npcm750_sgpio_pdata, }, + { .compatible = "nuvoton,npcm845-sgpio", .data = &npcm845_sgpio_pdata, }, + {} +}; +MODULE_DEVICE_TABLE(of, npcm_sgpio_of_table); + +static struct platform_driver npcm_sgpio_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = npcm_sgpio_of_table, + }, + .probe = npcm_sgpio_probe, +}; +module_platform_driver(npcm_sgpio_driver); + +MODULE_AUTHOR("Jim Liu <jjliu0@nuvoton.com>"); +MODULE_AUTHOR("Joseph Liu <kwliu@nuvoton.com>"); +MODULE_DESCRIPTION("Nuvoton NPCM Serial GPIO Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 01c0fd0a9d8c..d9b228bea42e 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -151,8 +151,8 @@ static void sprd_pmic_eic_irq_mask(struct irq_data *data) struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip); u32 offset = irqd_to_hwirq(data); - pmic_eic->reg[REG_IE] = 0; - pmic_eic->reg[REG_TRIG] = 0; + pmic_eic->reg[REG_IE] &= ~BIT(offset); + pmic_eic->reg[REG_TRIG] &= ~BIT(offset); gpiochip_disable_irq(chip, offset); } @@ -165,8 +165,8 @@ static void sprd_pmic_eic_irq_unmask(struct irq_data *data) gpiochip_enable_irq(chip, offset); - pmic_eic->reg[REG_IE] = 1; - pmic_eic->reg[REG_TRIG] = 1; + pmic_eic->reg[REG_IE] |= BIT(offset); + pmic_eic->reg[REG_TRIG] |= BIT(offset); } static int sprd_pmic_eic_irq_set_type(struct irq_data *data, @@ -174,13 +174,14 @@ static int sprd_pmic_eic_irq_set_type(struct irq_data *data, { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip); + u32 offset = irqd_to_hwirq(data); switch (flow_type) { case IRQ_TYPE_LEVEL_HIGH: - pmic_eic->reg[REG_IEV] = 1; + pmic_eic->reg[REG_IEV] |= BIT(offset); break; case IRQ_TYPE_LEVEL_LOW: - pmic_eic->reg[REG_IEV] = 0; + pmic_eic->reg[REG_IEV] &= ~BIT(offset); break; case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_FALLING: @@ -222,15 +223,15 @@ static void sprd_pmic_eic_bus_sync_unlock(struct irq_data *data) sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, 1); } else { sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, - pmic_eic->reg[REG_IEV]); + !!(pmic_eic->reg[REG_IEV] & BIT(offset))); } /* Set irq unmask */ sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IE, - pmic_eic->reg[REG_IE]); + !!(pmic_eic->reg[REG_IE] & BIT(offset))); /* Generate trigger start pulse for debounce EIC */ sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_TRIG, - pmic_eic->reg[REG_TRIG]); + !!(pmic_eic->reg[REG_TRIG] & BIT(offset))); mutex_unlock(&pmic_eic->buslock); } diff --git a/drivers/gpio/gpio-rtd.c b/drivers/gpio/gpio-rtd.c new file mode 100644 index 000000000000..a7939bd0aa56 --- /dev/null +++ b/drivers/gpio/gpio-rtd.c @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Realtek DHC gpio driver + * + * Copyright (c) 2023 Realtek Semiconductor Corp. + */ + +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#define RTD_GPIO_DEBOUNCE_1US 0 +#define RTD_GPIO_DEBOUNCE_10US 1 +#define RTD_GPIO_DEBOUNCE_100US 2 +#define RTD_GPIO_DEBOUNCE_1MS 3 +#define RTD_GPIO_DEBOUNCE_10MS 4 +#define RTD_GPIO_DEBOUNCE_20MS 5 +#define RTD_GPIO_DEBOUNCE_30MS 6 + +/** + * struct rtd_gpio_info - Specific GPIO register information + * @name: GPIO device name + * @gpio_base: GPIO base number + * @num_gpios: The number of GPIOs + * @dir_offset: Offset for GPIO direction registers + * @dato_offset: Offset for GPIO data output registers + * @dati_offset: Offset for GPIO data input registers + * @ie_offset: Offset for GPIO interrupt enable registers + * @dp_offset: Offset for GPIO detection polarity registers + * @gpa_offset: Offset for GPIO assert interrupt status registers + * @gpda_offset: Offset for GPIO deassert interrupt status registers + * @deb_offset: Offset for GPIO debounce registers + * @deb_val: Register values representing the GPIO debounce time + * @get_deb_setval: Used to get the corresponding value for setting the debounce register + */ +struct rtd_gpio_info { + const char *name; + unsigned int gpio_base; + unsigned int num_gpios; + u8 *dir_offset; + u8 *dato_offset; + u8 *dati_offset; + u8 *ie_offset; + u8 *dp_offset; + u8 *gpa_offset; + u8 *gpda_offset; + u8 *deb_offset; + u8 *deb_val; + u8 (*get_deb_setval)(const struct rtd_gpio_info *info, + unsigned int offset, u8 deb_index, + u8 *reg_offset, u8 *shift); +}; + +struct rtd_gpio { + struct gpio_chip gpio_chip; + const struct rtd_gpio_info *info; + void __iomem *base; + void __iomem *irq_base; + unsigned int irqs[2]; + raw_spinlock_t lock; +}; + +static u8 rtd_gpio_get_deb_setval(const struct rtd_gpio_info *info, unsigned int offset, + u8 deb_index, u8 *reg_offset, u8 *shift) +{ + *reg_offset = info->deb_offset[offset / 8]; + *shift = (offset % 8) * 4; + return info->deb_val[deb_index]; +} + +static u8 rtd1295_misc_gpio_get_deb_setval(const struct rtd_gpio_info *info, unsigned int offset, + u8 deb_index, u8 *reg_offset, u8 *shift) +{ + *reg_offset = info->deb_offset[0]; + *shift = (offset % 8) * 4; + return info->deb_val[deb_index]; +} + +static u8 rtd1295_iso_gpio_get_deb_setval(const struct rtd_gpio_info *info, unsigned int offset, + u8 deb_index, u8 *reg_offset, u8 *shift) +{ + *reg_offset = info->deb_offset[0]; + *shift = 0; + return info->deb_val[deb_index]; +} + +static const struct rtd_gpio_info rtd_iso_gpio_info = { + .name = "rtd_iso_gpio", + .gpio_base = 0, + .num_gpios = 82, + .dir_offset = (u8 []){ 0x0, 0x18, 0x2c }, + .dato_offset = (u8 []){ 0x4, 0x1c, 0x30 }, + .dati_offset = (u8 []){ 0x8, 0x20, 0x34 }, + .ie_offset = (u8 []){ 0xc, 0x24, 0x38 }, + .dp_offset = (u8 []){ 0x10, 0x28, 0x3c }, + .gpa_offset = (u8 []){ 0x8, 0xe0, 0x90 }, + .gpda_offset = (u8 []){ 0xc, 0xe4, 0x94 }, + .deb_offset = (u8 []){ 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c, + 0x60, 0x64, 0x68, 0x6c }, + .deb_val = (u8 []){ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }, + .get_deb_setval = rtd_gpio_get_deb_setval, +}; + +static const struct rtd_gpio_info rtd1619_iso_gpio_info = { + .name = "rtd1619_iso_gpio", + .gpio_base = 0, + .num_gpios = 86, + .dir_offset = (u8 []){ 0x0, 0x18, 0x2c }, + .dato_offset = (u8 []){ 0x4, 0x1c, 0x30 }, + .dati_offset = (u8 []){ 0x8, 0x20, 0x34 }, + .ie_offset = (u8 []){ 0xc, 0x24, 0x38 }, + .dp_offset = (u8 []){ 0x10, 0x28, 0x3c }, + .gpa_offset = (u8 []){ 0x8, 0xe0, 0x90 }, + .gpda_offset = (u8 []){ 0xc, 0xe4, 0x94 }, + .deb_offset = (u8 []){ 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c, + 0x60, 0x64, 0x68, 0x6c }, + .deb_val = (u8 []){ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }, + .get_deb_setval = rtd_gpio_get_deb_setval, +}; + +static const struct rtd_gpio_info rtd1395_iso_gpio_info = { + .name = "rtd1395_iso_gpio", + .gpio_base = 0, + .num_gpios = 57, + .dir_offset = (u8 []){ 0x0, 0x18 }, + .dato_offset = (u8 []){ 0x4, 0x1c }, + .dati_offset = (u8 []){ 0x8, 0x20 }, + .ie_offset = (u8 []){ 0xc, 0x24 }, + .dp_offset = (u8 []){ 0x10, 0x28 }, + .gpa_offset = (u8 []){ 0x8, 0xe0 }, + .gpda_offset = (u8 []){ 0xc, 0xe4 }, + .deb_offset = (u8 []){ 0x30, 0x34, 0x38, 0x3c, 0x40, 0x44, 0x48, 0x4c }, + .deb_val = (u8 []){ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }, + .get_deb_setval = rtd_gpio_get_deb_setval, +}; + +static const struct rtd_gpio_info rtd1295_misc_gpio_info = { + .name = "rtd1295_misc_gpio", + .gpio_base = 0, + .num_gpios = 101, + .dir_offset = (u8 []){ 0x0, 0x4, 0x8, 0xc }, + .dato_offset = (u8 []){ 0x10, 0x14, 0x18, 0x1c }, + .dati_offset = (u8 []){ 0x20, 0x24, 0x28, 0x2c }, + .ie_offset = (u8 []){ 0x30, 0x34, 0x38, 0x3c }, + .dp_offset = (u8 []){ 0x40, 0x44, 0x48, 0x4c }, + .gpa_offset = (u8 []){ 0x40, 0x44, 0xa4, 0xb8 }, + .gpda_offset = (u8 []){ 0x54, 0x58, 0xa8, 0xbc}, + .deb_offset = (u8 []){ 0x50 }, + .deb_val = (u8 []){ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }, + .get_deb_setval = rtd1295_misc_gpio_get_deb_setval, +}; + +static const struct rtd_gpio_info rtd1295_iso_gpio_info = { + .name = "rtd1295_iso_gpio", + .gpio_base = 101, + .num_gpios = 35, + .dir_offset = (u8 []){ 0x0, 0x18 }, + .dato_offset = (u8 []){ 0x4, 0x1c }, + .dati_offset = (u8 []){ 0x8, 0x20 }, + .ie_offset = (u8 []){ 0xc, 0x24 }, + .dp_offset = (u8 []){ 0x10, 0x28 }, + .gpa_offset = (u8 []){ 0x8, 0xe0 }, + .gpda_offset = (u8 []){ 0xc, 0xe4 }, + .deb_offset = (u8 []){ 0x14 }, + .deb_val = (u8 []){ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }, + .get_deb_setval = rtd1295_iso_gpio_get_deb_setval, +}; + +static int rtd_gpio_dir_offset(struct rtd_gpio *data, unsigned int offset) +{ + return data->info->dir_offset[offset / 32]; +} + +static int rtd_gpio_dato_offset(struct rtd_gpio *data, unsigned int offset) +{ + return data->info->dato_offset[offset / 32]; +} + +static int rtd_gpio_dati_offset(struct rtd_gpio *data, unsigned int offset) +{ + return data->info->dati_offset[offset / 32]; +} + +static int rtd_gpio_ie_offset(struct rtd_gpio *data, unsigned int offset) +{ + return data->info->ie_offset[offset / 32]; +} + +static int rtd_gpio_dp_offset(struct rtd_gpio *data, unsigned int offset) +{ + return data->info->dp_offset[offset / 32]; +} + + +static int rtd_gpio_gpa_offset(struct rtd_gpio *data, unsigned int offset) +{ + /* Each GPIO assert interrupt status register contains 31 GPIOs. */ + return data->info->gpa_offset[offset / 31]; +} + +static int rtd_gpio_gpda_offset(struct rtd_gpio *data, unsigned int offset) +{ + /* Each GPIO deassert interrupt status register contains 31 GPIOs. */ + return data->info->gpda_offset[offset / 31]; +} + +static int rtd_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, + unsigned int debounce) +{ + struct rtd_gpio *data = gpiochip_get_data(chip); + u8 deb_val, deb_index, reg_offset, shift; + unsigned int write_en; + u32 val; + + switch (debounce) { + case 1: + deb_index = RTD_GPIO_DEBOUNCE_1US; + break; + case 10: + deb_index = RTD_GPIO_DEBOUNCE_10US; + break; + case 100: + deb_index = RTD_GPIO_DEBOUNCE_100US; + break; + case 1000: + deb_index = RTD_GPIO_DEBOUNCE_1MS; + break; + case 10000: + deb_index = RTD_GPIO_DEBOUNCE_10MS; + break; + case 20000: + deb_index = RTD_GPIO_DEBOUNCE_20MS; + break; + case 30000: + deb_index = RTD_GPIO_DEBOUNCE_30MS; + break; + default: + return -ENOTSUPP; + } + + deb_val = data->info->get_deb_setval(data->info, offset, deb_index, ®_offset, &shift); + write_en = BIT(shift + 3); + val = (deb_val << shift) | write_en; + + guard(raw_spinlock_irqsave)(&data->lock); + writel_relaxed(val, data->base + reg_offset); + + return 0; +} + +static int rtd_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + int debounce; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + return gpiochip_generic_config(chip, offset, config); + case PIN_CONFIG_INPUT_DEBOUNCE: + debounce = pinconf_to_config_argument(config); + return rtd_gpio_set_debounce(chip, offset, debounce); + default: + return -ENOTSUPP; + } +} + +static void rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct rtd_gpio *data = gpiochip_get_data(chip); + u32 mask = BIT(offset % 32); + int dato_reg_offset; + u32 val; + + dato_reg_offset = rtd_gpio_dato_offset(data, offset); + + guard(raw_spinlock_irqsave)(&data->lock); + + val = readl_relaxed(data->base + dato_reg_offset); + if (value) + val |= mask; + else + val &= ~mask; + writel_relaxed(val, data->base + dato_reg_offset); +} + +static int rtd_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct rtd_gpio *data = gpiochip_get_data(chip); + int dato_reg_offset = rtd_gpio_dato_offset(data, offset); + int dati_reg_offset = rtd_gpio_dati_offset(data, offset); + int dir_reg_offset = rtd_gpio_dir_offset(data, offset); + int dat_reg_offset; + u32 val; + + guard(raw_spinlock_irqsave)(&data->lock); + + val = readl_relaxed(data->base + dir_reg_offset); + dat_reg_offset = (val & BIT(offset % 32)) ? dato_reg_offset : dati_reg_offset; + val = readl_relaxed(data->base + dat_reg_offset); + + return !!(val & BIT(offset % 32)); +} + +static int rtd_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct rtd_gpio *data = gpiochip_get_data(chip); + int reg_offset; + u32 val; + + reg_offset = rtd_gpio_dir_offset(data, offset); + val = readl_relaxed(data->base + reg_offset); + if (val & BIT(offset % 32)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int rtd_gpio_set_direction(struct gpio_chip *chip, unsigned int offset, bool out) +{ + struct rtd_gpio *data = gpiochip_get_data(chip); + u32 mask = BIT(offset % 32); + int reg_offset; + u32 val; + + reg_offset = rtd_gpio_dir_offset(data, offset); + + guard(raw_spinlock_irqsave)(&data->lock); + + val = readl_relaxed(data->base + reg_offset); + if (out) + val |= mask; + else + val &= ~mask; + writel_relaxed(val, data->base + reg_offset); + + return 0; +} + +static int rtd_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + return rtd_gpio_set_direction(chip, offset, false); +} + +static int rtd_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) +{ + rtd_gpio_set(chip, offset, value); + + return rtd_gpio_set_direction(chip, offset, true); +} + +static bool rtd_gpio_check_ie(struct rtd_gpio *data, int irq) +{ + int mask = BIT(irq % 32); + int ie_reg_offset; + u32 enable; + + ie_reg_offset = rtd_gpio_ie_offset(data, irq); + enable = readl_relaxed(data->base + ie_reg_offset); + + return enable & mask; +} + +static void rtd_gpio_irq_handle(struct irq_desc *desc) +{ + int (*get_reg_offset)(struct rtd_gpio *gpio, unsigned int offset); + struct rtd_gpio *data = irq_desc_get_handler_data(desc); + struct irq_domain *domain = data->gpio_chip.irq.domain; + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int irq = irq_desc_get_irq(desc); + unsigned long status; + int reg_offset, i, j; + unsigned int hwirq; + + if (irq == data->irqs[0]) + get_reg_offset = &rtd_gpio_gpa_offset; + else if (irq == data->irqs[1]) + get_reg_offset = &rtd_gpio_gpda_offset; + + chained_irq_enter(chip, desc); + + /* Each GPIO interrupt status register contains 31 GPIOs. */ + for (i = 0; i < data->info->num_gpios; i += 31) { + reg_offset = get_reg_offset(data, i); + + /* + * Bit 0 is the write_en bit, bit 0 to 31 corresponds to 31 GPIOs. + * When bit 0 is set to 0, write 1 to the other bits to clear the status. + * When bit 0 is set to 1, write 1 to the other bits to set the status. + */ + status = readl_relaxed(data->irq_base + reg_offset); + status &= ~BIT(0); + writel_relaxed(status, data->irq_base + reg_offset); + + for_each_set_bit(j, &status, 32) { + hwirq = i + j - 1; + if (rtd_gpio_check_ie(data, hwirq)) { + int girq = irq_find_mapping(domain, hwirq); + u32 irq_type = irq_get_trigger_type(girq); + + if ((irq == data->irqs[1]) && (irq_type != IRQ_TYPE_EDGE_BOTH)) + break; + generic_handle_domain_irq(domain, hwirq); + } + } + } + + chained_irq_exit(chip, desc); +} + +static void rtd_gpio_enable_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct rtd_gpio *data = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + /* Bit 0 is write_en and bit 1 to 31 is correspond to 31 GPIOs. */ + u32 clr_mask = BIT(hwirq % 31) << 1; + + u32 ie_mask = BIT(hwirq % 32); + int gpda_reg_offset; + int gpa_reg_offset; + int ie_reg_offset; + u32 val; + + ie_reg_offset = rtd_gpio_ie_offset(data, hwirq); + gpa_reg_offset = rtd_gpio_gpa_offset(data, hwirq); + gpda_reg_offset = rtd_gpio_gpda_offset(data, hwirq); + + gpiochip_enable_irq(gc, hwirq); + + guard(raw_spinlock_irqsave)(&data->lock); + + writel_relaxed(clr_mask, data->irq_base + gpa_reg_offset); + writel_relaxed(clr_mask, data->irq_base + gpda_reg_offset); + + val = readl_relaxed(data->base + ie_reg_offset); + val |= ie_mask; + writel_relaxed(val, data->base + ie_reg_offset); +} + +static void rtd_gpio_disable_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct rtd_gpio *data = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 ie_mask = BIT(hwirq % 32); + int ie_reg_offset; + u32 val; + + ie_reg_offset = rtd_gpio_ie_offset(data, hwirq); + + scoped_guard(raw_spinlock_irqsave, &data->lock) { + val = readl_relaxed(data->base + ie_reg_offset); + val &= ~ie_mask; + writel_relaxed(val, data->base + ie_reg_offset); + } + + gpiochip_disable_irq(gc, hwirq); +} + +static int rtd_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct rtd_gpio *data = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 mask = BIT(hwirq % 32); + int dp_reg_offset; + bool polarity; + u32 val; + + dp_reg_offset = rtd_gpio_dp_offset(data, hwirq); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + polarity = 1; + break; + + case IRQ_TYPE_EDGE_FALLING: + polarity = 0; + break; + + case IRQ_TYPE_EDGE_BOTH: + polarity = 1; + break; + + default: + return -EINVAL; + } + + scoped_guard(raw_spinlock_irqsave, &data->lock) { + val = readl_relaxed(data->base + dp_reg_offset); + if (polarity) + val |= mask; + else + val &= ~mask; + writel_relaxed(val, data->base + dp_reg_offset); + } + + irq_set_handler_locked(d, handle_simple_irq); + + return 0; +} + +static const struct irq_chip rtd_gpio_irq_chip = { + .name = "rtd-gpio", + .irq_enable = rtd_gpio_enable_irq, + .irq_disable = rtd_gpio_disable_irq, + .irq_set_type = rtd_gpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, +}; + +static int rtd_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_irq_chip *irq_chip; + struct rtd_gpio *data; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->irqs[0] = platform_get_irq(pdev, 0); + if (data->irqs[0] < 0) + return data->irqs[0]; + + data->irqs[1] = platform_get_irq(pdev, 1); + if (data->irqs[1] < 0) + return data->irqs[1]; + + data->info = device_get_match_data(dev); + if (!data->info) + return -EINVAL; + + raw_spin_lock_init(&data->lock); + + data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->irq_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(data->irq_base)) + return PTR_ERR(data->irq_base); + + data->gpio_chip.label = dev_name(dev); + data->gpio_chip.base = -1; + data->gpio_chip.ngpio = data->info->num_gpios; + data->gpio_chip.request = gpiochip_generic_request; + data->gpio_chip.free = gpiochip_generic_free; + data->gpio_chip.get_direction = rtd_gpio_get_direction; + data->gpio_chip.direction_input = rtd_gpio_direction_input; + data->gpio_chip.direction_output = rtd_gpio_direction_output; + data->gpio_chip.set = rtd_gpio_set; + data->gpio_chip.get = rtd_gpio_get; + data->gpio_chip.set_config = rtd_gpio_set_config; + data->gpio_chip.parent = dev; + + irq_chip = &data->gpio_chip.irq; + irq_chip->handler = handle_bad_irq; + irq_chip->default_type = IRQ_TYPE_NONE; + irq_chip->parent_handler = rtd_gpio_irq_handle; + irq_chip->parent_handler_data = data; + irq_chip->num_parents = 2; + irq_chip->parents = data->irqs; + + gpio_irq_chip_set_chip(irq_chip, &rtd_gpio_irq_chip); + + return devm_gpiochip_add_data(dev, &data->gpio_chip, data); +} + +static const struct of_device_id rtd_gpio_of_matches[] = { + { .compatible = "realtek,rtd1295-misc-gpio", .data = &rtd1295_misc_gpio_info }, + { .compatible = "realtek,rtd1295-iso-gpio", .data = &rtd1295_iso_gpio_info }, + { .compatible = "realtek,rtd1395-iso-gpio", .data = &rtd1395_iso_gpio_info }, + { .compatible = "realtek,rtd1619-iso-gpio", .data = &rtd1619_iso_gpio_info }, + { .compatible = "realtek,rtd1319-iso-gpio", .data = &rtd_iso_gpio_info }, + { .compatible = "realtek,rtd1619b-iso-gpio", .data = &rtd_iso_gpio_info }, + { .compatible = "realtek,rtd1319d-iso-gpio", .data = &rtd_iso_gpio_info }, + { .compatible = "realtek,rtd1315e-iso-gpio", .data = &rtd_iso_gpio_info }, + { } +}; +MODULE_DEVICE_TABLE(of, rtd_gpio_of_matches); + +static struct platform_driver rtd_gpio_platform_driver = { + .driver = { + .name = "gpio-rtd", + .of_match_table = rtd_gpio_of_matches, + }, + .probe = rtd_gpio_probe, +}; +module_platform_driver(rtd_gpio_platform_driver); + +MODULE_DESCRIPTION("Realtek DHC SoC gpio driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index 8decd9b5d229..067c8edb62e2 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -250,7 +250,6 @@ static int sifive_gpio_probe(struct platform_device *pdev) girq->handler = handle_bad_irq; girq->default_type = IRQ_TYPE_NONE; - platform_set_drvdata(pdev, chip); return gpiochip_add_data(&chip->gc, chip); } diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 1928209491e1..c4106e37e6db 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -20,6 +20,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irq_sim.h> +#include <linux/kernel.h> #include <linux/list.h> #include <linux/minmax.h> #include <linux/mod_devicetable.h> @@ -28,6 +29,7 @@ #include <linux/notifier.h> #include <linux/platform_device.h> #include <linux/property.h> +#include <linux/seq_file.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/string_helpers.h> @@ -224,6 +226,25 @@ static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset) } } +static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + const char *label; + int i; + + guard(mutex)(&chip->lock); + + for_each_requested_gpio(gc, i, label) + seq_printf(seq, " gpio-%-3d (%s) %s,%s\n", + gc->base + i, + label, + test_bit(i, chip->direction_map) ? "input" : + test_bit(i, chip->value_map) ? "output-high" : + "output-low", + test_bit(i, chip->pull_map) ? "pull-up" : + "pull-down"); +} + static ssize_t gpio_sim_sysfs_val_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -460,6 +481,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) gc->to_irq = gpio_sim_to_irq; gc->request = gpio_sim_request; gc->free = gpio_sim_free; + gc->dbg_show = PTR_IF(IS_ENABLED(CONFIG_DEBUG_FS), gpio_sim_dbg_show); gc->can_sleep = true; ret = devm_gpiochip_add_data(dev, gc, chip); @@ -1546,6 +1568,6 @@ static void __exit gpio_sim_exit(void) } module_exit(gpio_sim_exit); -MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl"); +MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>"); MODULE_DESCRIPTION("GPIO Simulator Module"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 27cc4da53565..6c5ee81d71b3 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -5,6 +5,7 @@ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson */ +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -255,7 +256,6 @@ static void stmpe_dbg_show_one(struct seq_file *s, { struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); struct stmpe *stmpe = stmpe_gpio->stmpe; - const char *label = gpiochip_is_requested(gc, offset); bool val = !!stmpe_gpio_get(gc, offset); u8 bank = offset / 8; u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB + bank]; @@ -263,6 +263,10 @@ static void stmpe_dbg_show_one(struct seq_file *s, int ret; u8 dir; + char *label __free(kfree) = gpiochip_dup_line_label(gc, offset); + if (IS_ERR(label)) + return; + ret = stmpe_reg_read(stmpe, dir_reg); if (ret < 0) return; diff --git a/drivers/gpio/gpio-tangier.c b/drivers/gpio/gpio-tangier.c index 7ce3eddaed25..b75e0b12087a 100644 --- a/drivers/gpio/gpio-tangier.c +++ b/drivers/gpio/gpio-tangier.c @@ -10,6 +10,7 @@ */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/export.h> @@ -19,6 +20,7 @@ #include <linux/math.h> #include <linux/module.h> #include <linux/pinctrl/pinconf-generic.h> +#include <linux/pm.h> #include <linux/spinlock.h> #include <linux/string_helpers.h> #include <linux/types.h> @@ -91,37 +93,31 @@ static int tng_gpio_get(struct gpio_chip *chip, unsigned int offset) static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct tng_gpio *priv = gpiochip_get_data(chip); - unsigned long flags; void __iomem *reg; u8 shift; reg = gpio_reg_and_bit(chip, offset, value ? GPSR : GPCR, &shift); - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); writel(BIT(shift), reg); - - raw_spin_unlock_irqrestore(&priv->lock, flags); } static int tng_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tng_gpio *priv = gpiochip_get_data(chip); - unsigned long flags; void __iomem *gpdr; u32 value; u8 shift; gpdr = gpio_reg_and_bit(chip, offset, GPDR, &shift); - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); value = readl(gpdr); value &= ~BIT(shift); writel(value, gpdr); - raw_spin_unlock_irqrestore(&priv->lock, flags); - return 0; } @@ -129,21 +125,18 @@ static int tng_gpio_direction_output(struct gpio_chip *chip, unsigned int offset int value) { struct tng_gpio *priv = gpiochip_get_data(chip); - unsigned long flags; void __iomem *gpdr; u8 shift; gpdr = gpio_reg_and_bit(chip, offset, GPDR, &shift); tng_gpio_set(chip, offset, value); - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); value = readl(gpdr); value |= BIT(shift); writel(value, gpdr); - raw_spin_unlock_irqrestore(&priv->lock, flags); - return 0; } @@ -164,14 +157,13 @@ static int tng_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, unsigned int debounce) { struct tng_gpio *priv = gpiochip_get_data(chip); - unsigned long flags; void __iomem *gfbr; u32 value; u8 shift; gfbr = gpio_reg_and_bit(chip, offset, GFBR, &shift); - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); value = readl(gfbr); if (debounce) @@ -180,8 +172,6 @@ static int tng_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, value |= BIT(shift); writel(value, gfbr); - raw_spin_unlock_irqrestore(&priv->lock, flags); - return 0; } @@ -207,27 +197,25 @@ static void tng_irq_ack(struct irq_data *d) { struct tng_gpio *priv = irq_data_get_irq_chip_data(d); irq_hw_number_t gpio = irqd_to_hwirq(d); - unsigned long flags; void __iomem *gisr; u8 shift; gisr = gpio_reg_and_bit(&priv->chip, gpio, GISR, &shift); - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); + writel(BIT(shift), gisr); - raw_spin_unlock_irqrestore(&priv->lock, flags); } static void tng_irq_unmask_mask(struct tng_gpio *priv, u32 gpio, bool unmask) { - unsigned long flags; void __iomem *gimr; u32 value; u8 shift; gimr = gpio_reg_and_bit(&priv->chip, gpio, GIMR, &shift); - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); value = readl(gimr); if (unmask) @@ -235,8 +223,6 @@ static void tng_irq_unmask_mask(struct tng_gpio *priv, u32 gpio, bool unmask) else value &= ~BIT(shift); writel(value, gimr); - - raw_spin_unlock_irqrestore(&priv->lock, flags); } static void tng_irq_mask(struct irq_data *d) @@ -267,10 +253,9 @@ static int tng_irq_set_type(struct irq_data *d, unsigned int type) void __iomem *gitr = gpio_reg(&priv->chip, gpio, GITR); void __iomem *glpr = gpio_reg(&priv->chip, gpio, GLPR); u8 shift = gpio % 32; - unsigned long flags; u32 value; - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); value = readl(grer); if (type & IRQ_TYPE_EDGE_RISING) @@ -311,8 +296,6 @@ static int tng_irq_set_type(struct irq_data *d, unsigned int type) irq_set_handler_locked(d, handle_edge_irq); } - raw_spin_unlock_irqrestore(&priv->lock, flags); - return 0; } @@ -324,10 +307,11 @@ static int tng_irq_set_wake(struct irq_data *d, unsigned int on) void __iomem *gwmr = gpio_reg(&priv->chip, gpio, priv->wake_regs.gwmr); void __iomem *gwsr = gpio_reg(&priv->chip, gpio, priv->wake_regs.gwsr); u8 shift = gpio % 32; - unsigned long flags; u32 value; - raw_spin_lock_irqsave(&priv->lock, flags); + dev_dbg(priv->dev, "%s wake for gpio %lu\n", str_enable_disable(on), gpio); + + guard(raw_spinlock_irqsave)(&priv->lock); /* Clear the existing wake status */ writel(BIT(shift), gwsr); @@ -339,9 +323,6 @@ static int tng_irq_set_wake(struct irq_data *d, unsigned int on) value &= ~BIT(shift); writel(value, gwmr); - raw_spin_unlock_irqrestore(&priv->lock, flags); - - dev_dbg(priv->dev, "%s wake for gpio %lu\n", str_enable_disable(on), gpio); return 0; } @@ -477,14 +458,13 @@ int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio) } EXPORT_SYMBOL_NS_GPL(devm_tng_gpio_probe, GPIO_TANGIER); -int tng_gpio_suspend(struct device *dev) +static int tng_gpio_suspend(struct device *dev) { struct tng_gpio *priv = dev_get_drvdata(dev); struct tng_gpio_context *ctx = priv->ctx; - unsigned long flags; unsigned int base; - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); for (base = 0; base < priv->chip.ngpio; base += 32, ctx++) { /* GPLR is RO, values read will be restored using GPSR */ @@ -498,20 +478,16 @@ int tng_gpio_suspend(struct device *dev) ctx->gwmr = readl(gpio_reg(&priv->chip, base, priv->wake_regs.gwmr)); } - raw_spin_unlock_irqrestore(&priv->lock, flags); - return 0; } -EXPORT_SYMBOL_NS_GPL(tng_gpio_suspend, GPIO_TANGIER); -int tng_gpio_resume(struct device *dev) +static int tng_gpio_resume(struct device *dev) { struct tng_gpio *priv = dev_get_drvdata(dev); struct tng_gpio_context *ctx = priv->ctx; - unsigned long flags; unsigned int base; - raw_spin_lock_irqsave(&priv->lock, flags); + guard(raw_spinlock_irqsave)(&priv->lock); for (base = 0; base < priv->chip.ngpio; base += 32, ctx++) { /* GPLR is RO, values read will be restored using GPSR */ @@ -525,11 +501,10 @@ int tng_gpio_resume(struct device *dev) writel(ctx->gwmr, gpio_reg(&priv->chip, base, priv->wake_regs.gwmr)); } - raw_spin_unlock_irqrestore(&priv->lock, flags); - return 0; } -EXPORT_SYMBOL_NS_GPL(tng_gpio_resume, GPIO_TANGIER); + +EXPORT_NS_GPL_SIMPLE_DEV_PM_OPS(tng_gpio_pm_ops, tng_gpio_suspend, tng_gpio_resume, GPIO_TANGIER); MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_AUTHOR("Pandith N <pandith.n@intel.com>"); diff --git a/drivers/gpio/gpio-tangier.h b/drivers/gpio/gpio-tangier.h index 16c4f22908fb..ca7ab6cf6fa5 100644 --- a/drivers/gpio/gpio-tangier.h +++ b/drivers/gpio/gpio-tangier.h @@ -13,6 +13,7 @@ #define _GPIO_TANGIER_H_ #include <linux/gpio/driver.h> +#include <linux/pm.h> #include <linux/spinlock_types.h> #include <linux/types.h> @@ -111,7 +112,6 @@ struct tng_gpio { int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio); -int tng_gpio_suspend(struct device *dev); -int tng_gpio_resume(struct device *dev); +extern const struct dev_pm_ops tng_gpio_pm_ops; #endif /* _GPIO_TANGIER_H_ */ diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index 7b38aa360112..cd1f17041f8c 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -96,16 +96,16 @@ static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int off * Below can be used for test purpose only. */ - if (IS_ENABLED(CONFIG_DEBUG_GPIO)) { - int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, - TPS65219_GPIO0_DIR_MASK, direction); - if (ret) { - dev_err(dev, - "GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n", - direction, offset); - return ret; - } +#if 0 + int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, + TPS65219_GPIO0_DIR_MASK, direction); + if (ret) { + dev_err(dev, + "GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n", + direction, offset); + return ret; } +#endif dev_err(dev, "GPIO%d direction set by NVM, change to %u failed, not allowed by specification\n", diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 7eaf8a28638c..f7d5120ff8f1 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -8,6 +8,7 @@ * */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> @@ -160,18 +161,21 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) for (i = 0; i < chip->ngpio; i++) { int gpio = i + chip->base; int reg; - const char *label, *pull, *powerdomain; + const char *pull, *powerdomain; /* We report the GPIO even if it's not requested since * we're also reporting things like alternate * functions which apply even when the GPIO is not in * use as a GPIO. */ - label = gpiochip_is_requested(chip, i); - if (!label) - label = "Unrequested"; + char *label __free(kfree) = gpiochip_dup_line_label(chip, i); + if (IS_ERR(label)) { + dev_err(wm831x->dev, "Failed to duplicate label\n"); + continue; + } - seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); + seq_printf(s, " gpio-%-3d (%-20.20s) ", + gpio, label ?: "Unrequested"); reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i); if (reg < 0) { diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index f4a474cef32d..bf05c9b5882b 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -8,6 +8,7 @@ * */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> @@ -193,18 +194,20 @@ static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) for (i = 0; i < chip->ngpio; i++) { int gpio = i + chip->base; int reg; - const char *label; /* We report the GPIO even if it's not requested since * we're also reporting things like alternate * functions which apply even when the GPIO is not in * use as a GPIO. */ - label = gpiochip_is_requested(chip, i); - if (!label) - label = "Unrequested"; + char *label __free(kfree) = gpiochip_dup_line_label(chip, i); + if (IS_ERR(label)) { + dev_err(wm8994->dev, "Failed to duplicate label\n"); + continue; + } - seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); + seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, + label ?: "Unrequested"); reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i); if (reg < 0) { diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 823198368250..7348df385198 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -52,7 +52,6 @@ * @dir: GPIO direction shadow register * @gpio_lock: Lock used for synchronization * @irq: IRQ used by GPIO device - * @irqchip: IRQ chip * @enable: GPIO IRQ enable/disable bitfield * @rising_edge: GPIO IRQ rising edge enable/disable bitfield * @falling_edge: GPIO IRQ falling edge enable/disable bitfield diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index f713d1ef7746..2a88736629ef 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -5,6 +5,7 @@ #include <linux/bitmap.h> #include <linux/build_bug.h> #include <linux/cdev.h> +#include <linux/cleanup.h> #include <linux/compat.h> #include <linux/compiler.h> #include <linux/device.h> @@ -19,8 +20,11 @@ #include <linux/kfifo.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/overflow.h> #include <linux/pinctrl/consumer.h> #include <linux/poll.h> +#include <linux/rbtree.h> +#include <linux/rwsem.h> #include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/timekeeping.h> @@ -62,45 +66,6 @@ typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long); typedef ssize_t (*read_fn)(struct file *, char __user *, size_t count, loff_t *); -static __poll_t call_poll_locked(struct file *file, - struct poll_table_struct *wait, - struct gpio_device *gdev, poll_fn func) -{ - __poll_t ret; - - down_read(&gdev->sem); - ret = func(file, wait); - up_read(&gdev->sem); - - return ret; -} - -static long call_ioctl_locked(struct file *file, unsigned int cmd, - unsigned long arg, struct gpio_device *gdev, - ioctl_fn func) -{ - long ret; - - down_read(&gdev->sem); - ret = func(file, cmd, arg); - up_read(&gdev->sem); - - return ret; -} - -static ssize_t call_read_locked(struct file *file, char __user *buf, - size_t count, loff_t *f_ps, - struct gpio_device *gdev, read_fn func) -{ - ssize_t ret; - - down_read(&gdev->sem); - ret = func(file, buf, count, f_ps); - up_read(&gdev->sem); - - return ret; -} - /* * GPIO line handle management */ @@ -235,8 +200,8 @@ static long linehandle_set_config(struct linehandle_state *lh, return 0; } -static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd, - unsigned long arg) +static long linehandle_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct linehandle_state *lh = file->private_data; void __user *ip = (void __user *)arg; @@ -245,6 +210,8 @@ static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned int i; int ret; + guard(rwsem_read)(&lh->gdev->sem); + if (!lh->gdev->chip) return -ENODEV; @@ -294,15 +261,6 @@ static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd, } } -static long linehandle_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct linehandle_state *lh = file->private_data; - - return call_ioctl_locked(file, cmd, arg, lh->gdev, - linehandle_ioctl_unlocked); -} - #ifdef CONFIG_COMPAT static long linehandle_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -461,6 +419,7 @@ out_free_lh: /** * struct line - contains the state of a requested line + * @node: to store the object in supinfo_tree if supplemental * @desc: the GPIO descriptor for this line. * @req: the corresponding line request * @irq: the interrupt triggered in response to events on this GPIO @@ -473,6 +432,7 @@ out_free_lh: * @line_seqno: the seqno for the current edge event in the sequence of * events for this line. * @work: the worker that implements software debouncing + * @debounce_period_us: the debounce period in microseconds * @sw_debounced: flag indicating if the software debouncer is active * @level: the current debounced physical level of the line * @hdesc: the Hardware Timestamp Engine (HTE) descriptor @@ -481,6 +441,7 @@ out_free_lh: * @last_seqno: the last sequence number before debounce period expires */ struct line { + struct rb_node node; struct gpio_desc *desc; /* * -- edge detector specific fields -- @@ -515,6 +476,15 @@ struct line { */ struct delayed_work work; /* + * debounce_period_us is accessed by debounce_irq_handler() and + * process_hw_ts() which are disabled when modified by + * debounce_setup(), edge_detector_setup() or edge_detector_stop() + * or can live with a stale version when updated by + * edge_detector_update(). + * The modifying functions are themselves mutually exclusive. + */ + unsigned int debounce_period_us; + /* * sw_debounce is accessed by linereq_set_config(), which is the * only setter, and linereq_get_values(), which can live with a * slightly stale value. @@ -546,6 +516,17 @@ struct line { #endif /* CONFIG_HTE */ }; +/* + * a rbtree of the struct lines containing supplemental info. + * Used to populate gpio_v2_line_info with cdev specific fields not contained + * in the struct gpio_desc. + * A line is determined to contain supplemental information by + * line_has_supinfo(). + */ +static struct rb_root supinfo_tree = RB_ROOT; +/* covers supinfo_tree */ +static DEFINE_SPINLOCK(supinfo_lock); + /** * struct linereq - contains the state of a userspace line request * @gdev: the GPIO device the line request pertains to @@ -559,7 +540,8 @@ struct line { * this line request. Note that this is not used when @num_lines is 1, as * the line_seqno is then the same and is cheaper to calculate. * @config_mutex: mutex for serializing ioctl() calls to ensure consistency - * of configuration, particularly multi-step accesses to desc flags. + * of configuration, particularly multi-step accesses to desc flags and + * changes to supinfo status. * @lines: the lines held by this line request, with @num_lines elements. */ struct linereq { @@ -575,6 +557,103 @@ struct linereq { struct line lines[] __counted_by(num_lines); }; +static void supinfo_insert(struct line *line) +{ + struct rb_node **new = &(supinfo_tree.rb_node), *parent = NULL; + struct line *entry; + + guard(spinlock)(&supinfo_lock); + + while (*new) { + entry = container_of(*new, struct line, node); + + parent = *new; + if (line->desc < entry->desc) { + new = &((*new)->rb_left); + } else if (line->desc > entry->desc) { + new = &((*new)->rb_right); + } else { + /* this should never happen */ + WARN(1, "duplicate line inserted"); + return; + } + } + + rb_link_node(&line->node, parent, new); + rb_insert_color(&line->node, &supinfo_tree); +} + +static void supinfo_erase(struct line *line) +{ + guard(spinlock)(&supinfo_lock); + + rb_erase(&line->node, &supinfo_tree); +} + +static struct line *supinfo_find(struct gpio_desc *desc) +{ + struct rb_node *node = supinfo_tree.rb_node; + struct line *line; + + while (node) { + line = container_of(node, struct line, node); + if (desc < line->desc) + node = node->rb_left; + else if (desc > line->desc) + node = node->rb_right; + else + return line; + } + return NULL; +} + +static void supinfo_to_lineinfo(struct gpio_desc *desc, + struct gpio_v2_line_info *info) +{ + struct gpio_v2_line_attribute *attr; + struct line *line; + + guard(spinlock)(&supinfo_lock); + + line = supinfo_find(desc); + if (!line) + return; + + attr = &info->attrs[info->num_attrs]; + attr->id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; + attr->debounce_period_us = READ_ONCE(line->debounce_period_us); + info->num_attrs++; +} + +static inline bool line_has_supinfo(struct line *line) +{ + return READ_ONCE(line->debounce_period_us); +} + +/* + * Checks line_has_supinfo() before and after the change to avoid unnecessary + * supinfo_tree access. + * Called indirectly by linereq_create() or linereq_set_config() so line + * is already protected from concurrent changes. + */ +static void line_set_debounce_period(struct line *line, + unsigned int debounce_period_us) +{ + bool was_suppl = line_has_supinfo(line); + + WRITE_ONCE(line->debounce_period_us, debounce_period_us); + + /* if supinfo status is unchanged then we're done */ + if (line_has_supinfo(line) == was_suppl) + return; + + /* supinfo status has changed, so update the tree */ + if (was_suppl) + supinfo_erase(line); + else + supinfo_insert(line); +} + #define GPIO_V2_LINE_BIAS_FLAGS \ (GPIO_V2_LINE_FLAG_BIAS_PULL_UP | \ GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN | \ @@ -625,13 +704,13 @@ static void linereq_put_event(struct linereq *lr, { bool overflow = false; - spin_lock(&lr->wait.lock); - if (kfifo_is_full(&lr->events)) { - overflow = true; - kfifo_skip(&lr->events); + scoped_guard(spinlock, &lr->wait.lock) { + if (kfifo_is_full(&lr->events)) { + overflow = true; + kfifo_skip(&lr->events); + } + kfifo_in(&lr->events, le, 1); } - kfifo_in(&lr->events, le, 1); - spin_unlock(&lr->wait.lock); if (!overflow) wake_up_poll(&lr->wait, EPOLLIN); else @@ -723,7 +802,7 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) line->total_discard_seq++; line->last_seqno = ts->seq; mod_delayed_work(system_wq, &line->work, - usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); + usecs_to_jiffies(READ_ONCE(line->debounce_period_us))); } else { if (unlikely(ts->seq < line->line_seqno)) return HTE_CB_HANDLED; @@ -864,7 +943,7 @@ static irqreturn_t debounce_irq_handler(int irq, void *p) struct line *line = p; mod_delayed_work(system_wq, &line->work, - usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); + usecs_to_jiffies(READ_ONCE(line->debounce_period_us))); return IRQ_HANDLED; } @@ -946,7 +1025,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us) /* try hardware */ ret = gpiod_set_debounce(line->desc, debounce_period_us); if (!ret) { - WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); + line_set_debounce_period(line, debounce_period_us); return ret; } if (ret != -ENOTSUPP) @@ -1025,8 +1104,7 @@ static void edge_detector_stop(struct line *line) cancel_delayed_work_sync(&line->work); WRITE_ONCE(line->sw_debounced, 0); WRITE_ONCE(line->edflags, 0); - if (line->desc) - WRITE_ONCE(line->desc->debounce_period_us, 0); + line_set_debounce_period(line, 0); /* do not change line->level - see comment in debounced_value() */ } @@ -1051,7 +1129,7 @@ static int edge_detector_setup(struct line *line, ret = debounce_setup(line, debounce_period_us); if (ret) return ret; - WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); + line_set_debounce_period(line, debounce_period_us); } /* detection disabled or sw debouncer will provide edge detection */ @@ -1093,12 +1171,12 @@ static int edge_detector_update(struct line *line, gpio_v2_line_config_debounce_period(lc, line_idx); if ((active_edflags == edflags) && - (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)) + (READ_ONCE(line->debounce_period_us) == debounce_period_us)) return 0; /* sw debounced and still will be...*/ if (debounce_period_us && READ_ONCE(line->sw_debounced)) { - WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); + line_set_debounce_period(line, debounce_period_us); return 0; } @@ -1272,9 +1350,18 @@ static long linereq_get_values(struct linereq *lr, void __user *ip) if (copy_from_user(&lv, ip, sizeof(lv))) return -EFAULT; + /* + * gpiod_get_array_value_complex() requires compacted desc and val + * arrays, rather than the sparse ones in lv. + * Calculation of num_get and construction of the desc array is + * optimized to avoid allocation for the desc array for the common + * num_get == 1 case. + */ + /* scan requested lines to calculate the subset to get */ for (num_get = 0, i = 0; i < lr->num_lines; i++) { if (lv.mask & BIT_ULL(i)) { num_get++; + /* capture desc for the num_get == 1 case */ descs = &lr->lines[i].desc; } } @@ -1283,6 +1370,7 @@ static long linereq_get_values(struct linereq *lr, void __user *ip) return -EINVAL; if (num_get != 1) { + /* build compacted desc array */ descs = kmalloc_array(num_get, sizeof(*descs), GFP_KERNEL); if (!descs) return -ENOMEM; @@ -1303,6 +1391,7 @@ static long linereq_get_values(struct linereq *lr, void __user *ip) lv.bits = 0; for (didx = 0, i = 0; i < lr->num_lines; i++) { + /* unpack compacted vals for the response */ if (lv.mask & BIT_ULL(i)) { if (lr->lines[i].sw_debounced) val = debounced_value(&lr->lines[i]); @@ -1320,22 +1409,38 @@ static long linereq_get_values(struct linereq *lr, void __user *ip) return 0; } -static long linereq_set_values_unlocked(struct linereq *lr, - struct gpio_v2_line_values *lv) +static long linereq_set_values(struct linereq *lr, void __user *ip) { DECLARE_BITMAP(vals, GPIO_V2_LINES_MAX); + struct gpio_v2_line_values lv; struct gpio_desc **descs; unsigned int i, didx, num_set; int ret; + if (copy_from_user(&lv, ip, sizeof(lv))) + return -EFAULT; + + guard(mutex)(&lr->config_mutex); + + /* + * gpiod_set_array_value_complex() requires compacted desc and val + * arrays, rather than the sparse ones in lv. + * Calculation of num_set and construction of the descs and vals arrays + * is optimized to minimize scanning the lv->mask, and to avoid + * allocation for the desc array for the common num_set == 1 case. + */ bitmap_zero(vals, GPIO_V2_LINES_MAX); + /* scan requested lines to determine the subset to be set */ for (num_set = 0, i = 0; i < lr->num_lines; i++) { - if (lv->mask & BIT_ULL(i)) { + if (lv.mask & BIT_ULL(i)) { + /* setting inputs is not allowed */ if (!test_bit(FLAG_IS_OUT, &lr->lines[i].desc->flags)) return -EPERM; - if (lv->bits & BIT_ULL(i)) + /* add to compacted values */ + if (lv.bits & BIT_ULL(i)) __set_bit(num_set, vals); num_set++; + /* capture desc for the num_set == 1 case */ descs = &lr->lines[i].desc; } } @@ -1343,12 +1448,12 @@ static long linereq_set_values_unlocked(struct linereq *lr, return -EINVAL; if (num_set != 1) { - /* build compacted desc array and values */ + /* build compacted desc array */ descs = kmalloc_array(num_set, sizeof(*descs), GFP_KERNEL); if (!descs) return -ENOMEM; for (didx = 0, i = 0; i < lr->num_lines; i++) { - if (lv->mask & BIT_ULL(i)) { + if (lv.mask & BIT_ULL(i)) { descs[didx] = lr->lines[i].desc; didx++; } @@ -1362,36 +1467,28 @@ static long linereq_set_values_unlocked(struct linereq *lr, return ret; } -static long linereq_set_values(struct linereq *lr, void __user *ip) -{ - struct gpio_v2_line_values lv; - int ret; - - if (copy_from_user(&lv, ip, sizeof(lv))) - return -EFAULT; - - mutex_lock(&lr->config_mutex); - - ret = linereq_set_values_unlocked(lr, &lv); - - mutex_unlock(&lr->config_mutex); - - return ret; -} - -static long linereq_set_config_unlocked(struct linereq *lr, - struct gpio_v2_line_config *lc) +static long linereq_set_config(struct linereq *lr, void __user *ip) { + struct gpio_v2_line_config lc; struct gpio_desc *desc; struct line *line; unsigned int i; u64 flags, edflags; int ret; + if (copy_from_user(&lc, ip, sizeof(lc))) + return -EFAULT; + + ret = gpio_v2_line_config_validate(&lc, lr->num_lines); + if (ret) + return ret; + + guard(mutex)(&lr->config_mutex); + for (i = 0; i < lr->num_lines; i++) { line = &lr->lines[i]; desc = lr->lines[i].desc; - flags = gpio_v2_line_config_flags(lc, i); + flags = gpio_v2_line_config_flags(&lc, i); gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags); edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* @@ -1399,7 +1496,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, * or output, else the line will be treated "as is". */ if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { - int val = gpio_v2_line_config_output_value(lc, i); + int val = gpio_v2_line_config_output_value(&lc, i); edge_detector_stop(line); ret = gpiod_direction_output(desc, val); @@ -1410,7 +1507,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, if (ret) return ret; - ret = edge_detector_update(line, lc, i, edflags); + ret = edge_detector_update(line, &lc, i, edflags); if (ret) return ret; } @@ -1422,33 +1519,14 @@ static long linereq_set_config_unlocked(struct linereq *lr, return 0; } -static long linereq_set_config(struct linereq *lr, void __user *ip) -{ - struct gpio_v2_line_config lc; - int ret; - - if (copy_from_user(&lc, ip, sizeof(lc))) - return -EFAULT; - - ret = gpio_v2_line_config_validate(&lc, lr->num_lines); - if (ret) - return ret; - - mutex_lock(&lr->config_mutex); - - ret = linereq_set_config_unlocked(lr, &lc); - - mutex_unlock(&lr->config_mutex); - - return ret; -} - -static long linereq_ioctl_unlocked(struct file *file, unsigned int cmd, - unsigned long arg) +static long linereq_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct linereq *lr = file->private_data; void __user *ip = (void __user *)arg; + guard(rwsem_read)(&lr->gdev->sem); + if (!lr->gdev->chip) return -ENODEV; @@ -1464,15 +1542,6 @@ static long linereq_ioctl_unlocked(struct file *file, unsigned int cmd, } } -static long linereq_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct linereq *lr = file->private_data; - - return call_ioctl_locked(file, cmd, arg, lr->gdev, - linereq_ioctl_unlocked); -} - #ifdef CONFIG_COMPAT static long linereq_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -1481,12 +1550,14 @@ static long linereq_ioctl_compat(struct file *file, unsigned int cmd, } #endif -static __poll_t linereq_poll_unlocked(struct file *file, - struct poll_table_struct *wait) +static __poll_t linereq_poll(struct file *file, + struct poll_table_struct *wait) { struct linereq *lr = file->private_data; __poll_t events = 0; + guard(rwsem_read)(&lr->gdev->sem); + if (!lr->gdev->chip) return EPOLLHUP | EPOLLERR; @@ -1499,22 +1570,16 @@ static __poll_t linereq_poll_unlocked(struct file *file, return events; } -static __poll_t linereq_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct linereq *lr = file->private_data; - - return call_poll_locked(file, wait, lr->gdev, linereq_poll_unlocked); -} - -static ssize_t linereq_read_unlocked(struct file *file, char __user *buf, - size_t count, loff_t *f_ps) +static ssize_t linereq_read(struct file *file, char __user *buf, + size_t count, loff_t *f_ps) { struct linereq *lr = file->private_data; struct gpio_v2_line_event le; ssize_t bytes_read = 0; int ret; + guard(rwsem_read)(&lr->gdev->sem); + if (!lr->gdev->chip) return -ENODEV; @@ -1522,28 +1587,22 @@ static ssize_t linereq_read_unlocked(struct file *file, char __user *buf, return -EINVAL; do { - spin_lock(&lr->wait.lock); - if (kfifo_is_empty(&lr->events)) { - if (bytes_read) { - spin_unlock(&lr->wait.lock); - return bytes_read; + scoped_guard(spinlock, &lr->wait.lock) { + if (kfifo_is_empty(&lr->events)) { + if (bytes_read) + return bytes_read; + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible_locked(lr->wait, + !kfifo_is_empty(&lr->events)); + if (ret) + return ret; } - if (file->f_flags & O_NONBLOCK) { - spin_unlock(&lr->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(lr->wait, - !kfifo_is_empty(&lr->events)); - if (ret) { - spin_unlock(&lr->wait.lock); - return ret; - } + ret = kfifo_out(&lr->events, &le, 1); } - - ret = kfifo_out(&lr->events, &le, 1); - spin_unlock(&lr->wait.lock); if (ret != 1) { /* * This should never happen - we were holding the @@ -1562,17 +1621,9 @@ static ssize_t linereq_read_unlocked(struct file *file, char __user *buf, return bytes_read; } -static ssize_t linereq_read(struct file *file, char __user *buf, - size_t count, loff_t *f_ps) -{ - struct linereq *lr = file->private_data; - - return call_read_locked(file, buf, count, f_ps, lr->gdev, - linereq_read_unlocked); -} - static void linereq_free(struct linereq *lr) { + struct line *line; unsigned int i; if (lr->device_unregistered_nb.notifier_call) @@ -1580,15 +1631,19 @@ static void linereq_free(struct linereq *lr) &lr->device_unregistered_nb); for (i = 0; i < lr->num_lines; i++) { - if (lr->lines[i].desc) { - edge_detector_stop(&lr->lines[i]); - gpiod_free(lr->lines[i].desc); - } + line = &lr->lines[i]; + if (!line->desc) + continue; + + edge_detector_stop(line); + if (line_has_supinfo(line)) + supinfo_erase(line); + gpiod_free(line->desc); } kfifo_free(&lr->events); kfree(lr->label); gpio_device_put(lr->gdev); - kfree(lr); + kvfree(lr); } static int linereq_release(struct inode *inode, struct file *file) @@ -1653,7 +1708,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) if (ret) return ret; - lr = kzalloc(struct_size(lr, lines, ulr.num_lines), GFP_KERNEL); + lr = kvzalloc(struct_size(lr, lines, ulr.num_lines), GFP_KERNEL); if (!lr) return -ENOMEM; lr->num_lines = ulr.num_lines; @@ -1818,12 +1873,14 @@ struct lineevent_state { (GPIOEVENT_REQUEST_RISING_EDGE | \ GPIOEVENT_REQUEST_FALLING_EDGE) -static __poll_t lineevent_poll_unlocked(struct file *file, - struct poll_table_struct *wait) +static __poll_t lineevent_poll(struct file *file, + struct poll_table_struct *wait) { struct lineevent_state *le = file->private_data; __poll_t events = 0; + guard(rwsem_read)(&le->gdev->sem); + if (!le->gdev->chip) return EPOLLHUP | EPOLLERR; @@ -1835,14 +1892,6 @@ static __poll_t lineevent_poll_unlocked(struct file *file, return events; } -static __poll_t lineevent_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct lineevent_state *le = file->private_data; - - return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked); -} - static int lineevent_unregistered_notify(struct notifier_block *nb, unsigned long action, void *data) { @@ -1859,8 +1908,8 @@ struct compat_gpioeevent_data { u32 id; }; -static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf, - size_t count, loff_t *f_ps) +static ssize_t lineevent_read(struct file *file, char __user *buf, + size_t count, loff_t *f_ps) { struct lineevent_state *le = file->private_data; struct gpioevent_data ge; @@ -1868,6 +1917,8 @@ static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf, ssize_t ge_size; int ret; + guard(rwsem_read)(&le->gdev->sem); + if (!le->gdev->chip) return -ENODEV; @@ -1888,28 +1939,22 @@ static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf, return -EINVAL; do { - spin_lock(&le->wait.lock); - if (kfifo_is_empty(&le->events)) { - if (bytes_read) { - spin_unlock(&le->wait.lock); - return bytes_read; + scoped_guard(spinlock, &le->wait.lock) { + if (kfifo_is_empty(&le->events)) { + if (bytes_read) + return bytes_read; + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible_locked(le->wait, + !kfifo_is_empty(&le->events)); + if (ret) + return ret; } - if (file->f_flags & O_NONBLOCK) { - spin_unlock(&le->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(le->wait, - !kfifo_is_empty(&le->events)); - if (ret) { - spin_unlock(&le->wait.lock); - return ret; - } + ret = kfifo_out(&le->events, &ge, 1); } - - ret = kfifo_out(&le->events, &ge, 1); - spin_unlock(&le->wait.lock); if (ret != 1) { /* * This should never happen - we were holding the lock @@ -1928,15 +1973,6 @@ static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf, return bytes_read; } -static ssize_t lineevent_read(struct file *file, char __user *buf, - size_t count, loff_t *f_ps) -{ - struct lineevent_state *le = file->private_data; - - return call_read_locked(file, buf, count, f_ps, le->gdev, - lineevent_read_unlocked); -} - static void lineevent_free(struct lineevent_state *le) { if (le->device_unregistered_nb.notifier_call) @@ -1957,13 +1993,15 @@ static int lineevent_release(struct inode *inode, struct file *file) return 0; } -static long lineevent_ioctl_unlocked(struct file *file, unsigned int cmd, - unsigned long arg) +static long lineevent_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct lineevent_state *le = file->private_data; void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; + guard(rwsem_read)(&le->gdev->sem); + if (!le->gdev->chip) return -ENODEV; @@ -1989,15 +2027,6 @@ static long lineevent_ioctl_unlocked(struct file *file, unsigned int cmd, return -EINVAL; } -static long lineevent_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct lineevent_state *le = file->private_data; - - return call_ioctl_locked(file, cmd, arg, le->gdev, - lineevent_ioctl_unlocked); -} - #ifdef CONFIG_COMPAT static long lineevent_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -2272,84 +2301,72 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, struct gpio_v2_line_info *info) { struct gpio_chip *gc = desc->gdev->chip; - bool ok_for_pinctrl; - unsigned long flags; - u32 debounce_period_us; - unsigned int num_attrs = 0; + unsigned long dflags; memset(info, 0, sizeof(*info)); info->offset = gpio_chip_hwgpio(desc); - /* - * This function takes a mutex so we must check this before taking - * the spinlock. - * - * FIXME: find a non-racy way to retrieve this information. Maybe a - * lock common to both frameworks? - */ - ok_for_pinctrl = pinctrl_gpio_can_use_line(gc, info->offset); - - spin_lock_irqsave(&gpio_lock, flags); + scoped_guard(spinlock_irqsave, &gpio_lock) { + if (desc->name) + strscpy(info->name, desc->name, sizeof(info->name)); - if (desc->name) - strscpy(info->name, desc->name, sizeof(info->name)); + if (desc->label) + strscpy(info->consumer, desc->label, + sizeof(info->consumer)); - if (desc->label) - strscpy(info->consumer, desc->label, sizeof(info->consumer)); + dflags = READ_ONCE(desc->flags); + } /* - * Userspace only need to know that the kernel is using this GPIO so - * it can't use it. + * Userspace only need know that the kernel is using this GPIO so it + * can't use it. + * The calculation of the used flag is slightly racy, as it may read + * desc, gc and pinctrl state without a lock covering all three at + * once. Worst case if the line is in transition and the calculation + * is inconsistent then it looks to the user like they performed the + * read on the other side of the transition - but that can always + * happen. + * The definitive test that a line is available to userspace is to + * request it. */ - info->flags = 0; - if (test_bit(FLAG_REQUESTED, &desc->flags) || - test_bit(FLAG_IS_HOGGED, &desc->flags) || - test_bit(FLAG_USED_AS_IRQ, &desc->flags) || - test_bit(FLAG_EXPORT, &desc->flags) || - test_bit(FLAG_SYSFS, &desc->flags) || + if (test_bit(FLAG_REQUESTED, &dflags) || + test_bit(FLAG_IS_HOGGED, &dflags) || + test_bit(FLAG_USED_AS_IRQ, &dflags) || + test_bit(FLAG_EXPORT, &dflags) || + test_bit(FLAG_SYSFS, &dflags) || !gpiochip_line_is_valid(gc, info->offset) || - !ok_for_pinctrl) + !pinctrl_gpio_can_use_line(gc, info->offset)) info->flags |= GPIO_V2_LINE_FLAG_USED; - if (test_bit(FLAG_IS_OUT, &desc->flags)) + if (test_bit(FLAG_IS_OUT, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_OUTPUT; else info->flags |= GPIO_V2_LINE_FLAG_INPUT; - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + if (test_bit(FLAG_ACTIVE_LOW, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW; - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + if (test_bit(FLAG_OPEN_DRAIN, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN; - if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + if (test_bit(FLAG_OPEN_SOURCE, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE; - if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + if (test_bit(FLAG_BIAS_DISABLE, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED; - if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + if (test_bit(FLAG_PULL_DOWN, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN; - if (test_bit(FLAG_PULL_UP, &desc->flags)) + if (test_bit(FLAG_PULL_UP, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP; - if (test_bit(FLAG_EDGE_RISING, &desc->flags)) + if (test_bit(FLAG_EDGE_RISING, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_EDGE_RISING; - if (test_bit(FLAG_EDGE_FALLING, &desc->flags)) + if (test_bit(FLAG_EDGE_FALLING, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING; - if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &desc->flags)) + if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME; - else if (test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags)) + else if (test_bit(FLAG_EVENT_CLOCK_HTE, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE; - - debounce_period_us = READ_ONCE(desc->debounce_period_us); - if (debounce_period_us) { - info->attrs[num_attrs].id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; - info->attrs[num_attrs].debounce_period_us = debounce_period_us; - num_attrs++; - } - info->num_attrs = num_attrs; - - spin_unlock_irqrestore(&gpio_lock, flags); } struct gpio_chardev_data { @@ -2455,6 +2472,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, return -EBUSY; } gpio_desc_to_lineinfo(desc, &lineinfo); + supinfo_to_lineinfo(desc, &lineinfo); if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) { if (watch) @@ -2481,12 +2499,17 @@ static int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip) return 0; } -static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) +/* + * gpio_ioctl() - ioctl handler for the GPIO chardev + */ +static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct gpio_chardev_data *cdev = file->private_data; struct gpio_device *gdev = cdev->gdev; void __user *ip = (void __user *)arg; + guard(rwsem_read)(&gdev->sem); + /* We fail any subsequent ioctl():s when the chip is gone */ if (!gdev->chip) return -ENODEV; @@ -2518,17 +2541,6 @@ static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned lo } } -/* - * gpio_ioctl() - ioctl handler for the GPIO chardev - */ -static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct gpio_chardev_data *cdev = file->private_data; - - return call_ioctl_locked(file, cmd, arg, cdev->gdev, - gpio_ioctl_unlocked); -} - #ifdef CONFIG_COMPAT static long gpio_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -2553,6 +2565,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb, chg.event_type = action; chg.timestamp_ns = ktime_get_ns(); gpio_desc_to_lineinfo(desc, &chg.info); + supinfo_to_lineinfo(desc, &chg.info); ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock); if (ret) @@ -2575,12 +2588,14 @@ static int gpio_device_unregistered_notify(struct notifier_block *nb, return NOTIFY_OK; } -static __poll_t lineinfo_watch_poll_unlocked(struct file *file, - struct poll_table_struct *pollt) +static __poll_t lineinfo_watch_poll(struct file *file, + struct poll_table_struct *pollt) { struct gpio_chardev_data *cdev = file->private_data; __poll_t events = 0; + guard(rwsem_read)(&cdev->gdev->sem); + if (!cdev->gdev->chip) return EPOLLHUP | EPOLLERR; @@ -2593,17 +2608,8 @@ static __poll_t lineinfo_watch_poll_unlocked(struct file *file, return events; } -static __poll_t lineinfo_watch_poll(struct file *file, - struct poll_table_struct *pollt) -{ - struct gpio_chardev_data *cdev = file->private_data; - - return call_poll_locked(file, pollt, cdev->gdev, - lineinfo_watch_poll_unlocked); -} - -static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf, - size_t count, loff_t *off) +static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, + size_t count, loff_t *off) { struct gpio_chardev_data *cdev = file->private_data; struct gpio_v2_line_info_changed event; @@ -2611,6 +2617,8 @@ static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf, int ret; size_t event_size; + guard(rwsem_read)(&cdev->gdev->sem); + if (!cdev->gdev->chip) return -ENODEV; @@ -2621,38 +2629,30 @@ static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf, #endif do { - spin_lock(&cdev->wait.lock); - if (kfifo_is_empty(&cdev->events)) { - if (bytes_read) { - spin_unlock(&cdev->wait.lock); - return bytes_read; + scoped_guard(spinlock, &cdev->wait.lock) { + if (kfifo_is_empty(&cdev->events)) { + if (bytes_read) + return bytes_read; + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible_locked(cdev->wait, + !kfifo_is_empty(&cdev->events)); + if (ret) + return ret; } - - if (file->f_flags & O_NONBLOCK) { - spin_unlock(&cdev->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(cdev->wait, - !kfifo_is_empty(&cdev->events)); - if (ret) { - spin_unlock(&cdev->wait.lock); - return ret; - } - } #ifdef CONFIG_GPIO_CDEV_V1 - /* must be after kfifo check so watch_abi_version is set */ - if (atomic_read(&cdev->watch_abi_version) == 2) - event_size = sizeof(struct gpio_v2_line_info_changed); - else - event_size = sizeof(struct gpioline_info_changed); - if (count < event_size) { - spin_unlock(&cdev->wait.lock); - return -EINVAL; - } + /* must be after kfifo check so watch_abi_version is set */ + if (atomic_read(&cdev->watch_abi_version) == 2) + event_size = sizeof(struct gpio_v2_line_info_changed); + else + event_size = sizeof(struct gpioline_info_changed); + if (count < event_size) + return -EINVAL; #endif - ret = kfifo_out(&cdev->events, &event, 1); - spin_unlock(&cdev->wait.lock); + ret = kfifo_out(&cdev->events, &event, 1); + } if (ret != 1) { ret = -EIO; break; @@ -2681,15 +2681,6 @@ static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf, return bytes_read; } -static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, - size_t count, loff_t *off) -{ - struct gpio_chardev_data *cdev = file->private_data; - - return call_read_locked(file, buf, count, off, cdev->gdev, - lineinfo_watch_read_unlocked); -} - /** * gpio_chrdev_open() - open the chardev for ioctl operations * @inode: inode for this chardev @@ -2703,17 +2694,15 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) struct gpio_chardev_data *cdev; int ret = -ENOMEM; - down_read(&gdev->sem); + guard(rwsem_read)(&gdev->sem); /* Fail on open if the backing gpiochip is gone */ - if (!gdev->chip) { - ret = -ENODEV; - goto out_unlock; - } + if (!gdev->chip) + return -ENODEV; cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) - goto out_unlock; + return -ENODEV; cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); if (!cdev->watched_lines) @@ -2742,8 +2731,6 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) if (ret) goto out_unregister_device_notifier; - up_read(&gdev->sem); - return ret; out_unregister_device_notifier: @@ -2757,8 +2744,6 @@ out_free_bitmap: bitmap_free(cdev->watched_lines); out_free_cdev: kfree(cdev); -out_unlock: - up_read(&gdev->sem); return ret; } diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 12d853845bb8..4dbf298bb5dd 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -165,10 +165,10 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv) /* Caller holds gpiod-data mutex. */ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) { - struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; - unsigned long irq_flags; - int ret; + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; + unsigned long irq_flags; + int ret; data->irq = gpiod_to_irq(desc); if (data->irq < 0) @@ -259,7 +259,7 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpiod_data *data = dev_get_drvdata(dev); - ssize_t status = size; + ssize_t status = size; int flags; flags = sysfs_match_string(trigger_names, buf); @@ -292,10 +292,11 @@ static DEVICE_ATTR_RW(edge); /* Caller holds gpiod-data mutex. */ static int gpio_sysfs_set_active_low(struct device *dev, int value) { - struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; - int status = 0; - unsigned int flags = data->irq_flags; + struct gpiod_data *data = dev_get_drvdata(dev); + unsigned int flags = data->irq_flags; + struct gpio_desc *desc = data->desc; + int status = 0; + if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) return 0; @@ -331,9 +332,9 @@ static ssize_t active_low_show(struct device *dev, static ssize_t active_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); - ssize_t status; - long value; + struct gpiod_data *data = dev_get_drvdata(dev); + ssize_t status; + long value; status = kstrtol(buf, 0, &value); if (status) @@ -399,7 +400,7 @@ static const struct attribute_group *gpio_groups[] = { static ssize_t base_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_chip *chip = dev_get_drvdata(dev); return sysfs_emit(buf, "%d\n", chip->base); } @@ -408,7 +409,7 @@ static DEVICE_ATTR_RO(base); static ssize_t label_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_chip *chip = dev_get_drvdata(dev); return sysfs_emit(buf, "%s\n", chip->label ?: ""); } @@ -417,7 +418,7 @@ static DEVICE_ATTR_RO(label); static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_chip *chip = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", chip->ngpio); } @@ -441,11 +442,10 @@ static ssize_t export_store(const struct class *class, const struct class_attribute *attr, const char *buf, size_t len) { - long gpio; - struct gpio_desc *desc; - int status; - struct gpio_chip *gc; - int offset; + struct gpio_desc *desc; + struct gpio_chip *gc; + int status, offset; + long gpio; status = kstrtol(buf, 0, &gpio); if (status < 0) @@ -496,9 +496,9 @@ static ssize_t unexport_store(const struct class *class, const struct class_attribute *attr, const char *buf, size_t len) { - long gpio; - struct gpio_desc *desc; - int status; + struct gpio_desc *desc; + int status; + long gpio; status = kstrtol(buf, 0, &gpio); if (status < 0) @@ -559,14 +559,13 @@ static struct class gpio_class = { */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { - struct gpio_chip *chip; - struct gpio_device *gdev; - struct gpiod_data *data; - unsigned long flags; - int status; - const char *ioname = NULL; - struct device *dev; - int offset; + const char *ioname = NULL; + struct gpio_device *gdev; + struct gpiod_data *data; + struct gpio_chip *chip; + unsigned long flags; + struct device *dev; + int status, offset; /* can't export until sysfs is available ... */ if (!class_is_registered(&gpio_class)) { @@ -733,9 +732,9 @@ EXPORT_SYMBOL_GPL(gpiod_unexport); int gpiochip_sysfs_register(struct gpio_device *gdev) { - struct device *dev; - struct device *parent; struct gpio_chip *chip = gdev->chip; + struct device *parent; + struct device *dev; /* * Many systems add gpio chips for SOC support very early, @@ -769,6 +768,25 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) return 0; } +int gpiochip_sysfs_register_all(void) +{ + struct gpio_device *gdev; + int ret; + + guard(rwsem_read)(&gpio_devices_sem); + + list_for_each_entry(gdev, &gpio_devices, list) { + if (gdev->mockdev) + continue; + + ret = gpiochip_sysfs_register(gdev); + if (ret) + return ret; + } + + return 0; +} + void gpiochip_sysfs_unregister(struct gpio_device *gdev) { struct gpio_desc *desc; @@ -793,9 +811,7 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) static int __init gpiolib_sysfs_init(void) { - int status; - unsigned long flags; - struct gpio_device *gdev; + int status; status = class_register(&gpio_class); if (status < 0) @@ -807,26 +823,6 @@ static int __init gpiolib_sysfs_init(void) * We run before arch_initcall() so chip->dev nodes can have * registered, and so arch_initcall() can always gpiod_export(). */ - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->mockdev) - continue; - - /* - * TODO we yield gpio_lock here because - * gpiochip_sysfs_register() acquires a mutex. This is unsafe - * and needs to be fixed. - * - * Also it would be nice to use gpio_device_find() here so we - * can keep gpio_chips local to gpiolib.c, but the yield of - * gpio_lock prevents us from doing this. - */ - spin_unlock_irqrestore(&gpio_lock, flags); - status = gpiochip_sysfs_register(gdev); - spin_lock_irqsave(&gpio_lock, flags); - } - spin_unlock_irqrestore(&gpio_lock, flags); - - return status; + return gpiochip_sysfs_register_all(); } postcore_initcall(gpiolib_sysfs_init); diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h index 0f213bdb4732..ab157cec0b4b 100644 --- a/drivers/gpio/gpiolib-sysfs.h +++ b/drivers/gpio/gpiolib-sysfs.h @@ -3,11 +3,12 @@ #ifndef GPIOLIB_SYSFS_H #define GPIOLIB_SYSFS_H -#ifdef CONFIG_GPIO_SYSFS - struct gpio_device; +#ifdef CONFIG_GPIO_SYSFS + int gpiochip_sysfs_register(struct gpio_device *gdev); +int gpiochip_sysfs_register_all(void); void gpiochip_sysfs_unregister(struct gpio_device *gdev); #else @@ -17,6 +18,11 @@ static inline int gpiochip_sysfs_register(struct gpio_device *gdev) return 0; } +static inline int gpiochip_sysfs_register_all(void) +{ + return 0; +} + static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) { } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 95d2a7b2ea3e..4c93cf73a826 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2,6 +2,7 @@ #include <linux/acpi.h> #include <linux/bitmap.h> +#include <linux/cleanup.h> #include <linux/compat.h> #include <linux/debugfs.h> #include <linux/device.h> @@ -15,6 +16,7 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/of.h> #include <linux/pinctrl/consumer.h> #include <linux/seq_file.h> @@ -45,19 +47,6 @@ * GPIOs can sometimes cost only an instruction or two per bit. */ - -/* When debugging, extend minimal trust to callers and platform code. - * Also emit diagnostic messages that may help initial bringup, when - * board setup or driver bugs are most common. - * - * Otherwise, minimize overhead in what may be bitbanging codepaths. - */ -#ifdef DEBUG -#define extra_checks 1 -#else -#define extra_checks 0 -#endif - /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); static dev_t gpio_devt; @@ -94,7 +83,9 @@ DEFINE_SPINLOCK(gpio_lock); static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); + LIST_HEAD(gpio_devices); +DECLARE_RWSEM(gpio_devices_sem); static DEFINE_MUTEX(gpio_machine_hogs_mutex); static LIST_HEAD(gpio_machine_hogs); @@ -126,20 +117,15 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) struct gpio_desc *gpio_to_desc(unsigned gpio) { struct gpio_device *gdev; - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->base <= gpio && - gdev->base + gdev->ngpio > gpio) { - spin_unlock_irqrestore(&gpio_lock, flags); - return &gdev->descs[gpio - gdev->base]; + scoped_guard(rwsem_read, &gpio_devices_sem) { + list_for_each_entry(gdev, &gpio_devices, list) { + if (gdev->base <= gpio && + gdev->base + gdev->ngpio > gpio) + return &gdev->descs[gpio - gdev->base]; } } - spin_unlock_irqrestore(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) pr_warn("invalid GPIO %d\n", gpio); @@ -255,6 +241,20 @@ int gpio_device_get_base(struct gpio_device *gdev) EXPORT_SYMBOL_GPL(gpio_device_get_base); /** + * gpio_device_get_label() - Get the label of this GPIO device + * @gdev: GPIO device + * + * Returns: + * Pointer to the string containing the GPIO device label. The string's + * lifetime is tied to that of the underlying GPIO device. + */ +const char *gpio_device_get_label(struct gpio_device *gdev) +{ + return gdev->label; +} +EXPORT_SYMBOL(gpio_device_get_label); + +/** * gpio_device_get_chip() - Get the gpio_chip implementation of this GPIO device * @gdev: GPIO device * @@ -276,7 +276,7 @@ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev) EXPORT_SYMBOL_GPL(gpio_device_get_chip); /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ -static int gpiochip_find_base(int ngpio) +static int gpiochip_find_base_unlocked(int ngpio) { struct gpio_device *gdev; int base = GPIO_DYNAMIC_BASE; @@ -349,7 +349,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction); * Return -EBUSY if the new chip overlaps with some other chip's integer * space. */ -static int gpiodev_add_to_list(struct gpio_device *gdev) +static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev) { struct gpio_device *prev, *next; @@ -398,26 +398,21 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) static struct gpio_desc *gpio_name_to_desc(const char * const name) { struct gpio_device *gdev; - unsigned long flags; if (!name) return NULL; - spin_lock_irqsave(&gpio_lock, flags); + guard(rwsem_read)(&gpio_devices_sem); list_for_each_entry(gdev, &gpio_devices, list) { struct gpio_desc *desc; for_each_gpio_desc(gdev->chip, desc) { - if (desc->name && !strcmp(desc->name, name)) { - spin_unlock_irqrestore(&gpio_lock, flags); + if (desc->name && !strcmp(desc->name, name)) return desc; - } } } - spin_unlock_irqrestore(&gpio_lock, flags); - return NULL; } @@ -655,11 +650,6 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); static void gpiodev_release(struct device *dev) { struct gpio_device *gdev = to_gpio_device(dev); - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - list_del(&gdev->list); - spin_unlock_irqrestore(&gpio_lock, flags); ida_free(&gpio_ida, gdev->id); kfree_const(gdev->label); @@ -817,7 +807,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *request_key) { struct gpio_device *gdev; - unsigned long flags; unsigned int i; int base = 0; int ret = 0; @@ -882,49 +871,46 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->ngpio = gc->ngpio; - spin_lock_irqsave(&gpio_lock, flags); + scoped_guard(rwsem_write, &gpio_devices_sem) { + /* + * TODO: this allocates a Linux GPIO number base in the global + * GPIO numberspace for this chip. In the long run we want to + * get *rid* of this numberspace and use only descriptors, but + * it may be a pipe dream. It will not happen before we get rid + * of the sysfs interface anyways. + */ + base = gc->base; - /* - * TODO: this allocates a Linux GPIO number base in the global - * GPIO numberspace for this chip. In the long run we want to - * get *rid* of this numberspace and use only descriptors, but - * 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) { - spin_unlock_irqrestore(&gpio_lock, flags); - ret = base; - base = 0; + base = gpiochip_find_base_unlocked(gc->ngpio); + if (base < 0) { + ret = base; + base = 0; + goto err_free_label; + } + /* + * TODO: it should not be necessary to reflect the assigned + * base outside of the GPIO subsystem. Go over drivers and + * see if anyone makes use of this, else drop this and assign + * a poison instead. + */ + gc->base = base; + } else { + dev_warn(&gdev->dev, + "Static allocation of GPIO base is deprecated, use dynamic allocation.\n"); + } + gdev->base = base; + + ret = gpiodev_add_to_list_unlocked(gdev); + if (ret) { + chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); goto err_free_label; } - /* - * TODO: it should not be necessary to reflect the assigned - * base outside of the GPIO subsystem. Go over drivers and - * see if anyone makes use of this, else drop this and assign - * a poison instead. - */ - gc->base = base; - } else { - dev_warn(&gdev->dev, - "Static allocation of GPIO base is deprecated, use dynamic allocation.\n"); - } - gdev->base = base; - ret = gpiodev_add_to_list(gdev); - if (ret) { - spin_unlock_irqrestore(&gpio_lock, flags); - chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); - goto err_free_label; + for (i = 0; i < gc->ngpio; i++) + gdev->descs[i].gdev = gdev; } - for (i = 0; i < gc->ngpio; i++) - gdev->descs[i].gdev = gdev; - - spin_unlock_irqrestore(&gpio_lock, flags); - BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); init_rwsem(&gdev->sem); @@ -1015,9 +1001,8 @@ err_free_gpiochip_mask: goto err_print_message; } err_remove_from_list: - spin_lock_irqsave(&gpio_lock, flags); - list_del(&gdev->list); - spin_unlock_irqrestore(&gpio_lock, flags); + scoped_guard(rwsem_write, &gpio_devices_sem) + list_del(&gdev->list); err_free_label: kfree_const(gdev->label); err_free_descs: @@ -1048,8 +1033,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); void gpiochip_remove(struct gpio_chip *gc) { struct gpio_device *gdev = gc->gpiodev; - unsigned long flags; - unsigned int i; + unsigned long flags; + unsigned int i; down_write(&gdev->sem); @@ -1071,7 +1056,7 @@ void gpiochip_remove(struct gpio_chip *gc) spin_lock_irqsave(&gpio_lock, flags); for (i = 0; i < gdev->ngpio; i++) { - if (gpiochip_is_requested(gc, i)) + if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags)) break; } spin_unlock_irqrestore(&gpio_lock, flags); @@ -1080,6 +1065,9 @@ void gpiochip_remove(struct gpio_chip *gc) dev_crit(&gdev->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); + scoped_guard(rwsem_write, &gpio_devices_sem) + list_del(&gdev->list); + /* * The gpiochip side puts its use of the device to rest here: * if there are no userspace clients, the chardev and device will @@ -1126,7 +1114,7 @@ struct gpio_device *gpio_device_find(void *data, */ might_sleep(); - guard(spinlock_irqsave)(&gpio_lock); + guard(rwsem_read)(&gpio_devices_sem); list_for_each_entry(gdev, &gpio_devices, list) { if (gdev->chip && match(gdev->chip, data)) @@ -2185,10 +2173,10 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); */ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) { - struct gpio_chip *gc = desc->gdev->chip; - int ret; - unsigned long flags; - unsigned offset; + struct gpio_chip *gc = desc->gdev->chip; + unsigned long flags; + unsigned int offset; + int ret; if (label) { label = kstrdup_const(label, GFP_KERNEL); @@ -2300,9 +2288,9 @@ int gpiod_request(struct gpio_desc *desc, const char *label) static bool gpiod_free_commit(struct gpio_desc *desc) { - bool ret = false; - unsigned long flags; - struct gpio_chip *gc; + struct gpio_chip *gc; + unsigned long flags; + bool ret = false; might_sleep(); @@ -2331,9 +2319,6 @@ static bool gpiod_free_commit(struct gpio_desc *desc) #ifdef CONFIG_OF_DYNAMIC desc->hog = NULL; #endif -#ifdef CONFIG_GPIO_CDEV - WRITE_ONCE(desc->debounce_period_us, 0); -#endif ret = true; } @@ -2353,38 +2338,53 @@ void gpiod_free(struct gpio_desc *desc) return; if (!gpiod_free_commit(desc)) - WARN_ON(extra_checks); + WARN_ON(1); module_put(desc->gdev->owner); gpio_device_put(desc->gdev); } /** - * gpiochip_is_requested - return string iff signal was requested - * @gc: controller managing the signal - * @offset: of signal within controller's 0..(ngpio - 1) range + * gpiochip_dup_line_label - Get a copy of the consumer label. + * @gc: GPIO chip controlling this line. + * @offset: Hardware offset of the line. * - * Returns NULL if the GPIO is not currently requested, else a string. - * The string returned is the label passed to gpio_request(); if none has been - * passed it is a meaningless, non-NULL constant. + * Returns: + * Pointer to a copy of the consumer label if the line is requested or NULL + * if it's not. If a valid pointer was returned, it must be freed using + * kfree(). In case of a memory allocation error, the function returns %ENOMEM. * - * This function is for use by GPIO controller drivers. The label can - * help with diagnostics, and knowing that the signal is used as a GPIO - * can help avoid accidentally multiplexing it to another controller. + * Must not be called from atomic context. */ -const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset) +char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; + char *label; desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) return NULL; - if (test_bit(FLAG_REQUESTED, &desc->flags) == 0) + guard(spinlock_irqsave)(&gpio_lock); + + if (!test_bit(FLAG_REQUESTED, &desc->flags)) return NULL; - return desc->label; + + /* + * FIXME: Once we mark gpiod_direction_input/output() and + * gpiod_get_direction() with might_sleep(), we'll be able to protect + * the GPIO descriptors with mutex (while value setting operations will + * become lockless). + * + * Until this happens, this allocation needs to be atomic. + */ + label = kstrdup(desc->label, GFP_ATOMIC); + if (!label) + return ERR_PTR(-ENOMEM); + + return label; } -EXPORT_SYMBOL_GPL(gpiochip_is_requested); +EXPORT_SYMBOL_GPL(gpiochip_dup_line_label); /** * gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor @@ -2564,8 +2564,8 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce) */ int gpiod_direction_input(struct gpio_desc *desc) { - struct gpio_chip *gc; - int ret = 0; + struct gpio_chip *gc; + int ret = 0; VALIDATE_DESC(desc); gc = desc->gdev->chip; @@ -2914,7 +2914,7 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) { - struct gpio_chip *gc; + struct gpio_chip *gc; int value; gc = desc->gdev->chip; @@ -3209,7 +3209,7 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { - struct gpio_chip *gc; + struct gpio_chip *gc; gc = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); @@ -3716,7 +3716,7 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); */ int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { - might_sleep_if(extra_checks); + might_sleep(); VALIDATE_DESC(desc); return gpiod_get_raw_value_commit(desc); } @@ -3735,7 +3735,7 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) { int value; - might_sleep_if(extra_checks); + might_sleep(); VALIDATE_DESC(desc); value = gpiod_get_raw_value_commit(desc); if (value < 0) @@ -3766,7 +3766,7 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { - might_sleep_if(extra_checks); + might_sleep(); if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, true, array_size, @@ -3792,7 +3792,7 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { - might_sleep_if(extra_checks); + might_sleep(); if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, true, array_size, @@ -3813,7 +3813,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); */ void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { - might_sleep_if(extra_checks); + might_sleep(); VALIDATE_DESC_VOID(desc); gpiod_set_raw_value_commit(desc, value); } @@ -3831,7 +3831,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); */ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { - might_sleep_if(extra_checks); + might_sleep(); VALIDATE_DESC_VOID(desc); gpiod_set_value_nocheck(desc, value); } @@ -3854,7 +3854,7 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { - might_sleep_if(extra_checks); + might_sleep(); if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, true, array_size, desc_array, @@ -3896,7 +3896,7 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { - might_sleep_if(extra_checks); + might_sleep(); if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(false, true, array_size, @@ -4696,13 +4696,11 @@ core_initcall(gpiolib_dev_init); static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { - struct gpio_chip *gc = gdev->chip; - struct gpio_desc *desc; - unsigned gpio = gdev->base; - int value; - bool is_out; - bool is_irq; - bool active_low; + struct gpio_chip *gc = gdev->chip; + bool active_low, is_irq, is_out; + unsigned int gpio = gdev->base; + struct gpio_desc *desc; + int value; for_each_gpio_desc(gc, desc) { if (test_bit(FLAG_REQUESTED, &desc->flags)) { @@ -4727,35 +4725,33 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { - unsigned long flags; struct gpio_device *gdev = NULL; loff_t index = *pos; s->private = ""; - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) - if (index-- == 0) { - spin_unlock_irqrestore(&gpio_lock, flags); + guard(rwsem_read)(&gpio_devices_sem); + + list_for_each_entry(gdev, &gpio_devices, list) { + if (index-- == 0) return gdev; - } - spin_unlock_irqrestore(&gpio_lock, flags); + } return NULL; } static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { - unsigned long flags; struct gpio_device *gdev = v; void *ret = NULL; - spin_lock_irqsave(&gpio_lock, flags); - if (list_is_last(&gdev->list, &gpio_devices)) - ret = NULL; - else - ret = list_first_entry(&gdev->list, struct gpio_device, list); - spin_unlock_irqrestore(&gpio_lock, flags); + scoped_guard(rwsem_read, &gpio_devices_sem) { + if (list_is_last(&gdev->list, &gpio_devices)) + ret = NULL; + else + ret = list_first_entry(&gdev->list, struct gpio_device, + list); + } s->private = "\n"; ++*pos; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 3ccacf3c1288..97df54abf57a 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -15,6 +15,7 @@ #include <linux/gpio/consumer.h> /* for enum gpiod_flags */ #include <linux/gpio/driver.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/notifier.h> #include <linux/rwsem.h> @@ -136,6 +137,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); extern spinlock_t gpio_lock; extern struct list_head gpio_devices; +extern struct rw_semaphore gpio_devices_sem; void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); @@ -147,7 +149,6 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); * @label: Name of the consumer * @name: Line name * @hog: Pointer to the device node that hogs this line (if any) - * @debounce_period_us: Debounce period in microseconds * * These are obtained using gpiod_get() and are preferable to the old * integer-based handles. @@ -185,10 +186,6 @@ struct gpio_desc { #ifdef CONFIG_OF_DYNAMIC struct device_node *hog; #endif -#ifdef CONFIG_GPIO_CDEV - /* debounce period in microseconds */ - unsigned int debounce_period_us; -#endif }; #define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 3cd0798ee631..3c8c02043481 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -9,6 +9,7 @@ #include <linux/acpi.h> #include <linux/array_size.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/gpio/driver.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -1173,7 +1174,6 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) const char *pull_str = NULL; const char *pull = NULL; unsigned long flags; - const char *label; unsigned int pin; pin = vg->soc->pins[i].number; @@ -1200,9 +1200,10 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) seq_printf(s, "Pin %i: can't retrieve community\n", pin); continue; } - label = gpiochip_is_requested(chip, i); - if (!label) - label = "Unrequested"; + + char *label __free(kfree) = gpiochip_dup_line_label(chip, i); + if (IS_ERR(label)) + continue; switch (conf0 & BYT_PULL_ASSIGN_MASK) { case BYT_PULL_ASSIGN_UP: @@ -1231,7 +1232,7 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s pad-%-3d offset:0x%03x mux:%d %s%s%s", pin, - label, + label ?: "Unrequested", val & BYT_INPUT_EN ? " " : "in", val & BYT_OUTPUT_EN ? " " : "out", str_hi_lo(val & BYT_LEVEL), diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.c b/drivers/pinctrl/nomadik/pinctrl-abx500.c index d3c32d809bac..80e3ac333136 100644 --- a/drivers/pinctrl/nomadik/pinctrl-abx500.c +++ b/drivers/pinctrl/nomadik/pinctrl-abx500.c @@ -6,7 +6,9 @@ * * Driver allows to use AxB5xx unused pins to be used as GPIO */ + #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/err.h> #include <linux/gpio/driver.h> #include <linux/init.h> @@ -453,12 +455,11 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s, unsigned offset, unsigned gpio) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); - const char *label = gpiochip_is_requested(chip, offset - 1); u8 gpio_offset = offset - 1; int mode = -1; bool is_out; bool pd; - int ret; + int ret = -ENOMEM; const char *modes[] = { [ABX500_DEFAULT] = "default", @@ -474,6 +475,10 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s, [ABX500_GPIO_PULL_UP] = "pull up", }; + char *label __free(kfree) = gpiochip_dup_line_label(chip, offset - 1); + if (IS_ERR(label)) + goto out; + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, gpio_offset, &is_out); if (ret < 0) diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c index 863732287b1e..7911353ac97d 100644 --- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c +++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c @@ -8,6 +8,7 @@ * Copyright (C) 2011-2013 Linus Walleij <linus.walleij@linaro.org> */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/err.h> @@ -917,7 +918,6 @@ static void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev, struct gpio_chip *chip, unsigned offset, unsigned gpio) { - const char *label = gpiochip_is_requested(chip, offset); struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); int mode; bool is_out; @@ -934,6 +934,10 @@ static void nmk_gpio_dbg_show_one(struct seq_file *s, [NMK_GPIO_ALT_C+4] = "altC4", }; + char *label = gpiochip_dup_line_label(chip, offset); + if (IS_ERR(label)) + return; + clk_enable(nmk_chip->clk); is_out = !!(readl(nmk_chip->addr + NMK_GPIO_DIR) & BIT(offset)); pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & BIT(offset)); diff --git a/drivers/pinctrl/sunplus/sppctl.c b/drivers/pinctrl/sunplus/sppctl.c index bb5ef391dbe4..ae156f779a16 100644 --- a/drivers/pinctrl/sunplus/sppctl.c +++ b/drivers/pinctrl/sunplus/sppctl.c @@ -4,6 +4,7 @@ * Copyright (C) Sunplus Tech / Tibbo Tech. */ +#include <linux/cleanup.h> #include <linux/bitfield.h> #include <linux/device.h> #include <linux/err.h> @@ -500,16 +501,15 @@ static int sppctl_gpio_set_config(struct gpio_chip *chip, unsigned int offset, static void sppctl_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { - const char *label; int i; for (i = 0; i < chip->ngpio; i++) { - label = gpiochip_is_requested(chip, i); - if (!label) - label = ""; + char *label __free(kfree) = gpiochip_dup_line_label(chip, i); + if (IS_ERR(label)) + continue; seq_printf(s, " gpio-%03d (%-16.16s | %-16.16s)", i + chip->base, - chip->names[i], label); + chip->names[i], label ?: ""); seq_printf(s, " %c", sppctl_gpio_get_direction(chip, i) ? 'I' : 'O'); seq_printf(s, ":%d", sppctl_gpio_get(chip, i)); seq_printf(s, " %s", sppctl_first_get(chip, i) ? "gpi" : "mux"); |