diff options
| author | Mark Brown <broonie@kernel.org> | 2026-05-29 23:13:54 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-05-29 23:13:54 +0100 |
| commit | 72cd7b71160e5e201a1e9b941bbfacc7cd25003f (patch) | |
| tree | 1e61b49504d13050958041ef5ae7fadce908543d /drivers | |
| parent | 1ef9f7af25f6afae52decb8280285d2824d69c83 (diff) | |
| parent | 9de94681ee48770ec7e2062451a572b557bf9298 (diff) | |
| download | linux-next-history-72cd7b71160e5e201a1e9b941bbfacc7cd25003f.tar.gz | |
Merge branch 'gpio/for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
Diffstat (limited to 'drivers')
46 files changed, 960 insertions, 696 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 020e51e30317a..bbd0ebe2f1316 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -102,6 +102,14 @@ config GPIO_CDEV_V1 This ABI version is deprecated. Please use the latest ABI for new developments. +config GPIO_KUNIT + tristate "Build GPIO Kunit test cases" + depends on KUNIT + default KUNIT_ALL_TESTS + help + Say Y here to build the module containing Kunit test cases verifying + the functionality of the GPIO subsystem. + config GPIO_GENERIC depends on HAS_IOMEM # Only for IOMEM drivers tristate @@ -156,6 +164,7 @@ config GPIO_74XX_MMIO config GPIO_ALTERA tristate "Altera GPIO" + select GPIO_GENERIC select GPIOLIB_IRQCHIP help Say Y or M here to build support for the Altera PIO device. @@ -301,7 +310,7 @@ config GPIO_EM config GPIO_EN7523 tristate "Airoha GPIO support" - depends on ARCH_AIROHA + depends on ARCH_AIROHA || COMPILE_TEST default ARCH_AIROHA select GPIO_GENERIC select GPIOLIB_IRQCHIP @@ -698,7 +707,7 @@ config GPIO_SPACEMIT_K1 config GPIO_SPEAR_SPICS bool "ST SPEAr13xx SPI Chip Select as GPIO support" - depends on PLAT_SPEAR + depends on PLAT_SPEAR || COMPILE_TEST select GENERIC_IRQ_CHIP help Say yes here to support ST SPEAr SPI Chip Select as GPIO device. @@ -805,8 +814,18 @@ config GPIO_VISCONTI help Say yes here to support GPIO on Tohisba Visconti. +config GPIO_WAVESHARE_DSI_TOUCH + tristate "Waveshare GPIO controller for DSI panels" + depends on BACKLIGHT_CLASS_DEVICE + depends on I2C + select REGMAP_I2C + help + Enable support for the GPIO and PWM controller found on Waveshare DSI + TOUCH panel kits. It provides GPIOs (used for regulator control and + resets) and backlight support. + config GPIO_WCD934X - tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver" + tristate "Qualcomm WCD9340/WCD9341 GPIO controller driver" depends on MFD_WCD934X help This driver is to support GPIO block found on the Qualcomm Technologies @@ -814,7 +833,7 @@ config GPIO_WCD934X config GPIO_XGENE bool "APM X-Gene GPIO controller support" - depends on ARM64 + depends on ARM64 || COMPILE_TEST help This driver is to support the GPIO block within the APM X-Gene SoC platform's generic flash controller. The GPIO pins are muxed with @@ -858,7 +877,7 @@ config GPIO_XTENSA config GPIO_ZEVIO bool "LSI ZEVIO SoC memory mapped GPIOs" - depends on ARM + depends on ARM || COMPILE_TEST help Say yes here to support the GPIO controller in LSI ZEVIO SoCs. @@ -1093,15 +1112,6 @@ config GPIO_SCH311X To compile this driver as a module, choose M here: the module will be called gpio-sch311x. -config GPIO_TS5500 - tristate "TS-5500 DIO blocks and compatibles" - depends on TS5500 || COMPILE_TEST - help - This driver supports Digital I/O exposed by pin blocks found on some - Technologic Systems platforms. It includes, but is not limited to, 3 - blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600 - LCD port. - config GPIO_WINBOND tristate "Winbond Super I/O GPIO support" select ISA_BUS_API @@ -2060,6 +2070,7 @@ config GPIO_VIRTIO config GPIO_SIM tristate "GPIO Simulator Module" + depends on SYSFS select IRQ_SIM select CONFIGFS_FS help diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b267598b517de..8ec03c9aec20d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o +obj-$(CONFIG_GPIO_KUNIT) += gpiolib-kunit.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o @@ -194,7 +195,6 @@ obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o -obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o @@ -205,6 +205,7 @@ obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o +obj-$(CONFIG_GPIO_WAVESHARE_DSI_TOUCH) += gpio-waveshare-dsi.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index c226524efebab..5ca61cf5206aa 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -112,7 +112,7 @@ static int gen_74x164_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct gen_74x164_chip *chip; - u32 nregs; + u32 nregs, init_state; int ret; /* @@ -134,6 +134,21 @@ static int gen_74x164_probe(struct spi_device *spi) chip->registers = nregs; + /* + * Optionally seed the chain with a board-specified pattern so the + * outputs come up in a known state on the first SPI write. The + * property follows the nxp,pcf8575 convention where bit N maps to + * GPIO line N. On this output-only device, bit=0 drives the line + * low and bit=1 drives it high. The bitmask covers up to 32 lines; + * any further outputs come up zeroed by devm_kzalloc(). + */ + if (!device_property_read_u32(dev, "lines-initial-states", &init_state)) { + unsigned int i; + + for (i = 0; i < min(nregs, 4U); i++) + chip->buffer[nregs - 1 - i] = (init_state >> (i * 8)) & 0xff; + } + chip->gpiod_oe = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(chip->gpiod_oe)) return PTR_ERR(chip->gpiod_oe); diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index fe5bcaa90496a..350feea2afa3d 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -501,8 +501,8 @@ static int adnp_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adnp_i2c_id[] = { - { "gpio-adnp" }, - { }, + { .name = "gpio-adnp" }, + { } }; MODULE_DEVICE_TABLE(i2c, adnp_i2c_id); diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index 0fd3cc26d017b..6f10fc6460080 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -507,8 +507,8 @@ static const struct adp5585_gpio_chip adp5589_gpio_chip_info = { }; static const struct platform_device_id adp5585_gpio_id_table[] = { - { "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info }, - { "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info }, + { .name = "adp5585-gpio", .driver_data = (kernel_ulong_t)&adp5585_gpio_chip_info }, + { .name = "adp5589-gpio", .driver_data = (kernel_ulong_t)&adp5589_gpio_chip_info }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table); diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 9508d764cce40..fe144360a88d8 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -17,6 +17,7 @@ #include <linux/types.h> #include <linux/gpio/driver.h> +#include <linux/gpio/generic.h> #define ALTERA_GPIO_MAX_NGPIO 32 #define ALTERA_GPIO_DATA 0x0 @@ -26,7 +27,7 @@ /** * struct altera_gpio_chip -* @gc : GPIO chip structure. +* @chip : Generic GPIO chip structure. * @regs : memory mapped IO address for the controller registers. * @gpio_lock : synchronization lock so that new irq/set/get requests * will be blocked until the current one completes. @@ -34,7 +35,7 @@ * (rising, falling, both, high) */ struct altera_gpio_chip { - struct gpio_chip gc; + struct gpio_generic_chip chip; void __iomem *regs; raw_spinlock_t gpio_lock; int interrupt_trigger; @@ -106,72 +107,6 @@ static unsigned int altera_gpio_irq_startup(struct irq_data *d) return 0; } -static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - - return !!(readl(altera_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); -} - -static int altera_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int data_reg; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA); - if (value) - data_reg |= BIT(offset); - else - data_reg &= ~BIT(offset); - writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - -static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int gpio_ddr; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - /* Set pin as input, assumes software controlled IP */ - gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR); - gpio_ddr &= ~BIT(offset); - writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - -static int altera_gpio_direction_output(struct gpio_chip *gc, - unsigned offset, int value) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int data_reg, gpio_ddr; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - /* Sets the GPIO value */ - data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA); - if (value) - data_reg |= BIT(offset); - else - data_reg &= ~BIT(offset); - writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); - - /* Set pin as output, assumes software controlled IP */ - gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR); - gpio_ddr |= BIT(offset); - writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - static void altera_gpio_irq_edge_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); @@ -231,9 +166,12 @@ static const struct irq_chip altera_gpio_irq_chip = { static int altera_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config; struct device *dev = &pdev->dev; int reg, ret; struct altera_gpio_chip *altera_gc; + struct gpio_generic_chip *chip; + struct gpio_chip *gc; struct gpio_irq_chip *girq; int mapped_irq; @@ -243,35 +181,45 @@ static int altera_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&altera_gc->gpio_lock); + altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(altera_gc->regs)) + return dev_err_probe(dev, PTR_ERR(altera_gc->regs), + "failed to ioremap memory resource\n"); + + chip = &altera_gc->chip; + + config = (struct gpio_generic_chip_config) { + .dev = dev, + .sz = 4, + .dat = altera_gc->regs + ALTERA_GPIO_DATA, + .set = altera_gc->regs + ALTERA_GPIO_DATA, + .dirout = altera_gc->regs + ALTERA_GPIO_DIR, + }; + + ret = gpio_generic_chip_init(chip, &config); + if (ret) + return dev_err_probe(dev, ret, "unable to init generic GPIO\n"); + + gc = &chip->gc; + if (device_property_read_u32(dev, "altr,ngpio", ®)) /* By default assume maximum ngpio */ - altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO; + gc->ngpio = ALTERA_GPIO_MAX_NGPIO; else - altera_gc->gc.ngpio = reg; + gc->ngpio = reg; - if (altera_gc->gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { + if (gc->ngpio > ALTERA_GPIO_MAX_NGPIO) { dev_warn(&pdev->dev, "ngpio is greater than %d, defaulting to %d\n", ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); - altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO; + gc->ngpio = ALTERA_GPIO_MAX_NGPIO; } - altera_gc->gc.direction_input = altera_gpio_direction_input; - altera_gc->gc.direction_output = altera_gpio_direction_output; - altera_gc->gc.get = altera_gpio_get; - altera_gc->gc.set = altera_gpio_set; - altera_gc->gc.owner = THIS_MODULE; - altera_gc->gc.parent = &pdev->dev; - altera_gc->gc.base = -1; - - altera_gc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); - if (!altera_gc->gc.label) + gc->base = -1; + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); + if (!gc->label) return -ENOMEM; - altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(altera_gc->regs)) - return dev_err_probe(dev, PTR_ERR(altera_gc->regs), "failed to ioremap memory resource\n"); - mapped_irq = platform_get_irq_optional(pdev, 0); if (mapped_irq < 0) goto skip_irq; @@ -283,7 +231,7 @@ static int altera_gpio_probe(struct platform_device *pdev) } altera_gc->interrupt_trigger = reg; - girq = &altera_gc->gc.irq; + girq = &gc->irq; gpio_irq_chip_set_chip(girq, &altera_gpio_irq_chip); if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) @@ -300,7 +248,7 @@ static int altera_gpio_probe(struct platform_device *pdev) girq->parents[0] = mapped_irq; skip_irq: - ret = devm_gpiochip_add_data(dev, &altera_gc->gc, altera_gc); + ret = devm_gpiochip_add_data(dev, gc, altera_gc); if (ret) { dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); return ret; diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 15fd5e210d747..8078b5d7b80c9 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -59,8 +59,8 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 0 }, - { 0, }, /* terminate list */ + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS) }, + { }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c index d0f936ed80af3..306e234112090 100644 --- a/drivers/gpio/gpio-bd72720.c +++ b/drivers/gpio/gpio-bd72720.c @@ -263,8 +263,8 @@ static int gpo_bd72720_probe(struct platform_device *pdev) } static const struct platform_device_id bd72720_gpio_id[] = { - { "bd72720-gpio" }, - { }, + { .name = "bd72720-gpio" }, + { } }; MODULE_DEVICE_TABLE(platform, bd72720_gpio_id); diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index cc5b1746f2fe8..f829fc40c02be 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -110,8 +110,8 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id bd9571mwv_gpio_id_table[] = { - { "bd9571mwv-gpio", ROHM_CHIP_TYPE_BD9571 }, - { "bd9574mwf-gpio", ROHM_CHIP_TYPE_BD9574 }, + { .name = "bd9571mwv-gpio", .driver_data = ROHM_CHIP_TYPE_BD9571 }, + { .name = "bd9574mwf-gpio", .driver_data = ROHM_CHIP_TYPE_BD9574 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, bd9571mwv_gpio_id_table); diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c index 435483826c6e5..9deda8a9d11aa 100644 --- a/drivers/gpio/gpio-cros-ec.c +++ b/drivers/gpio/gpio-cros-ec.c @@ -196,8 +196,8 @@ static int cros_ec_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id cros_ec_gpio_id[] = { - { "cros-ec-gpio", 0 }, - {} + { .name = "cros-ec-gpio" }, + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_gpio_id); diff --git a/drivers/gpio/gpio-ds4520.c b/drivers/gpio/gpio-ds4520.c index f52ecae382a45..5add662a7ef51 100644 --- a/drivers/gpio/gpio-ds4520.c +++ b/drivers/gpio/gpio-ds4520.c @@ -54,7 +54,7 @@ static const struct of_device_id ds4520_gpio_of_match_table[] = { MODULE_DEVICE_TABLE(of, ds4520_gpio_of_match_table); static const struct i2c_device_id ds4520_gpio_id_table[] = { - { "ds4520-gpio" }, + { .name = "ds4520-gpio" }, { } }; MODULE_DEVICE_TABLE(i2c, ds4520_gpio_id_table); diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 15cebc8b5d663..c1f3d83a67c10 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -694,6 +694,7 @@ static const struct acpi_device_id dwapb_acpi_match[] = { {"APMC0D07", GPIO_REG_OFFSET_V1}, {"APMC0D81", GPIO_REG_OFFSET_V2}, {"FUJI200A", GPIO_REG_OFFSET_V1}, + {"LECA0001", GPIO_REG_OFFSET_V1}, { } }; MODULE_DEVICE_TABLE(acpi, dwapb_acpi_match); diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 1f56e44ffc9a3..8784e433e1ffb 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -323,8 +323,7 @@ static int ep93xx_setup_irqs(struct platform_device *pdev, } girq->default_type = IRQ_TYPE_NONE; - /* TODO: replace with handle_bad_irq() once we are fully hierarchical */ - girq->handler = handle_simple_irq; + girq->handler = handle_bad_irq; return 0; } diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c index afc1b8461dabb..45b02d36e66ff 100644 --- a/drivers/gpio/gpio-fxl6408.c +++ b/drivers/gpio/gpio-fxl6408.c @@ -150,7 +150,7 @@ static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = { MODULE_DEVICE_TABLE(of, fxl6408_dt_ids); static const struct i2c_device_id fxl6408_id[] = { - { "fxl6408" }, + { .name = "fxl6408" }, { } }; MODULE_DEVICE_TABLE(i2c, fxl6408_id); diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 2e5d97b7363fd..bf1f91c3c4a8b 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -109,7 +109,7 @@ static int gw_pld_probe(struct i2c_client *client) } static const struct i2c_device_id gw_pld_id[] = { - { "gw-pld", }, + { .name = "gw-pld" }, { } }; MODULE_DEVICE_TABLE(i2c, gw_pld_id); diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index f34d87869c8b0..669b139cd499b 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -311,12 +311,7 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) } g->chip.gc.ngpio = 16; g->chip.gc.label = "IXP4XX_GPIO_CHIP"; - /* - * TODO: when we have migrated to device tree and all GPIOs - * are fetched using phandles, set this to -1 to get rid of - * the fixed gpiochip base. - */ - g->chip.gc.base = 0; + g->chip.gc.base = -1; g->chip.gc.parent = &pdev->dev; g->chip.gc.owner = THIS_MODULE; diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index f4413fa5a8110..0d1bd9df265a4 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -156,7 +156,7 @@ static int lp873x_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id lp873x_gpio_id_table[] = { - { "lp873x-gpio", }, + { .name = "lp873x-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, lp873x_gpio_id_table); diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index 0f337c1283b22..3ac78f2b0fa70 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -171,7 +171,7 @@ static int lp87565_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id lp87565_gpio_id_table[] = { - { "lp87565-q1-gpio", }, + { .name = "lp87565-q1-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, lp87565_gpio_id_table); diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c index 621d609ece904..62f2434c0d79b 100644 --- a/drivers/gpio/gpio-max7300.c +++ b/drivers/gpio/gpio-max7300.c @@ -53,7 +53,7 @@ static void max7300_remove(struct i2c_client *client) } static const struct i2c_device_id max7300_id[] = { - { "max7300" }, + { .name = "max7300" }, { } }; MODULE_DEVICE_TABLE(i2c, max7300_id); diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 281ba1740a6a0..24c67c9129549 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -103,16 +103,16 @@ static uint64_t max732x_features[] = { }; static const struct i2c_device_id max732x_id[] = { - { "max7319", MAX7319 }, - { "max7320", MAX7320 }, - { "max7321", MAX7321 }, - { "max7322", MAX7322 }, - { "max7323", MAX7323 }, - { "max7324", MAX7324 }, - { "max7325", MAX7325 }, - { "max7326", MAX7326 }, - { "max7327", MAX7327 }, - { }, + { .name = "max7319", .driver_data = MAX7319 }, + { .name = "max7320", .driver_data = MAX7320 }, + { .name = "max7321", .driver_data = MAX7321 }, + { .name = "max7322", .driver_data = MAX7322 }, + { .name = "max7323", .driver_data = MAX7323 }, + { .name = "max7324", .driver_data = MAX7324 }, + { .name = "max7325", .driver_data = MAX7325 }, + { .name = "max7326", .driver_data = MAX7326 }, + { .name = "max7327", .driver_data = MAX7327 }, + { } }; MODULE_DEVICE_TABLE(i2c, max732x_id); diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index e6c85411c695e..2bf3b55a61b5e 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -367,7 +367,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) static const struct platform_device_id max77620_gpio_devtype[] = { { .name = "max77620-gpio", }, { .name = "max20024-gpio", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype); diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c index 3bf9f23d15328..c6bdac7fb44a6 100644 --- a/drivers/gpio/gpio-max77759.c +++ b/drivers/gpio/gpio-max77759.c @@ -502,7 +502,7 @@ static const struct of_device_id max77759_gpio_of_id[] = { MODULE_DEVICE_TABLE(of, max77759_gpio_of_id); static const struct platform_device_id max77759_gpio_platform_id[] = { - { "max77759-gpio", }, + { .name = "max77759-gpio" }, { } }; MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_id); diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 12f11a6c96653..7e2690d92df6f 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -330,13 +330,13 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable) ret = enable_irq_wake(port->irq_high); else ret = enable_irq_wake(port->irq); - port->wakeup_pads |= (1 << gpio_idx); + port->wakeup_pads |= BIT(gpio_idx); } else { if (port->irq_high && (gpio_idx >= 16)) ret = disable_irq_wake(port->irq_high); else ret = disable_irq_wake(port->irq); - port->wakeup_pads &= ~(1 << gpio_idx); + port->wakeup_pads &= ~BIT(gpio_idx); } return ret; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b9c905a0ffa90..2ee35e855e4da 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -86,49 +86,49 @@ #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) static const struct i2c_device_id pca953x_id[] = { - { "pca6408", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca6416", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9505", 40 | PCA953X_TYPE | PCA_INT, }, - { "pca9506", 40 | PCA953X_TYPE | PCA_INT, }, - { "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9536", 4 | PCA953X_TYPE, }, - { "pca9537", 4 | PCA953X_TYPE | PCA_INT, }, - { "pca9538", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9539", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9554", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9555", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9556", 8 | PCA953X_TYPE, }, - { "pca9557", 8 | PCA953X_TYPE, }, - { "pca9574", 8 | PCA957X_TYPE | PCA_INT, }, - { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, - { "pca9698", 40 | PCA953X_TYPE, }, + { .name = "pca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9505", .driver_data = 40 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9506", .driver_data = 40 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9534", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9535", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9536", .driver_data = 4 | PCA953X_TYPE }, + { .name = "pca9537", .driver_data = 4 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9555", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9556", .driver_data = 8 | PCA953X_TYPE }, + { .name = "pca9557", .driver_data = 8 | PCA953X_TYPE }, + { .name = "pca9574", .driver_data = 8 | PCA957X_TYPE | PCA_INT }, + { .name = "pca9575", .driver_data = 16 | PCA957X_TYPE | PCA_INT }, + { .name = "pca9698", .driver_data = 40 | PCA953X_TYPE }, - { "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6534", 34 | PCAL653X_TYPE | PCA_LATCH_INT, }, - { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal9554b", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, + { .name = "pcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6524", .driver_data = 24 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6534", .driver_data = 34 | PCAL653X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9535", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9554b", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9555a", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, - { "max7310", 8 | PCA953X_TYPE, }, - { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, - { "max7313", 16 | PCA953X_TYPE | PCA_INT, }, - { "max7315", 8 | PCA953X_TYPE | PCA_INT, }, - { "max7318", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, - { "tca6418", 18 | TCA6418_TYPE | PCA_INT, }, - { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, - { "tca9538", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, - { "tca9554", 8 | PCA953X_TYPE | PCA_INT, }, - { "xra1202", 8 | PCA953X_TYPE }, + { .name = "max7310", .driver_data = 8 | PCA953X_TYPE }, + { .name = "max7312", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "max7313", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "max7315", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "max7318", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca6107", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6418", .driver_data = 18 | TCA6418_TYPE | PCA_INT }, + { .name = "tca6424", .driver_data = 24 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "xra1202", .driver_data = 8 | PCA953X_TYPE }, - { "tcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "tcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, + { .name = "tcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "tcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, { } }; MODULE_DEVICE_TABLE(i2c, pca953x_id); @@ -877,11 +877,9 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio); bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio); - bitmap_complement(reg_direction, reg_direction, gc->ngpio); - bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); /* Look for any newly setup interrupt */ - for_each_set_bit(level, irq_mask, gc->ngpio) + for_each_andnot_bit(level, irq_mask, reg_direction, gc->ngpio) pca953x_gpio_direction_input(&chip->gpio_chip, level); mutex_unlock(&chip->irq_lock); @@ -1005,8 +1003,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio); bitmap_or(pending, pending, cur_stat, gc->ngpio); - bitmap_complement(cur_stat, new_stat, gc->ngpio); - bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio); + bitmap_andnot(cur_stat, reg_direction, new_stat, gc->ngpio); bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio); bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio); bitmap_or(pending, pending, old_stat, gc->ngpio); diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index 4a368803fb038..7a47a9aa0414e 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -163,9 +163,9 @@ static const struct pca9570_chip_data slg7xl45106_gpio = { }; static const struct i2c_device_id pca9570_id_table[] = { - { "pca9570", (kernel_ulong_t)&pca9570_gpio}, - { "pca9571", (kernel_ulong_t)&pca9571_gpio }, - { "slg7xl45106", (kernel_ulong_t)&slg7xl45106_gpio }, + { .name = "pca9570", .driver_data = (kernel_ulong_t)&pca9570_gpio }, + { .name = "pca9571", .driver_data = (kernel_ulong_t)&pca9571_gpio }, + { .name = "slg7xl45106", .driver_data = (kernel_ulong_t)&slg7xl45106_gpio }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9570_id_table); diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 3b9de8c3d924c..c942b959571b2 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -20,19 +20,19 @@ #include <linux/spinlock.h> static const struct i2c_device_id pcf857x_id[] = { - { "pcf8574", 8 }, - { "pcf8574a", 8 }, - { "pca8574", 8 }, - { "pca9670", 8 }, - { "pca9672", 8 }, - { "pca9674", 8 }, - { "pcf8575", 16 }, - { "pca8575", 16 }, - { "pca9671", 16 }, - { "pca9673", 16 }, - { "pca9675", 16 }, - { "max7328", 8 }, - { "max7329", 8 }, + { .name = "pcf8574", .driver_data = 8 }, + { .name = "pcf8574a", .driver_data = 8 }, + { .name = "pca8574", .driver_data = 8 }, + { .name = "pca9670", .driver_data = 8 }, + { .name = "pca9672", .driver_data = 8 }, + { .name = "pca9674", .driver_data = 8 }, + { .name = "pcf8575", .driver_data = 16 }, + { .name = "pca8575", .driver_data = 16 }, + { .name = "pca9671", .driver_data = 16 }, + { .name = "pca9673", .driver_data = 16 }, + { .name = "pca9675", .driver_data = 16 }, + { .name = "max7328", .driver_data = 8 }, + { .name = "max7329", .driver_data = 8 }, { } }; MODULE_DEVICE_TABLE(i2c, pcf857x_id); diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 664cf1eef494c..5d61053e0596a 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -708,15 +708,15 @@ static int pxa_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id gpio_id_table[] = { - { "pxa25x-gpio", (unsigned long)&pxa25x_id }, - { "pxa26x-gpio", (unsigned long)&pxa26x_id }, - { "pxa27x-gpio", (unsigned long)&pxa27x_id }, - { "pxa3xx-gpio", (unsigned long)&pxa3xx_id }, - { "pxa93x-gpio", (unsigned long)&pxa93x_id }, - { "mmp-gpio", (unsigned long)&mmp_id }, - { "mmp2-gpio", (unsigned long)&mmp2_id }, - { "pxa1928-gpio", (unsigned long)&pxa1928_id }, - { }, + { .name = "pxa25x-gpio", .driver_data = (unsigned long)&pxa25x_id }, + { .name = "pxa26x-gpio", .driver_data = (unsigned long)&pxa26x_id }, + { .name = "pxa27x-gpio", .driver_data = (unsigned long)&pxa27x_id }, + { .name = "pxa3xx-gpio", .driver_data = (unsigned long)&pxa3xx_id }, + { .name = "pxa93x-gpio", .driver_data = (unsigned long)&pxa93x_id }, + { .name = "mmp-gpio", .driver_data = (unsigned long)&mmp_id }, + { .name = "mmp2-gpio", .driver_data = (unsigned long)&mmp2_id }, + { .name = "pxa1928-gpio", .driver_data = (unsigned long)&pxa1928_id }, + { } }; static struct platform_driver pxa_gpio_driver = { diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index 5e3152c2e51a8..37ef56f453187 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -40,16 +40,18 @@ #define REALTEK_GPIO_PORTS_PER_BANK 4 /** - * realtek_gpio_ctrl - Realtek Otto GPIO driver data + * struct realtek_gpio_ctrl - Realtek Otto GPIO driver data * * @chip: Associated gpio_generic_chip instance * @base: Base address of the register block for a GPIO bank + * @cpumask_base: Base address of the per-CPU interrupt mask registers + * @cpu_irq_maskable: CPUs that can receive GPIO interrupts * @lock: Lock for accessing the IRQ registers and values * @intr_mask: Mask for interrupts lines * @intr_type: Interrupt type selection * @bank_read: Read a bank setting as a single 32-bit value * @bank_write: Write a bank setting as a single 32-bit value - * @imr_line_pos: Bit shift of an IRQ line's IMR value. + * @line_imr_pos: Bit shift of an IRQ line's IMR value. * * The DIR, DATA, and ISR registers consist of four 8-bit port values, packed * into a single 32-bit register. Use @bank_read (@bank_write) to get (assign) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 9ae4a41a24277..51b4d69b87403 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -31,6 +31,7 @@ struct gpio_regmap { unsigned int reg_clr_base; unsigned int reg_dir_in_base; unsigned int reg_dir_out_base; + unsigned long *fixed_direction_mask; unsigned long *fixed_direction_output; #ifdef CONFIG_REGMAP_IRQ @@ -138,6 +139,20 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip, return regmap_write(gpio->regmap, reg, mask); } +static bool gpio_regmap_fixed_direction(struct gpio_regmap *gpio, + unsigned int offset) +{ + if (!gpio->fixed_direction_output) + return false; + + /* In this case only some GPIOs are fixed as input/output */ + if (gpio->fixed_direction_mask && + !test_bit(offset, gpio->fixed_direction_mask)) + return false; + + return true; +} + static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -145,7 +160,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int base, val, reg, mask; int invert, ret; - if (gpio->fixed_direction_output) { + if (gpio_regmap_fixed_direction(gpio, offset)) { if (test_bit(offset, gpio->fixed_direction_output)) return GPIO_LINE_DIRECTION_OUT; else @@ -181,6 +196,22 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, return GPIO_LINE_DIRECTION_IN; } +static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio, + unsigned int offset, bool output) +{ + if (test_bit(offset, gpio->fixed_direction_output)) { + if (output) + return 0; + else + return -EINVAL; + } else { + if (output) + return -EINVAL; + else + return 0; + } +} + static int gpio_regmap_set_direction(struct gpio_chip *chip, unsigned int offset, bool output) { @@ -188,6 +219,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip, unsigned int base, val, reg, mask; int invert, ret; + /* + * If the direction is fixed, only accept the fixed + * direction in this call. + */ + if (gpio_regmap_fixed_direction(gpio, offset)) + return gpio_regmap_try_direction_fixed(gpio, offset, output); + if (gpio->reg_dir_out_base) { base = gpio_regmap_addr(gpio->reg_dir_out_base); invert = 0; @@ -219,6 +257,20 @@ static int gpio_regmap_direction_input(struct gpio_chip *chip, static int gpio_regmap_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { + struct gpio_regmap *gpio = gpiochip_get_data(chip); + int ret; + + /* + * First check if this is gonna work on a fixed direction line, + * if it doesn't (i.e. this is a fixed input line), then do not + * attempt to set the output value either and just bail out. + */ + if (gpio_regmap_fixed_direction(gpio, offset)) { + ret = gpio_regmap_try_direction_fixed(gpio, offset, true); + if (ret) + return ret; + } + gpio_regmap_set(chip, offset, value); return gpio_regmap_set_direction(chip, offset, true); @@ -302,12 +354,23 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config goto err_free_gpio; } + if (config->fixed_direction_mask) { + gpio->fixed_direction_mask = bitmap_alloc(chip->ngpio, + GFP_KERNEL); + if (!gpio->fixed_direction_mask) { + ret = -ENOMEM; + goto err_free_gpio; + } + bitmap_copy(gpio->fixed_direction_mask, + config->fixed_direction_mask, chip->ngpio); + } + if (config->fixed_direction_output) { gpio->fixed_direction_output = bitmap_alloc(chip->ngpio, GFP_KERNEL); if (!gpio->fixed_direction_output) { ret = -ENOMEM; - goto err_free_gpio; + goto err_free_bitmap_dirmask; } bitmap_copy(gpio->fixed_direction_output, config->fixed_direction_output, chip->ngpio); @@ -329,7 +392,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config ret = gpiochip_add_data(chip, gpio); if (ret < 0) - goto err_free_bitmap; + goto err_free_bitmap_output; #ifdef CONFIG_REGMAP_IRQ if (config->regmap_irq_chip) { @@ -355,8 +418,10 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config err_remove_gpiochip: gpiochip_remove(chip); -err_free_bitmap: +err_free_bitmap_output: bitmap_free(gpio->fixed_direction_output); +err_free_bitmap_dirmask: + bitmap_free(gpio->fixed_direction_mask); err_free_gpio: kfree(gpio); return ERR_PTR(ret); @@ -376,6 +441,7 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio) gpiochip_remove(&gpio->gpio_chip); bitmap_free(gpio->fixed_direction_output); + bitmap_free(gpio->fixed_direction_mask); kfree(gpio); } EXPORT_SYMBOL_GPL(gpio_regmap_unregister); diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 0da2c5a45843e..f0f570527cf23 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -695,9 +695,9 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, pdev = dev->pdev; if (pdev) - return sprintf(page, "%s\n", dev_name(&pdev->dev)); + return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); - return sprintf(page, "gpio-sim.%d\n", dev->id); + return sysfs_emit(page, "gpio-sim.%d\n", dev->id); } CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name); @@ -711,7 +711,7 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page) scoped_guard(mutex, &dev->lock) live = gpio_sim_device_is_live(dev); - return sprintf(page, "%c\n", live ? '1' : '0'); + return sysfs_emit(page, "%c\n", live ? '1' : '0'); } static unsigned int gpio_sim_get_line_names_size(struct gpio_sim_bank *bank) @@ -1059,7 +1059,7 @@ static int gpio_sim_emit_chip_name(struct device *dev, void *data) return 0; if (device_match_fwnode(dev, ctx->swnode)) - return sprintf(ctx->page, "%s\n", dev_name(dev)); + return sysfs_emit(ctx->page, "%s\n", dev_name(dev)); return 0; } @@ -1077,7 +1077,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, return device_for_each_child(&dev->pdev->dev, &ctx, gpio_sim_emit_chip_name); - return sprintf(page, "none\n"); + return sysfs_emit(page, "none\n"); } CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name); @@ -1090,7 +1090,7 @@ gpio_sim_bank_config_label_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", bank->label ?: ""); + return sysfs_emit(page, "%s\n", bank->label ?: ""); } static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, @@ -1125,7 +1125,7 @@ gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%u\n", bank->num_lines); + return sysfs_emit(page, "%u\n", bank->num_lines); } static ssize_t @@ -1171,7 +1171,7 @@ gpio_sim_line_config_name_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", line->name ?: ""); + return sysfs_emit(page, "%s\n", line->name ?: ""); } static ssize_t gpio_sim_line_config_name_store(struct config_item *item, @@ -1206,7 +1206,7 @@ gpio_sim_line_config_valid_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%c\n", line->valid ? '1' : '0'); + return sysfs_emit(page, "%c\n", line->valid ? '1' : '0'); } static ssize_t gpio_sim_line_config_valid_store(struct config_item *item, @@ -1244,7 +1244,7 @@ static ssize_t gpio_sim_hog_config_name_show(struct config_item *item, guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", hog->name ?: ""); + return sysfs_emit(page, "%s\n", hog->name ?: ""); } static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, @@ -1298,7 +1298,7 @@ static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item, return -EINVAL; } - return sprintf(page, "%s\n", repr); + return sysfs_emit(page, "%s\n", repr); } static ssize_t @@ -1338,7 +1338,7 @@ static ssize_t gpio_sim_hog_config_active_low_show(struct config_item *item, guard(mutex)(&dev->lock); - return sprintf(page, "%c\n", hog->active_low ? '1' : '0'); + return sysfs_emit(page, "%c\n", hog->active_low ? '1' : '0'); } static ssize_t diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index aa7c3e44234f5..d9a2dedf50eae 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -19,6 +19,7 @@ #include <dt-bindings/gpio/tegra186-gpio.h> #include <dt-bindings/gpio/tegra194-gpio.h> #include <dt-bindings/gpio/tegra234-gpio.h> +#include <dt-bindings/gpio/nvidia,tegra238-gpio.h> #include <dt-bindings/gpio/tegra241-gpio.h> #include <dt-bindings/gpio/tegra256-gpio.h> #include <dt-bindings/gpio/nvidia,tegra264-gpio.h> @@ -1239,6 +1240,67 @@ static const struct tegra_gpio_soc tegra234_aon_soc = { .has_vm_support = false, }; +#define TEGRA238_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA238_MAIN, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra238_main_ports[] = { + TEGRA238_MAIN_GPIO_PORT(A, 0, 0, 8), + TEGRA238_MAIN_GPIO_PORT(B, 0, 1, 5), + TEGRA238_MAIN_GPIO_PORT(C, 0, 2, 8), + TEGRA238_MAIN_GPIO_PORT(D, 0, 3, 8), + TEGRA238_MAIN_GPIO_PORT(E, 0, 4, 4), + TEGRA238_MAIN_GPIO_PORT(F, 0, 5, 8), + TEGRA238_MAIN_GPIO_PORT(G, 0, 6, 8), + TEGRA238_MAIN_GPIO_PORT(H, 0, 7, 6), + TEGRA238_MAIN_GPIO_PORT(J, 1, 0, 8), + TEGRA238_MAIN_GPIO_PORT(K, 1, 1, 4), + TEGRA238_MAIN_GPIO_PORT(L, 1, 2, 8), + TEGRA238_MAIN_GPIO_PORT(M, 1, 3, 8), + TEGRA238_MAIN_GPIO_PORT(N, 1, 4, 3), + TEGRA238_MAIN_GPIO_PORT(P, 1, 5, 8), + TEGRA238_MAIN_GPIO_PORT(Q, 1, 6, 3), + TEGRA238_MAIN_GPIO_PORT(R, 2, 0, 8), + TEGRA238_MAIN_GPIO_PORT(S, 2, 1, 8), + TEGRA238_MAIN_GPIO_PORT(T, 2, 2, 8), + TEGRA238_MAIN_GPIO_PORT(U, 2, 3, 6), + TEGRA238_MAIN_GPIO_PORT(V, 2, 4, 2), + TEGRA238_MAIN_GPIO_PORT(W, 3, 0, 8), + TEGRA238_MAIN_GPIO_PORT(X, 3, 1, 2) +}; + +static const struct tegra_gpio_soc tegra238_main_soc = { + .num_ports = ARRAY_SIZE(tegra238_main_ports), + .ports = tegra238_main_ports, + .name = "tegra238-gpio", + .instance = 0, + .num_irqs_per_bank = 8, + .has_vm_support = true, +}; + +#define TEGRA238_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA238_AON, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra238_aon_ports[] = { + TEGRA238_AON_GPIO_PORT(AA, 0, 0, 8), + TEGRA238_AON_GPIO_PORT(BB, 0, 1, 1), + TEGRA238_AON_GPIO_PORT(CC, 0, 2, 8), + TEGRA238_AON_GPIO_PORT(DD, 0, 3, 8), + TEGRA238_AON_GPIO_PORT(EE, 0, 4, 6), + TEGRA238_AON_GPIO_PORT(FF, 0, 5, 8), + TEGRA238_AON_GPIO_PORT(GG, 0, 6, 8), + TEGRA238_AON_GPIO_PORT(HH, 0, 7, 4) +}; + +static const struct tegra_gpio_soc tegra238_aon_soc = { + .num_ports = ARRAY_SIZE(tegra238_aon_ports), + .ports = tegra238_aon_ports, + .name = "tegra238-gpio-aon", + .instance = 1, + .num_irqs_per_bank = 8, + .has_gte = true, + .has_vm_support = false, +}; + #define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins) @@ -1333,6 +1395,7 @@ static const struct tegra_gpio_soc tegra264_aon_soc = { .name = "tegra264-gpio-aon", .instance = 1, .num_irqs_per_bank = 8, + .has_gte = true, .has_vm_support = true, }; @@ -1448,6 +1511,12 @@ static const struct of_device_id tegra186_gpio_of_match[] = { .compatible = "nvidia,tegra234-gpio-aon", .data = &tegra234_aon_soc }, { + .compatible = "nvidia,tegra238-gpio", + .data = &tegra238_main_soc + }, { + .compatible = "nvidia,tegra238-gpio-aon", + .data = &tegra238_aon_soc + }, { .compatible = "nvidia,tegra256-gpio", .data = &tegra256_main_soc }, { diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index 866ff2d436d53..c38538653e991 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -112,7 +112,7 @@ static int tpic2810_probe(struct i2c_client *client) } static const struct i2c_device_id tpic2810_id_table[] = { - { "tpic2810", }, + { .name = "tpic2810" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, tpic2810_id_table); diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index df770ecf28bc4..f29d8485ab331 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -91,7 +91,7 @@ static int tps65086_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps65086_gpio_id_table[] = { - { "tps65086-gpio", }, + { .name = "tps65086-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65086_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 3b4c41f5ef554..bf85663349fb5 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -201,7 +201,7 @@ static const struct of_device_id tps65218_dt_match[] = { MODULE_DEVICE_TABLE(of, tps65218_dt_match); static const struct platform_device_id tps65218_gpio_id_table[] = { - { "tps65218-gpio", }, + { .name = "tps65218-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index 158f63bcf10cd..457fd8a589e89 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -249,8 +249,8 @@ static int tps65219_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps6521x_gpio_id_table[] = { - { "tps65214-gpio", TPS65214 }, - { "tps65219-gpio", TPS65219 }, + { .name = "tps65214-gpio", .driver_data = TPS65214 }, + { .name = "tps65219-gpio", .driver_data = TPS65219 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index 7a2c5685c2fdb..cf3fa49a70974 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -115,7 +115,7 @@ static int tps65912_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps65912_gpio_id_table[] = { - { "tps65912-gpio", }, + { .name = "tps65912-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65912_gpio_id_table); diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index d9ee8fc77ccdf..b46b48e56c565 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -175,7 +175,7 @@ static int ts4900_gpio_probe(struct i2c_client *client) } static const struct i2c_device_id ts4900_gpio_id_table[] = { - { "ts4900-gpio", }, + { .name = "ts4900-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ts4900_gpio_id_table); diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c deleted file mode 100644 index 3c7f2efe10fd7..0000000000000 --- a/drivers/gpio/gpio-ts5500.c +++ /dev/null @@ -1,446 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Digital I/O driver for Technologic Systems TS-5500 - * - * Copyright (c) 2012 Savoir-faire Linux Inc. - * Vivien Didelot <vivien.didelot@savoirfairelinux.com> - * - * Technologic Systems platforms have pin blocks, exposing several Digital - * Input/Output lines (DIO). This driver aims to support single pin blocks. - * In that sense, the support is not limited to the TS-5500 blocks. - * Actually, the following platforms have DIO support: - * - * TS-5500: - * Documentation: https://docs.embeddedts.com/TS-5500 - * Blocks: DIO1, DIO2 and LCD port. - * - * TS-5600: - * Documentation: https://docs.embeddedts.com/TS-5600 - * Blocks: LCD port (identical to TS-5500 LCD). - */ - -#include <linux/bitops.h> -#include <linux/gpio/driver.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -/* List of supported Technologic Systems platforms DIO blocks */ -enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD }; - -struct ts5500_priv { - const struct ts5500_dio *pinout; - struct gpio_chip gpio_chip; - spinlock_t lock; - bool strap; - u8 hwirq; -}; - -/* - * Hex 7D is used to control several blocks (e.g. DIO2 and LCD port). - * This flag ensures that the region has been requested by this driver. - */ -static bool hex7d_reserved; - -/* - * This structure is used to describe capabilities of DIO lines, - * such as available directions and connected interrupt (if any). - */ -struct ts5500_dio { - const u8 value_addr; - const u8 value_mask; - const u8 control_addr; - const u8 control_mask; - const bool no_input; - const bool no_output; - const u8 irq; -}; - -#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \ - { \ - .value_addr = vaddr, \ - .value_mask = BIT(vbit), \ - .control_addr = caddr, \ - .control_mask = BIT(cbit), \ - } - -#define TS5500_DIO_IN(addr, bit) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_output = true, \ - } - -#define TS5500_DIO_IN_IRQ(addr, bit, _irq) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_output = true, \ - .irq = _irq, \ - } - -#define TS5500_DIO_OUT(addr, bit) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_input = true, \ - } - -/* - * Input/Output DIO lines are programmed in groups of 4. Their values are - * available through 4 consecutive bits in a value port, whereas the direction - * of these 4 lines is driven by only 1 bit in a control port. - */ -#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit) \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit) - -/* - * TS-5500 DIO1 block - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x7b 0 0x7a 0 x x DIO1_0 1 0 - * 0x7b 1 0x7a 0 x x DIO1_1 3 1 - * 0x7b 2 0x7a 0 x x DIO1_2 5 2 - * 0x7b 3 0x7a 0 x x DIO1_3 7 3 - * 0x7b 4 0x7a 1 x x DIO1_4 9 4 - * 0x7b 5 0x7a 1 x x DIO1_5 11 5 - * 0x7b 6 0x7a 1 x x DIO1_6 13 6 - * 0x7b 7 0x7a 1 x x DIO1_7 15 7 - * 0x7c 0 0x7a 5 x x DIO1_8 4 8 - * 0x7c 1 0x7a 5 x x DIO1_9 6 9 - * 0x7c 2 0x7a 5 x x DIO1_10 8 10 - * 0x7c 3 0x7a 5 x x DIO1_11 10 11 - * 0x7c 4 x DIO1_12 12 12 - * 0x7c 5 x 7 DIO1_13 14 13 - */ -static const struct ts5500_dio ts5500_dio1[] = { - TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0), - TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1), - TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5), - TS5500_DIO_IN(0x7c, 4), - TS5500_DIO_IN_IRQ(0x7c, 5, 7), -}; - -/* - * TS-5500 DIO2 block - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x7e 0 0x7d 0 x x DIO2_0 1 0 - * 0x7e 1 0x7d 0 x x DIO2_1 3 1 - * 0x7e 2 0x7d 0 x x DIO2_2 5 2 - * 0x7e 3 0x7d 0 x x DIO2_3 7 3 - * 0x7e 4 0x7d 1 x x DIO2_4 9 4 - * 0x7e 5 0x7d 1 x x DIO2_5 11 5 - * 0x7e 6 0x7d 1 x x DIO2_6 13 6 - * 0x7e 7 0x7d 1 x x DIO2_7 15 7 - * 0x7f 0 0x7d 5 x x DIO2_8 4 8 - * 0x7f 1 0x7d 5 x x DIO2_9 6 9 - * 0x7f 2 0x7d 5 x x DIO2_10 8 10 - * 0x7f 3 0x7d 5 x x DIO2_11 10 11 - * 0x7f 4 x 6 DIO2_13 14 12 - */ -static const struct ts5500_dio ts5500_dio2[] = { - TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0), - TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1), - TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5), - TS5500_DIO_IN_IRQ(0x7f, 4, 6), -}; - -/* - * TS-5500 LCD port used as DIO block - * TS-5600 LCD port is identical - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x72 0 0x7d 2 x x LCD_0 8 0 - * 0x72 1 0x7d 2 x x LCD_1 7 1 - * 0x72 2 0x7d 2 x x LCD_2 10 2 - * 0x72 3 0x7d 2 x x LCD_3 9 3 - * 0x72 4 0x7d 3 x x LCD_4 12 4 - * 0x72 5 0x7d 3 x x LCD_5 11 5 - * 0x72 6 0x7d 3 x x LCD_6 14 6 - * 0x72 7 0x7d 3 x x LCD_7 13 7 - * 0x73 0 x LCD_EN 5 8 - * 0x73 6 x LCD_WR 6 9 - * 0x73 7 x 1 LCD_RS 3 10 - */ -static const struct ts5500_dio ts5500_lcd[] = { - TS5500_DIO_GROUP(0x72, 0, 0x7d, 2), - TS5500_DIO_GROUP(0x72, 4, 0x7d, 3), - TS5500_DIO_OUT(0x73, 0), - TS5500_DIO_IN(0x73, 6), - TS5500_DIO_IN_IRQ(0x73, 7, 1), -}; - -static inline void ts5500_set_mask(u8 mask, u8 addr) -{ - u8 val = inb(addr); - val |= mask; - outb(val, addr); -} - -static inline void ts5500_clear_mask(u8 mask, u8 addr) -{ - u8 val = inb(addr); - val &= ~mask; - outb(val, addr); -} - -static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - if (line.no_input) - return -ENXIO; - - if (line.no_output) - return 0; - - spin_lock_irqsave(&priv->lock, flags); - ts5500_clear_mask(line.control_mask, line.control_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - - return !!(inb(line.value_addr) & line.value_mask); -} - -static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - if (line.no_output) - return -ENXIO; - - spin_lock_irqsave(&priv->lock, flags); - if (!line.no_input) - ts5500_set_mask(line.control_mask, line.control_addr); - - if (val) - ts5500_set_mask(line.value_mask, line.value_addr); - else - ts5500_clear_mask(line.value_mask, line.value_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (val) - ts5500_set_mask(line.value_mask, line.value_addr); - else - ts5500_clear_mask(line.value_mask, line.value_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio *block = priv->pinout; - const struct ts5500_dio line = block[offset]; - - /* Only one pin is connected to an interrupt */ - if (line.irq) - return line.irq; - - /* As this pin is input-only, we may strap it to another in/out pin */ - if (priv->strap) - return priv->hwirq; - - return -ENXIO; -} - -static int ts5500_enable_irq(struct ts5500_priv *priv) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->hwirq == 7) - ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */ - else if (priv->hwirq == 6) - ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */ - else if (priv->hwirq == 1) - ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */ - else - ret = -EINVAL; - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} - -static void ts5500_disable_irq(struct ts5500_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->hwirq == 7) - ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */ - else if (priv->hwirq == 6) - ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */ - else if (priv->hwirq == 1) - ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */ - else - dev_err(priv->gpio_chip.parent, "invalid hwirq %d\n", - priv->hwirq); - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int ts5500_dio_probe(struct platform_device *pdev) -{ - enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data; - struct device *dev = &pdev->dev; - const char *name = dev_name(dev); - struct ts5500_priv *priv; - unsigned long flags; - int ret; - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - - priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - platform_set_drvdata(pdev, priv); - priv->hwirq = ret; - spin_lock_init(&priv->lock); - - priv->gpio_chip.owner = THIS_MODULE; - priv->gpio_chip.label = name; - priv->gpio_chip.parent = dev; - priv->gpio_chip.direction_input = ts5500_gpio_input; - priv->gpio_chip.direction_output = ts5500_gpio_output; - priv->gpio_chip.get = ts5500_gpio_get; - priv->gpio_chip.set = ts5500_gpio_set; - priv->gpio_chip.to_irq = ts5500_gpio_to_irq; - priv->gpio_chip.base = -1; - - switch (block) { - case TS5500_DIO1: - priv->pinout = ts5500_dio1; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1); - - if (!devm_request_region(dev, 0x7a, 3, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - break; - case TS5500_DIO2: - priv->pinout = ts5500_dio2; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2); - - if (!devm_request_region(dev, 0x7e, 2, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - - if (hex7d_reserved) - break; - - if (!devm_request_region(dev, 0x7d, 1, name)) { - dev_err(dev, "failed to request %s 7D\n", name); - return -EBUSY; - } - - hex7d_reserved = true; - break; - case TS5500_LCD: - case TS5600_LCD: - priv->pinout = ts5500_lcd; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd); - - if (!devm_request_region(dev, 0x72, 2, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - - if (!hex7d_reserved) { - if (!devm_request_region(dev, 0x7d, 1, name)) { - dev_err(dev, "failed to request %s 7D\n", name); - return -EBUSY; - } - - hex7d_reserved = true; - } - - /* Ensure usage of LCD port as DIO */ - spin_lock_irqsave(&priv->lock, flags); - ts5500_clear_mask(BIT(4), 0x7d); - spin_unlock_irqrestore(&priv->lock, flags); - break; - } - - ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv); - if (ret) { - dev_err(dev, "failed to register the gpio chip\n"); - return ret; - } - - ret = ts5500_enable_irq(priv); - if (ret) { - dev_err(dev, "invalid interrupt %d\n", priv->hwirq); - return ret; - } - - return 0; -} - -static void ts5500_dio_remove(struct platform_device *pdev) -{ - struct ts5500_priv *priv = platform_get_drvdata(pdev); - - ts5500_disable_irq(priv); -} - -static const struct platform_device_id ts5500_dio_ids[] = { - { "ts5500-dio1", TS5500_DIO1 }, - { "ts5500-dio2", TS5500_DIO2 }, - { "ts5500-dio-lcd", TS5500_LCD }, - { "ts5600-dio-lcd", TS5600_LCD }, - { } -}; -MODULE_DEVICE_TABLE(platform, ts5500_dio_ids); - -static struct platform_driver ts5500_dio_driver = { - .driver = { - .name = "ts5500-dio", - }, - .probe = ts5500_dio_probe, - .remove = ts5500_dio_remove, - .id_table = ts5500_dio_ids, -}; - -module_platform_driver(ts5500_dio_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>"); -MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver"); diff --git a/drivers/gpio/gpio-usbio.c b/drivers/gpio/gpio-usbio.c index 34d42c743d5bc..489c8ac6299e1 100644 --- a/drivers/gpio/gpio-usbio.c +++ b/drivers/gpio/gpio-usbio.c @@ -31,6 +31,7 @@ static const struct acpi_device_id usbio_gpio_acpi_hids[] = { { "INTC10B5" }, /* LNL */ { "INTC10D1" }, /* MTL-CVF */ { "INTC10E2" }, /* PTL */ + { "INTC1116" }, /* NVL */ { } }; diff --git a/drivers/gpio/gpio-waveshare-dsi.c b/drivers/gpio/gpio-waveshare-dsi.c new file mode 100644 index 0000000000000..38f52351bb584 --- /dev/null +++ b/drivers/gpio/gpio-waveshare-dsi.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Waveshare International Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +/* I2C registers of the microcontroller. */ +#define REG_TP 0x94 +#define REG_LCD 0x95 +#define REG_PWM 0x96 +#define REG_SIZE 0x97 +#define REG_ID 0x98 +#define REG_VERSION 0x99 + +enum { + GPIO_AVDD = 0, + GPIO_PANEL_RESET = 1, + GPIO_BL_ENABLE = 2, + GPIO_IOVCC = 4, + GPIO_VCC = 8, + GPIO_TS_RESET = 9, +}; + +#define NUM_GPIO 16 + +struct waveshare_gpio { + struct mutex dir_lock; + struct mutex pwr_lock; + struct regmap *regmap; + u16 poweron_state; + + struct gpio_chip gc; +}; + +static const struct regmap_config waveshare_gpio_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_VERSION, +}; + +static int waveshare_gpio_get(struct waveshare_gpio *state, unsigned int offset) +{ + u16 pwr_state; + + guard(mutex)(&state->pwr_lock); + pwr_state = state->poweron_state & BIT(offset); + + return !!pwr_state; +} + +static int waveshare_gpio_set(struct waveshare_gpio *state, unsigned int offset, int value) +{ + u16 last_val; + int err; + + guard(mutex)(&state->pwr_lock); + + last_val = state->poweron_state; + if (value) + last_val |= BIT(offset); + else + last_val &= ~BIT(offset); + + state->poweron_state = last_val; + + err = regmap_write(state->regmap, REG_TP, last_val >> 8); + if (!err) + err = regmap_write(state->regmap, REG_LCD, last_val & 0xff); + + return err; +} + +static int waveshare_gpio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int waveshare_gpio_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct waveshare_gpio *state = gpiochip_get_data(gc); + + return waveshare_gpio_get(state, offset); +} + +static int waveshare_gpio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct waveshare_gpio *state = gpiochip_get_data(gc); + + return waveshare_gpio_set(state, offset, value); +} + +static int waveshare_gpio_update_status(struct backlight_device *bl) +{ + struct waveshare_gpio *state = bl_get_data(bl); + int brightness = backlight_get_brightness(bl); + + waveshare_gpio_set(state, GPIO_BL_ENABLE, brightness); + + return regmap_write(state->regmap, REG_PWM, brightness); +} + +static const struct backlight_ops waveshare_gpio_bl = { + .update_status = waveshare_gpio_update_status, +}; + +static int waveshare_gpio_probe(struct i2c_client *i2c) +{ + struct backlight_properties props = {}; + struct waveshare_gpio *state; + struct device *dev = &i2c->dev; + struct backlight_device *bl; + struct regmap *regmap; + unsigned int data; + int ret; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + ret = devm_mutex_init(dev, &state->dir_lock); + if (ret) + return ret; + + ret = devm_mutex_init(dev, &state->pwr_lock); + if (ret) + return ret; + + regmap = devm_regmap_init_i2c(i2c, &waveshare_gpio_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n"); + + state->regmap = regmap; + i2c_set_clientdata(i2c, state); + + ret = regmap_read(regmap, REG_ID, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel hw id = 0x%x\n", data); + + ret = regmap_read(regmap, REG_SIZE, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel size = %d\n", data); + + ret = regmap_read(regmap, REG_VERSION, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel mcu version = 0x%x\n", data); + + ret = waveshare_gpio_set(state, GPIO_TS_RESET, 1); + if (ret) + return dev_err_probe(dev, ret, "Failed to program GPIOs\n"); + + msleep(20); + + state->gc.parent = dev; + state->gc.label = i2c->name; + state->gc.owner = THIS_MODULE; + state->gc.base = -1; + state->gc.ngpio = NUM_GPIO; + + /* it is output only */ + state->gc.get = waveshare_gpio_gpio_get; + state->gc.set = waveshare_gpio_gpio_set; + state->gc.get_direction = waveshare_gpio_gpio_get_direction; + state->gc.can_sleep = true; + + ret = devm_gpiochip_add_data(dev, &state->gc, state); + if (ret) + return dev_err_probe(dev, ret, "Failed to create gpiochip\n"); + + props.type = BACKLIGHT_RAW; + props.max_brightness = 255; + props.brightness = 255; + bl = devm_backlight_device_register(dev, dev_name(dev), dev, state, + &waveshare_gpio_bl, &props); + return PTR_ERR_OR_ZERO(bl); +} + +static const struct of_device_id waveshare_gpio_dt_ids[] = { + { .compatible = "waveshare,dsi-touch-gpio" }, + {}, +}; +MODULE_DEVICE_TABLE(of, waveshare_gpio_dt_ids); + +static struct i2c_driver waveshare_gpio_regulator_driver = { + .driver = { + .name = "waveshare-regulator", + .of_match_table = of_match_ptr(waveshare_gpio_dt_ids), + }, + .probe = waveshare_gpio_probe, +}; + +module_i2c_driver(waveshare_gpio_regulator_driver); + +MODULE_DESCRIPTION("GPIO controller driver for Waveshare DSI touch panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index be4b4d7305477..532205175827a 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -495,13 +495,11 @@ static void xgpio_irqhandler(struct irq_desc *desc) xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, hw); - bitmap_complement(rising, chip->last_irq_read, 64); - bitmap_and(rising, rising, hw, 64); + bitmap_andnot(rising, hw, chip->last_irq_read, 64); bitmap_and(rising, rising, chip->enable, 64); bitmap_and(rising, rising, chip->rising_edge, 64); - bitmap_complement(falling, hw, 64); - bitmap_and(falling, falling, chip->last_irq_read, 64); + bitmap_andnot(falling, chip->last_irq_read, hw, 64); bitmap_and(falling, falling, chip->enable, 64); bitmap_and(falling, falling, chip->falling_edge, 64); diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index 29375bea2289b..af0158522ac5d 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -64,14 +64,14 @@ static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin, unsigned port_offset) { unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; - return readl(IOMEM(c->regs + section_offset + port_offset)); + return readl(c->regs + section_offset + port_offset); } static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin, unsigned port_offset, u32 val) { unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; - writel(val, IOMEM(c->regs + section_offset + port_offset)); + writel(val, c->regs + section_offset + port_offset); } /* Functions for struct gpio_chip */ diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 571e366624d2a..8118ae3412c20 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -25,6 +25,7 @@ #define VERSAL_GPIO_MAX_BANK 4 #define PMC_GPIO_MAX_BANK 5 #define VERSAL_UNUSED_BANKS 2 +#define EIO_GPIO_MAX_BANK 2 #define ZYNQ_GPIO_BANK0_NGPIO 32 #define ZYNQ_GPIO_BANK1_NGPIO 22 @@ -818,6 +819,16 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL) }; +static const struct zynq_platform_data eio_gpio_def = { + .label = "eio_gpio", + .ngpio = 52, + .max_bank = EIO_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[1] = 26, + .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */ +}; + static const struct zynq_platform_data versal_gpio_def = { .label = "versal_gpio", .quirks = GPIO_QUIRK_VERSAL, @@ -882,6 +893,7 @@ static const struct of_device_id zynq_gpio_of_match[] = { { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def }, { .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def }, + { .compatible = "xlnx,eio-gpio-1.0", .data = &eio_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 72422c5db3641..2ec825ffab7da 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -353,6 +353,13 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo { int ret; + /* + * We are passing the devres device here so if the user did not pass + * another parent, it's this one. + */ + if (!gc->parent) + gc->parent = dev; + ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key); if (ret < 0) return ret; diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c new file mode 100644 index 0000000000000..380b68f879e55 --- /dev/null +++ b/drivers/gpio/gpiolib-kunit.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries + */ + +#include <linux/fwnode.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> +#include <linux/gpio/property.h> +#include <linux/notifier.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +#include <kunit/platform_device.h> +#include <kunit/test.h> + +#define GPIO_TEST_PROVIDER "gpio-test-provider" +#define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer" +#define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer" + +static int gpio_test_provider_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int gpio_test_provider_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + return 0; +} + +static int gpio_test_provider_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_chip *gc; + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->base = -1; + gc->ngpio = 4; + gc->label = "gpio-swnode-consumer-test-device"; + gc->parent = dev; + gc->owner = THIS_MODULE; + + gc->get_direction = gpio_test_provider_get_direction; + gc->set = gpio_test_provider_set; + + return devm_gpiochip_add_data(dev, gc, NULL); +} + +static struct platform_driver gpio_test_provider_driver = { + .probe = gpio_test_provider_probe, + .driver = { + .name = GPIO_TEST_PROVIDER, + }, +}; + +static const struct software_node gpio_test_provider_swnode = { + .name = "gpio-test-provider-primary", +}; + +struct gpio_swnode_consumer_pdata { + bool gpio_ok; +}; + +static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = { + .gpio_ok = false, +}; + +static int gpio_swnode_consumer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_swnode_consumer_pdata *pdata = dev_get_platdata(dev); + struct gpio_desc *desc; + + desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + pdata->gpio_ok = true; + + return 0; +} + +static struct platform_driver gpio_swnode_consumer_driver = { + .probe = gpio_swnode_consumer_probe, + .driver = { + .name = GPIO_SWNODE_TEST_CONSUMER, + }, +}; + +static void gpio_swnode_lookup_by_primary(struct kunit *test) +{ + struct gpio_swnode_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct platform_device *pdev; + bool bound = false; + int ret; + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_test_provider_swnode, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_SWNODE_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_swnode_pdata_template, + .size_data = sizeof(gpio_swnode_pdata_template), + .properties = properties, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + scoped_guard(device, &pdev->dev) + bound = device_is_bound(&pdev->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + pdata = dev_get_platdata(&pdev->dev); + KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); +} + +static void gpio_swnode_lookup_by_secondary(struct kunit *test) +{ + struct gpio_swnode_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct fwnode_handle *primary; + struct platform_device *pdev; + bool bound = false; + int ret; + + /* + * Can't live on the stack as it will still get referenced in cleanup + * path after this function returns. + */ + primary = kunit_kzalloc(test, sizeof(*primary), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary); + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode_init(primary, NULL); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .fwnode = primary, + .swnode = &gpio_test_provider_swnode, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_SWNODE_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_swnode_pdata_template, + .size_data = sizeof(gpio_swnode_pdata_template), + .properties = properties, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + scoped_guard(device, &pdev->dev) + bound = device_is_bound(&pdev->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + pdata = dev_get_platdata(&pdev->dev); + KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); +} + +static struct kunit_case gpio_swnode_lookup_tests[] = { + KUNIT_CASE(gpio_swnode_lookup_by_primary), + KUNIT_CASE(gpio_swnode_lookup_by_secondary), + { } +}; + +static struct kunit_suite gpio_swnode_lookup_test_suite = { + .name = "gpio-swnode-lookup", + .test_cases = gpio_swnode_lookup_tests, +}; + +static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier); + +struct gpio_unbind_consumer_drvdata { + struct device *dev; + struct gpio_desc *desc; + struct notifier_block nb; + int set_retval; +}; + +static int gpio_unbind_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct gpio_unbind_consumer_drvdata *drvdata = + container_of(nb, struct gpio_unbind_consumer_drvdata, nb); + struct device *dev = data; + + if (dev != drvdata->dev) + return NOTIFY_DONE; + + drvdata->set_retval = gpiod_set_value_cansleep(drvdata->desc, 0); + + return NOTIFY_OK; +} + +static void gpio_unbind_unregister_notifier(void *data) +{ + struct notifier_block *nb = data; + + blocking_notifier_chain_unregister(&gpio_unbind_notifier, nb); +} + +static int gpio_unbind_consumer_probe(struct platform_device *pdev) +{ + struct gpio_unbind_consumer_drvdata *data; + struct device *dev = &pdev->dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + + data->desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); + if (IS_ERR(data->desc)) + return PTR_ERR(data->desc); + + data->nb.notifier_call = gpio_unbind_notify; + ret = blocking_notifier_chain_register(&gpio_unbind_notifier, &data->nb); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, gpio_unbind_unregister_notifier, &data->nb); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + return 0; +} + +static struct platform_driver gpio_unbind_consumer_driver = { + .probe = gpio_unbind_consumer_probe, + .driver = { + .name = GPIO_UNBIND_TEST_CONSUMER, + }, +}; + +static void gpio_unbind_with_consumers(struct kunit *test) +{ + struct gpio_unbind_consumer_drvdata *cons_data; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct platform_device *prvd, *cons; + bool bound = false; + int ret; + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_unbind_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_test_provider_swnode, + }; + + prvd = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_UNBIND_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .properties = properties, + }; + + cons = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + + wait_for_device_probe(); + scoped_guard(device, &cons->dev) + bound = device_is_bound(&cons->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + kunit_platform_device_unregister(test, prvd); + + ret = blocking_notifier_call_chain(&gpio_unbind_notifier, 0, &cons->dev); + KUNIT_ASSERT_EQ(test, ret, NOTIFY_OK); + + scoped_guard(device, &cons->dev) { + cons_data = platform_get_drvdata(cons); + ret = cons_data->set_retval; + } + + KUNIT_ASSERT_EQ(test, ret, -ENODEV); +} + +static struct kunit_case gpio_unbind_with_consumers_tests[] = { + KUNIT_CASE(gpio_unbind_with_consumers), + { } +}; + +static struct kunit_suite gpio_unbind_with_consumers_test_suite = { + .name = "gpio-unbind-with-consumers", + .test_cases = gpio_unbind_with_consumers_tests, +}; + +kunit_test_suites( + &gpio_swnode_lookup_test_suite, + &gpio_unbind_with_consumers_test_suite, +); + +MODULE_DESCRIPTION("Test module for the GPIO subsystem"); +MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1e6dce430dca0..c72eec54cb196 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -55,7 +55,7 @@ /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); -static dev_t gpio_devt; +static dev_t gpio_devt __ro_after_init; #define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */ static int gpio_bus_match(struct device *dev, const struct device_driver *drv) @@ -114,7 +114,7 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc); static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc); static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc); -static bool gpiolib_initialized; +static bool gpiolib_initialized __ro_after_init; const char *gpiod_get_label(struct gpio_desc *desc) { @@ -491,6 +491,28 @@ int gpiod_get_direction(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_get_direction); +/** + * gpiod_is_single_ended - check if the GPIO is configured as single-ended + * @desc: the GPIO descriptor to check + * + * Returns true if the GPIO is configured as either Open Drain or Open Source. + * In these modes, the direction of the line cannot always be reliably + * determined by reading hardware registers, as the "off" state (High-Z) + * is physically indistinguishable from an input state. + */ +bool gpiod_is_single_ended(struct gpio_desc *desc) +{ + if (!desc) + return false; + + if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags) || + test_bit(GPIOD_FLAG_OPEN_SOURCE, &desc->flags)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(gpiod_is_single_ended); + /* * Add a new chip to the global chips list, keeping the list of chips sorted * by range(means [base, base + ngpio - 1]) order. @@ -5340,7 +5362,7 @@ EXPORT_SYMBOL_GPL(gpiod_put_array); * gpio_device of the GPIO chip with the firmware node and then simply * bind it to this stub driver. */ -static struct device_driver gpio_stub_drv = { +static struct device_driver gpio_stub_drv __ro_after_init = { .name = "gpio_stub_drv", .bus = &gpio_bus_type, }; @@ -5498,8 +5520,8 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) if (gc->label) seq_printf(s, ", %s", gc->label); if (gc->can_sleep) - seq_printf(s, ", can sleep"); - seq_printf(s, ":\n"); + seq_puts(s, ", can sleep"); + seq_puts(s, ":\n"); if (gc->dbg_show) gc->dbg_show(s, gc); |
