diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-08 11:55:21 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-08 11:55:21 -0800 |
commit | d20f7a09e5eeeeef5db679adc9a490fecb6a4c87 (patch) | |
tree | 94a01e0ba407a5cd41ab05ec624807658a36b54f /drivers/gpio | |
parent | dd72945c43d34bee496b847e021069dc31f7398f (diff) | |
parent | 7d0003da6297eb128f3490e396e6fc6df71557cd (diff) |
Merge tag 'gpio-updates-for-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski:
"We have a single new driver, new features in others and some cleanups
all over the place.
Nothing really stands out and it is all relatively small.
- new driver: gpio-modepin (plus relevant change in zynqmp firmware)
- add interrupt support to gpio-virtio
- enable the 'gpio-line-names' property in the DT bindings for
gpio-rockchip
- use the subsystem helpers where applicable in gpio-uniphier instead
of accessing IRQ structures directly
- code shrink in gpio-xilinx
- add interrupt to gpio-mlxbf2 (and include the removal of custom
interrupt code from the mellanox ethernet driver)
- support multiple interrupts per bank in gpio-tegra186 (and force
one interrupt per bank in older models)
- fix GPIO line IRQ offset calculation in gpio-realtek-otto
- drop unneeded MODULE_ALIAS expansions in multiple drivers
- code cleanup in gpio-aggregator
- minor improvements in gpio-max730x and gpio-mc33880
- Kconfig cleanups"
* tag 'gpio-updates-for-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
virtio_gpio: drop packed attribute
gpio: virtio: Add IRQ support
gpio: realtek-otto: fix GPIO line IRQ offset
gpio: clean up Kconfig file
net: mellanox: mlxbf_gige: Replace non-standard interrupt handling
gpio: mlxbf2: Introduce IRQ support
gpio: mc33880: Drop if with an always false condition
gpio: max730x: Make __max730x_remove() return void
gpio: aggregator: Wrap access to gpiochip_fwd.tmp[]
gpio: modepin: Add driver support for modepin GPIO controller
dt-bindings: gpio: zynqmp: Add binding documentation for modepin
firmware: zynqmp: Add MMIO read and write support for PS_MODE pin
gpio: tps65218: drop unneeded MODULE_ALIAS
gpio: max77620: drop unneeded MODULE_ALIAS
gpio: xilinx: simplify getting .driver_data
gpio: tegra186: Support multiple interrupts per bank
gpio: tegra186: Force one interrupt per bank
gpio: uniphier: Use helper functions to get private data from IRQ data
gpio: uniphier: Use helper function to get IRQ hardware number
dt-bindings: gpio: add gpio-line-names to rockchip,gpio-bank.yaml
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 123 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-aggregator.c | 25 | ||||
-rw-r--r-- | drivers/gpio/gpio-max7300.c | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-max7301.c | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-max730x.c | 6 | ||||
-rw-r--r-- | drivers/gpio/gpio-max77620.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-mc33880.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-mlxbf2.c | 142 | ||||
-rw-r--r-- | drivers/gpio/gpio-realtek-otto.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-tegra186.c | 114 | ||||
-rw-r--r-- | drivers/gpio/gpio-tps65218.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-uniphier.c | 18 | ||||
-rw-r--r-- | drivers/gpio/gpio-virtio.c | 302 | ||||
-rw-r--r-- | drivers/gpio/gpio-xilinx.c | 6 | ||||
-rw-r--r-- | drivers/gpio/gpio-zynqmp-modepin.c | 162 |
16 files changed, 802 insertions, 111 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index fae5141251e5..072ed610f9c6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -15,7 +15,7 @@ menuconfig GPIOLIB bool "GPIO Support" help This enables GPIO support through the generic GPIO library. - You only need to enable this, if you also want to enable + You only need to enable this if you also want to enable one or more of the GPIO drivers below. If unsure, say N. @@ -140,8 +140,8 @@ config GPIO_AMDPT depends on ACPI select GPIO_GENERIC help - driver for GPIO functionality on Promontory IOHub - Require ACPI ASL code to enumerate as a platform device. + Driver for GPIO functionality on Promontory IOHub. + Requires ACPI ASL code to enumerate as a platform device. config GPIO_ASPEED tristate "Aspeed GPIO support" @@ -306,7 +306,7 @@ config GPIO_HISI help Say Y or M here to build support for the HiSilicon GPIO controller driver GPIO block. - This GPIO controller support double-edge interrupt and multi-core + This GPIO controller supports double-edge interrupt and multi-core concurrent access. config GPIO_HLWD @@ -326,7 +326,7 @@ config GPIO_ICH help Say yes here to support the GPIO functionality of a number of Intel ICH-based chipsets. Currently supported devices: ICH6, ICH7, ICH8 - ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg + ICH9, ICH10, Series 5/3400 (e.g. Ibex Peak), Series 6/C200 (e.g. Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake). If unsure, say N. @@ -337,7 +337,7 @@ config GPIO_IOP select GPIO_GENERIC help Say yes here to support the GPIO functionality of a number of Intel - IOP32X or IOP33X. + IOP32X or IOP33X series of chips. If unsure, say N. @@ -364,7 +364,7 @@ config GPIO_LOONGSON bool "Loongson-2/3 GPIO support" depends on CPU_LOONGSON2EF || CPU_LOONGSON64 help - driver for GPIO functionality on Loongson-2F/3A/3B processors. + Driver for GPIO functionality on Loongson-2F/3A/3B processors. config GPIO_LPC18XX tristate "NXP LPC18XX/43XX GPIO support" @@ -392,15 +392,15 @@ config GPIO_MENZ127 depends on MCB select GPIO_GENERIC help - Say yes here to support the MEN 16Z127 GPIO Controller + Say yes here to support the MEN 16Z127 GPIO Controller. config GPIO_MM_LANTIQ bool "Lantiq Memory mapped GPIOs" depends on LANTIQ && SOC_XWAY help This enables support for memory mapped GPIOs on the External Bus Unit - (EBU) found on Lantiq SoCs. The gpios are output only as they are - created by attaching a 16bit latch to the bus. + (EBU) found on Lantiq SoCs. The GPIOs are output only as they are + created by attaching a 16-bit latch to the bus. config GPIO_MPC5200 def_bool y @@ -424,7 +424,7 @@ config GPIO_MT7621 select GPIO_GENERIC select GPIOLIB_IRQCHIP help - Say yes here to support the Mediatek MT7621 SoC GPIO device + Say yes here to support the Mediatek MT7621 SoC GPIO device. config GPIO_MVEBU def_bool y @@ -469,7 +469,7 @@ config GPIO_PL061 select IRQ_DOMAIN select GPIOLIB_IRQCHIP help - Say yes here to support the PrimeCell PL061 GPIO device + Say yes here to support the PrimeCell PL061 GPIO device. config GPIO_PMIC_EIC_SPRD tristate "Spreadtrum PMIC EIC support" @@ -483,7 +483,7 @@ config GPIO_PXA bool "PXA GPIO support" depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST help - Say yes here to support the PXA GPIO device + Say yes here to support the PXA GPIO device. config GPIO_RCAR tristate "Renesas R-Car and RZ/G GPIO support" @@ -573,7 +573,7 @@ config GPIO_SPEAR_SPICS depends on PLAT_SPEAR select GENERIC_IRQ_CHIP help - Say yes here to support ST SPEAr SPI Chip Select as GPIO device + Say yes here to support ST SPEAr SPI Chip Select as GPIO device. config GPIO_SPRD tristate "Spreadtrum GPIO support" @@ -598,8 +598,8 @@ config GPIO_STP_XWAY help This enables support for the Serial To Parallel (STP) unit found on XWAY SoC. The STP allows the SoC to drive a shift registers cascade, - that can be up to 24 bit. This peripheral is aimed at driving leds. - Some of the gpios/leds can be auto updated by the soc with dsl and + that can be up to 24 bits. This peripheral is aimed at driving LEDs. + Some of the GPIOs/LEDs can be auto updated by the SoC with DSL and phy status. config GPIO_SYSCON @@ -679,10 +679,10 @@ config GPIO_VISCONTI Say yes here to support GPIO on Tohisba Visconti. config GPIO_VR41XX - tristate "NEC VR4100 series General-purpose I/O Uint support" + tristate "NEC VR4100 series General-purpose I/O Unit support" depends on CPU_VR41XX help - Say yes here to support the NEC VR4100 series General-purpose I/O Uint + Say yes here to support the NEC VR4100 series General-purpose I/O Unit. config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" @@ -690,14 +690,14 @@ config GPIO_VX855 select MFD_CORE select MFD_VX855 help - Support access to the VX855/VX875 GPIO lines through the gpio library. + Support access to the VX855/VX875 GPIO lines through the GPIO library. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the + This driver provides common support for accessing the device. + Additional drivers must be enabled in order to use the functionality of the device. config GPIO_WCD934X - tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver" + tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver" depends on MFD_WCD934X && OF_GPIO help This driver is to support GPIO block found on the Qualcomm Technologies @@ -727,7 +727,7 @@ config GPIO_XILINX select GPIOLIB_IRQCHIP depends on OF_GPIO help - Say yes here to support the Xilinx FPGA GPIO device + Say yes here to support the Xilinx FPGA GPIO device. config GPIO_XLP tristate "Netlogic XLP GPIO support" @@ -748,7 +748,7 @@ config GPIO_XTENSA depends on !SMP help Say yes here to support the Xtensa internal GPIO32 IMPWIRE (input) - and EXPSTATE (output) ports + and EXPSTATE (output) ports. config GPIO_ZEVIO bool "LSI ZEVIO SoC memory mapped GPIOs" @@ -763,6 +763,18 @@ config GPIO_ZYNQ help Say yes here to support Xilinx Zynq GPIO controller. +config GPIO_ZYNQMP_MODEPIN + tristate "ZynqMP ps-mode pin GPIO configuration driver" + depends on ZYNQMP_FIRMWARE + default ZYNQMP_FIRMWARE + help + Say yes here to support the ZynqMP ps-mode pin GPIO configuration + driver. + + This ps-mode pin GPIO driver is based on GPIO framework. PS_MODE + is 4-bits boot mode pins. It sets and gets the status of + the ps-mode pin. Every pin can be configured as input/output. + config GPIO_LOONGSON1 tristate "Loongson1 GPIO support" depends on MACH_LOONGSON32 @@ -773,12 +785,12 @@ config GPIO_LOONGSON1 config GPIO_AMD_FCH tristate "GPIO support for AMD Fusion Controller Hub (G-series SOCs)" help - This option enables driver for GPIO on AMDs Fusion Controller Hub, - as found on G-series SOCs (eg. GX-412TC) + This option enables driver for GPIO on AMD's Fusion Controller Hub, + as found on G-series SOCs (e.g. GX-412TC). - Note: This driver doesn't registers itself automatically, as it - needs to be provided with platform specific configuration. - (See eg. CONFIG_PCENGINES_APU2.) + Note: This driver doesn't register itself automatically, as it + needs to be provided with platform-specific configuration. + (See e.g. CONFIG_PCENGINES_APU2.) config GPIO_MSC313 bool "MStar MSC313 GPIO support" @@ -788,7 +800,7 @@ config GPIO_MSC313 select IRQ_DOMAIN_HIERARCHY help Say Y here to support the main GPIO block on MStar/SigmaStar - ARMv7 based SoCs. + ARMv7-based SoCs. config GPIO_IDT3243X tristate "IDT 79RC3243X GPIO support" @@ -797,7 +809,7 @@ config GPIO_IDT3243X select GPIOLIB_IRQCHIP help Select this option to enable GPIO driver for - IDT 79RC3243X based devices like Mikrotik RB532. + IDT 79RC3243X-based devices like Mikrotik RB532. To compile this driver as a module, choose M here: the module will be called gpio-idt3243x. @@ -875,7 +887,7 @@ config GPIO_IT87 well. To compile this driver as a module, choose M here: the module will - be called gpio_it87 + be called gpio_it87. config GPIO_SCH tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO" @@ -891,7 +903,7 @@ config GPIO_SCH powered by the core power rail and are turned off during sleep modes (S3 and higher). The remaining four GPIOs are powered by the Intel SCH suspend power supply. These GPIOs remain - active during S3. The suspend powered GPIOs can be used to wake the + active during S3. The suspend-powered GPIOs can be used to wake the system from the Suspend-to-RAM state. The Intel Tunnel Creek processor has 5 GPIOs powered by the @@ -1044,7 +1056,7 @@ config GPIO_PCA953X_IRQ select GPIOLIB_IRQCHIP help Say yes here to enable the pca953x to be used as an interrupt - controller. It requires the driver to be built in the kernel. + controller. config GPIO_PCA9570 tristate "PCA9570 4-Bit I2C GPO expander" @@ -1171,7 +1183,7 @@ config GPIO_CRYSTAL_COVE help Support for GPIO pins on Crystal Cove PMIC. - Say Yes if you have a Intel SoC based tablet with Crystal Cove PMIC + Say Yes if you have a Intel SoC-based tablet with Crystal Cove PMIC inside. This driver can also be built as a module. If so, the module will be @@ -1201,7 +1213,7 @@ config GPIO_DA9055 Say yes here to enable the GPIO driver for the DA9055 chip. The Dialog DA9055 PMIC chip has 3 GPIO pins that can be - be controller by this driver. + be controlled by this driver. If driver is built as a module it will be called gpio-da9055. @@ -1223,7 +1235,7 @@ config HTC_EGPIO help This driver supports the CPLD egpio chip present on several HTC phones. It provides basic support for input - pins, output pins, and irqs. + pins, output pins, and IRQs. config GPIO_JANZ_TTL tristate "Janz VMOD-TTL Digital IO Module" @@ -1284,8 +1296,8 @@ config GPIO_MAX77620 help GPIO driver for MAX77620 and MAX20024 PMIC from Maxim Semiconductor. MAX77620 PMIC has 8 pins that can be configured as GPIOs. The - driver also provides interrupt support for each of the gpios. - Say yes here to enable the max77620 to be used as gpio controller. + driver also provides interrupt support for each of the GPIOs. + Say yes here to enable the max77620 to be used as GPIO controller. config GPIO_MAX77650 tristate "Maxim MAX77650/77651 GPIO support" @@ -1307,8 +1319,8 @@ config GPIO_RC5T583 help Select this option to enable GPIO driver for the Ricoh RC5T583 chip family. - This driver provides the support for driving/reading the gpio pins - of RC5T583 device through standard gpio library. + This driver provides the support for driving/reading the GPIO pins + of RC5T583 device through standard GPIO library. config GPIO_SL28CPLD tristate "Kontron sl28cpld GPIO support" @@ -1377,7 +1389,7 @@ config GPIO_TPS65912 tristate "TI TPS65912 GPIO" depends on MFD_TPS65912 help - This driver supports TPS65912 gpio chip + This driver supports TPS65912 GPIO chip. config GPIO_TPS68470 bool "TPS68470 GPIO" @@ -1385,7 +1397,7 @@ config GPIO_TPS68470 help Select this option to enable GPIO driver for the TPS68470 chip family. - There are 7 GPIOs and few sensor related GPIOs supported + There are 7 GPIOs and few sensor-related GPIOs supported by the TPS68470. While the 7 GPIOs can be configured as input or output as appropriate, the sensor related GPIOs are "output only" GPIOs. @@ -1430,7 +1442,7 @@ config GPIO_WHISKEY_COVE help Support for GPIO pins on Whiskey Cove PMIC. - Say Yes if you have a Intel SoC based tablet with Whiskey Cove PMIC + Say Yes if you have an Intel SoC-based tablet with Whiskey Cove PMIC inside. This driver can also be built as a module. If so, the module will be @@ -1467,10 +1479,10 @@ config GPIO_AMD8111 depends on X86 || COMPILE_TEST depends on HAS_IOPORT_MAP help - The AMD 8111 south bridge contains 32 GPIO pins which can be used. + The AMD 8111 southbridge contains 32 GPIO pins which can be used. - Note, that usually system firmware/ACPI handles GPIO pins on their - own and users might easily break their systems with uncarefull usage + Note that usually system firmware/ACPI handles GPIO pins on their + own and users might easily break their systems with uncareful usage of this driver! If unsure, say N @@ -1518,22 +1530,22 @@ config GPIO_ML_IOH select GENERIC_IRQ_CHIP help ML7213 is companion chip for Intel Atom E6xx series. - This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output - Hub) which is for IVI(In-Vehicle Infotainment) use. + This driver can be used for OKI SEMICONDUCTOR ML7213 IOH (Input/Output + Hub) which is for IVI (In-Vehicle Infotainment) use. This driver can access the IOH's GPIO device. config GPIO_PCH - tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7223/ML7831) GPIO" + tristate "Intel EG20T PCH/LAPIS Semiconductor IOH (ML7223/ML7831) GPIO" depends on X86_32 || MIPS || COMPILE_TEST select GENERIC_IRQ_CHIP help - This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff - which is an IOH(Input/Output Hub) for x86 embedded processor. + This driver is for PCH (Platform Controller Hub) GPIO of Intel Topcliff, + which is an IOH (Input/Output Hub) for x86 embedded processor. This driver can access PCH GPIO device. - This driver also can be used for LAPIS Semiconductor IOH(Input/ + This driver also can be used for LAPIS Semiconductor IOH (Input/ Output Hub), ML7223 and ML7831. - ML7223 IOH is for MP(Media Phone) use. + ML7223 IOH is for MP (Media Phone) use. ML7831 IOH is for general purpose use. ML7223/ML7831 is companion chip for Intel Atom E6xx series. ML7223/ML7831 is completely compatible for Intel EG20T PCH. @@ -1584,7 +1596,7 @@ config GPIO_74X164 help Driver for 74x164 compatible serial-in/parallel-out 8-outputs shift registers. This driver can be used to provide access - to more gpio outputs. + to more GPIO outputs. config GPIO_MAX3191X tristate "Maxim MAX3191x industrial serializer" @@ -1674,6 +1686,7 @@ config GPIO_MOCKUP config GPIO_VIRTIO tristate "VirtIO GPIO support" depends on VIRTIO + select GPIOLIB_IRQCHIP help Say Y here to enable guest support for virtio-based GPIO controllers. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fbcda637d5e1..71ee9fc2ff83 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -184,3 +184,4 @@ obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o +obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN) += gpio-zynqmp-modepin.o diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 34e35b64dcdc..e9671d1660ef 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -247,6 +247,11 @@ struct gpiochip_fwd { unsigned long tmp[]; /* values and descs for multiple ops */ }; +#define fwd_tmp_values(fwd) &(fwd)->tmp[0] +#define fwd_tmp_descs(fwd) (void *)&(fwd)->tmp[BITS_TO_LONGS((fwd)->chip.ngpio)] + +#define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios)) + static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset) { struct gpiochip_fwd *fwd = gpiochip_get_data(chip); @@ -279,15 +284,11 @@ static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset) static int gpio_fwd_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask, unsigned long *bits) { - struct gpio_desc **descs; - unsigned long *values; + struct gpio_desc **descs = fwd_tmp_descs(fwd); + unsigned long *values = fwd_tmp_values(fwd); unsigned int i, j = 0; int error; - /* Both values bitmap and desc pointers are stored in tmp[] */ - values = &fwd->tmp[0]; - descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)]; - bitmap_clear(values, 0, fwd->chip.ngpio); for_each_set_bit(i, mask, fwd->chip.ngpio) descs[j++] = fwd->descs[i]; @@ -333,14 +334,10 @@ static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value) static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask, unsigned long *bits) { - struct gpio_desc **descs; - unsigned long *values; + struct gpio_desc **descs = fwd_tmp_descs(fwd); + unsigned long *values = fwd_tmp_values(fwd); unsigned int i, j = 0; - /* Both values bitmap and desc pointers are stored in tmp[] */ - values = &fwd->tmp[0]; - descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)]; - for_each_set_bit(i, mask, fwd->chip.ngpio) { __assign_bit(j, values, test_bit(i, bits)); descs[j++] = fwd->descs[i]; @@ -398,8 +395,8 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, unsigned int i; int error; - fwd = devm_kzalloc(dev, struct_size(fwd, tmp, - BITS_TO_LONGS(ngpios) + ngpios), GFP_KERNEL); + fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)), + GFP_KERNEL); if (!fwd) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c index 19cc2ed6a3f5..b2b547dd6e84 100644 --- a/drivers/gpio/gpio-max7300.c +++ b/drivers/gpio/gpio-max7300.c @@ -50,7 +50,9 @@ static int max7300_probe(struct i2c_client *client, static int max7300_remove(struct i2c_client *client) { - return __max730x_remove(&client->dev); + __max730x_remove(&client->dev); + + return 0; } static const struct i2c_device_id max7300_id[] = { diff --git a/drivers/gpio/gpio-max7301.c b/drivers/gpio/gpio-max7301.c index 1307c243b4e9..5862d73bf325 100644 --- a/drivers/gpio/gpio-max7301.c +++ b/drivers/gpio/gpio-max7301.c @@ -66,7 +66,9 @@ static int max7301_probe(struct spi_device *spi) static int max7301_remove(struct spi_device *spi) { - return __max730x_remove(&spi->dev); + __max730x_remove(&spi->dev); + + return 0; } static const struct spi_device_id max7301_id[] = { diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c index b8c1fe20f49a..bb5cf14ae4c8 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -220,18 +220,14 @@ exit_destroy: } EXPORT_SYMBOL_GPL(__max730x_probe); -int __max730x_remove(struct device *dev) +void __max730x_remove(struct device *dev) { struct max7301 *ts = dev_get_drvdata(dev); - if (ts == NULL) - return -ENODEV; - /* Power down the chip and disable IRQ output */ ts->write(dev, 0x04, 0x00); gpiochip_remove(&ts->chip); mutex_destroy(&ts->lock); - return 0; } EXPORT_SYMBOL_GPL(__max730x_remove); diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 82b3a913005d..ebf9dea6546b 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -365,5 +365,4 @@ module_platform_driver(max77620_gpio_driver); MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC"); MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); MODULE_AUTHOR("Chaitanya Bandi <bandik@nvidia.com>"); -MODULE_ALIAS("platform:max77620-gpio"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index f8194f7c6186..31d2be1bebc8 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -139,8 +139,6 @@ static int mc33880_remove(struct spi_device *spi) struct mc33880 *mc; mc = spi_get_drvdata(spi); - if (!mc) - return -ENODEV; gpiochip_remove(&mc->chip); mutex_destroy(&mc->lock); diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 40a052bc6784..3d89912a05b8 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES + */ + #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/device.h> #include <linux/gpio/driver.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/kernel.h> @@ -43,9 +48,14 @@ #define YU_GPIO_MODE0 0x0c #define YU_GPIO_DATASET 0x14 #define YU_GPIO_DATACLEAR 0x18 +#define YU_GPIO_CAUSE_RISE_EN 0x44 +#define YU_GPIO_CAUSE_FALL_EN 0x48 #define YU_GPIO_MODE1_CLEAR 0x50 #define YU_GPIO_MODE0_SET 0x54 #define YU_GPIO_MODE0_CLEAR 0x58 +#define YU_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x80 +#define YU_GPIO_CAUSE_OR_EVTEN0 0x94 +#define YU_GPIO_CAUSE_OR_CLRCAUSE 0x98 struct mlxbf2_gpio_context_save_regs { u32 gpio_mode0; @@ -55,6 +65,7 @@ struct mlxbf2_gpio_context_save_regs { /* BlueField-2 gpio block context structure. */ struct mlxbf2_gpio_context { struct gpio_chip gc; + struct irq_chip irq_chip; /* YU GPIO blocks address */ void __iomem *gpio_io; @@ -218,15 +229,114 @@ static int mlxbf2_gpio_direction_output(struct gpio_chip *chip, return ret; } +static void mlxbf2_gpio_irq_enable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); +} + +static void mlxbf2_gpio_irq_disable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + val &= ~BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); +} + +static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr) +{ + struct mlxbf2_gpio_context *gs = ptr; + struct gpio_chip *gc = &gs->gc; + unsigned long pending; + u32 level; + + pending = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0); + writel(pending, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + + for_each_set_bit(level, &pending, gc->ngpio) { + int gpio_irq = irq_find_mapping(gc->irq.domain, level); + generic_handle_irq(gpio_irq); + } + + return IRQ_RETVAL(pending); +} + +static int +mlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + unsigned long flags; + bool fall = false; + bool rise = false; + u32 val; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_BOTH: + fall = true; + rise = true; + break; + case IRQ_TYPE_EDGE_RISING: + rise = true; + break; + case IRQ_TYPE_EDGE_FALLING: + fall = true; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + if (fall) { + val = readl(gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); + } + + if (rise) { + val = readl(gs->gpio_io + YU_GPIO_CAUSE_RISE_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_RISE_EN); + } + spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); + + return 0; +} + /* BlueField-2 GPIO driver initialization routine. */ static int mlxbf2_gpio_probe(struct platform_device *pdev) { struct mlxbf2_gpio_context *gs; struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; struct gpio_chip *gc; unsigned int npins; - int ret; + const char *name; + int ret, irq; + + name = dev_name(dev); gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL); if (!gs) @@ -266,6 +376,34 @@ mlxbf2_gpio_probe(struct platform_device *pdev) gc->ngpio = npins; gc->owner = THIS_MODULE; + irq = platform_get_irq(pdev, 0); + if (irq >= 0) { + gs->irq_chip.name = name; + gs->irq_chip.irq_set_type = mlxbf2_gpio_irq_set_type; + gs->irq_chip.irq_enable = mlxbf2_gpio_irq_enable; + gs->irq_chip.irq_disable = mlxbf2_gpio_irq_disable; + + girq = &gs->gc.irq; + girq->chip = &gs->irq_chip; + girq->handler = handle_simple_irq; + girq->default_type = IRQ_TYPE_NONE; + /* This will let us handle the parent IRQ in the driver */ + girq->num_parents = 0; + girq->parents = NULL; + girq->parent_handler = NULL; + + /* + * Directly request the irq here instead of passing + * a flow-handler because the irq is shared. + */ + ret = devm_request_irq(dev, irq, mlxbf2_gpio_irq_handler, + IRQF_SHARED, name, gs); + if (ret) { + dev_err(dev, "failed to request IRQ"); + return ret; + } + } + platform_set_drvdata(pdev, gs); ret = devm_gpiochip_add_data(dev, &gs->gc, gs); @@ -320,5 +458,5 @@ static struct platform_driver mlxbf2_gpio_driver = { module_platform_driver(mlxbf2_gpio_driver); MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver"); -MODULE_AUTHOR("Mellanox Technologies"); +MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index eeeb39bc171d..bd75401b549d 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -205,7 +205,7 @@ static void realtek_gpio_irq_handler(struct irq_desc *desc) status = realtek_gpio_read_isr(ctrl, lines_done / 8); port_pin_count = min(gc->ngpio - lines_done, 8U); for_each_set_bit(offset, &status, port_pin_count) - generic_handle_domain_irq(gc->irq.domain, offset); + generic_handle_domain_irq(gc->irq.domain, offset + lines_done); } chained_irq_exit(irq_chip, desc); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index c99858f40a27..c026e7141e4e 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -69,6 +69,8 @@ struct tegra_gpio_soc { const char *name; unsigned int instance; + unsigned int num_irqs_per_bank; + const struct tegra186_pin_range *pin_ranges; unsigned int num_pin_ranges; const char *pinmux; @@ -81,6 +83,8 @@ struct tegra_gpio { unsigned int *irq; const struct tegra_gpio_soc *soc; + unsigned int num_irqs_per_bank; + unsigned int num_banks; void __iomem *secure; void __iomem *base; @@ -450,7 +454,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) struct irq_domain *domain = gpio->gpio.irq.domain; struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int parent = irq_desc_get_irq(desc); - unsigned int i, offset = 0; + unsigned int i, j, offset = 0; chained_irq_enter(chip, desc); @@ -463,7 +467,12 @@ static void tegra186_gpio_irq(struct irq_desc *desc) base = gpio->base + port->bank * 0x1000 + port->port * 0x200; /* skip ports that are not associated with this bank */ - if (parent != gpio->irq[port->bank]) + for (j = 0; j < gpio->num_irqs_per_bank; j++) { + if (parent == gpio->irq[port->bank * gpio->num_irqs_per_bank + j]) + break; + } + + if (j == gpio->num_irqs_per_bank) goto skip; value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); @@ -565,6 +574,7 @@ static const struct of_device_id tegra186_pmc_of_match[] = { static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) { + struct device *dev = gpio->gpio.parent; unsigned int i, j; u32 value; @@ -583,17 +593,60 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 && (value & TEGRA186_GPIO_CTL_SCR_SEC_WEN) == 0) { - for (j = 0; j < 8; j++) { + /* + * On Tegra194 and later, each pin can be routed to one or more + * interrupts. + */ + for (j = 0; j < gpio->num_irqs_per_bank; j++) { + dev_dbg(dev, "programming default interrupt routing for port %s\n", + port->name); + offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); - value = readl(base + offset); - value = BIT(port->pins) - 1; - writel(value, base + offset); + /* + * By default we only want to route GPIO pins to IRQ 0. This works + * only under the assumption that we're running as the host kernel + * and hence all GPIO pins are owned by Linux. + * + * For cases where Linux is the guest OS, the hypervisor will have + * to configure the interrupt routing and pass only the valid + * interrupts via device tree. + */ + if (j == 0) { + value = readl(base + offset); + value = BIT(port->pins) - 1; + writel(value, base + offset); + } } } } } +static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +{ + struct device *dev = gpio->gpio.parent; + + if (gpio->num_irq > gpio->num_banks) { + if (gpio->num_irq % gpio->num_banks != 0) + goto error; + } + + if (gpio->num_irq < gpio->num_banks) + goto error; + + gpio->num_irqs_per_bank = gpio->num_irq / gpio->num_banks; + + if (gpio->num_irqs_per_bank > gpio->soc->num_irqs_per_bank) + goto error; + + return 0; + +error: + dev_err(dev, "invalid number of interrupts (%u) for %u banks\n", + gpio->num_irq, gpio->num_banks); + return -EINVAL; +} + static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; @@ -608,7 +661,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; gpio->soc = device_get_match_data(&pdev->dev); + gpio->gpio.label = gpio->soc->name; + gpio->gpio.parent = &pdev->dev; + + /* count the number of banks in the controller */ + for (i = 0; i < gpio->soc->num_ports; i++) + if (gpio->soc->ports[i].bank > gpio->num_banks) + gpio->num_banks = gpio->soc->ports[i].bank; + + gpio->num_banks++; + /* get register apertures */ gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); if (IS_ERR(gpio->secure)) { gpio->secure = devm_platform_ioremap_resource(pdev, 0); @@ -629,6 +692,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_irq = err; + err = tegra186_gpio_irqs_per_bank(gpio); + if (err < 0) + return err; + gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); if (!gpio->irq) @@ -642,9 +709,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->irq[i] = err; } - gpio->gpio.label = gpio->soc->name; - gpio->gpio.parent = &pdev->dev; - gpio->gpio.request = gpiochip_generic_request; gpio->gpio.free = gpiochip_generic_free; gpio->gpio.get_direction = tegra186_gpio_get_direction; @@ -708,7 +772,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler = tegra186_gpio_irq; irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; - irq->parents = gpio->irq; + + /* + * To simplify things, use a single interrupt per bank for now. Some + * chips support up to 8 interrupts per bank, which can be useful to + * distribute the load and decrease the processing latency for GPIOs + * but it also requires a more complicated interrupt routing than we + * currently program. + */ + if (gpio->num_irqs_per_bank > 1) { + irq->parents = devm_kcalloc(&pdev->dev, gpio->num_banks, + sizeof(*irq->parents), GFP_KERNEL); + if (!irq->parents) + return -ENOMEM; + + for (i = 0; i < gpio->num_banks; i++) + irq->parents[i] = gpio->irq[i * gpio->num_irqs_per_bank]; + + irq->num_parents = gpio->num_banks; + } else { + irq->num_parents = gpio->num_irq; + irq->parents = gpio->irq; + } + + if (gpio->soc->num_irqs_per_bank > 1) + tegra186_gpio_init_route_mapping(gpio); np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (np) { @@ -719,8 +807,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - tegra186_gpio_init_route_mapping(gpio); - irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio, sizeof(*irq->map), GFP_KERNEL); if (!irq->map) @@ -777,6 +863,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .ports = tegra186_main_ports, .name = "tegra186-gpio", .instance = 0, + .num_irqs_per_bank = 1, }; #define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -803,6 +890,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { .ports = tegra186_aon_ports, .name = "tegra186-gpio-aon", .instance = 1, + .num_irqs_per_bank = 1, }; #define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -854,6 +942,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = { .ports = tegra194_main_ports, .name = "tegra194-gpio", .instance = 0, + .num_irqs_per_bank = 8, .num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges), .pin_ranges = tegra194_main_pin_ranges, .pinmux = "nvidia,tegra194-pinmux", @@ -880,6 +969,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .ports = tegra194_aon_ports, .name = "tegra194-gpio-aon", .instance = 1, + .num_irqs_per_bank = 8, }; static const struct of_device_id tegra186_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 3517debe2b0b..912382be48e1 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -230,4 +230,3 @@ module_platform_driver(tps65218_gpio_driver); MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>"); MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tps65218-gpio"); diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 39dca147d587..19ce6675cbc0 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -179,8 +179,8 @@ static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) static void uniphier_gpio_irq_mask(struct irq_data *data) { - struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data); + u32 mask = BIT(irqd_to_hwirq(data)); uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, 0); @@ -189,8 +189,8 @@ static void uniphier_gpio_irq_mask(struct irq_data *data) static void uniphier_gpio_irq_unmask(struct irq_data *data) { - struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data); + u32 mask = BIT(irqd_to_hwirq(data)); uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, mask); @@ -199,8 +199,8 @@ static void uniphier_gpio_irq_unmask(struct irq_data *data) static int uniphier_gpio_irq_set_type(struct irq_data *data, unsigned int type) { - struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data); + u32 mask = BIT(irqd_to_hwirq(data)); u32 val = 0; if (type == IRQ_TYPE_EDGE_BOTH) { @@ -297,7 +297,8 @@ static int uniphier_gpio_irq_domain_activate(struct irq_domain *domain, struct uniphier_gpio_priv *priv = domain->host_data; struct gpio_chip *chip = &priv->chip; - return gpiochip_lock_as_irq(chip, data->hwirq + UNIPHIER_GPIO_IRQ_OFFSET); + return gpiochip_lock_as_irq(chip, + irqd_to_hwirq(data) + UNIPHIER_GPIO_IRQ_OFFSET); } static void uniphier_gpio_irq_domain_deactivate(struct irq_domain *domain, @@ -306,7 +307,8 @@ static void uniphier_gpio_irq_domain_deactivate(struct irq_domain *domain, struct uniphier_gpio_priv *priv = domain->host_data; struct gpio_chip *chip = &priv->chip; - gpiochip_unlock_as_irq(chip, data->hwirq + UNIPHIER_GPIO_IRQ_OFFSET); + gpiochip_unlock_as_irq(chip, + irqd_to_hwirq(data) + UNIPHIER_GPIO_IRQ_OFFSET); } static const struct irq_domain_ops uniphier_gpio_irq_domain_ops = { diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c index d24f1c9264bc..aeec4bf0b625 100644 --- a/drivers/gpio/gpio-virtio.c +++ b/drivers/gpio/gpio-virtio.c @@ -16,6 +16,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/spinlock.h> #include <linux/virtio_config.h> #include <uapi/linux/virtio_gpio.h> #include <uapi/linux/virtio_ids.h> @@ -28,12 +29,30 @@ struct virtio_gpio_line { unsigned int rxlen; }; +struct vgpio_irq_line { + u8 type; + bool disabled; + bool masked; + bool queued; + bool update_pending; + bool queue_pending; + + struct virtio_gpio_irq_request ireq ____cacheline_aligned; + struct virtio_gpio_irq_response ires ____cacheline_aligned; +}; + struct virtio_gpio { struct virtio_device *vdev; struct mutex lock; /* Protects virtqueue operation */ struct gpio_chip gc; struct virtio_gpio_line *lines; struct virtqueue *request_vq; + + /* irq support */ + struct virtqueue *event_vq; + struct mutex irq_lock; /* Protects irq operation */ + raw_spinlock_t eventq_lock; /* Protects queuing of the buffer */ + struct vgpio_irq_line *irq_lines; }; static int _virtio_gpio_req(struct virtio_gpio *vgpio, u16 type, u16 gpio, @@ -186,6 +205,238 @@ static void virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, NULL); } +/* Interrupt handling */ +static void virtio_gpio_irq_prepare(struct virtio_gpio *vgpio, u16 gpio) +{ + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[gpio]; + struct virtio_gpio_irq_request *ireq = &irq_line->ireq; + struct virtio_gpio_irq_response *ires = &irq_line->ires; + struct scatterlist *sgs[2], req_sg, res_sg; + int ret; + + if (WARN_ON(irq_line->queued || irq_line->masked || irq_line->disabled)) + return; + + ireq->gpio = cpu_to_le16(gpio); + sg_init_one(&req_sg, ireq, sizeof(*ireq)); + sg_init_one(&res_sg, ires, sizeof(*ires)); + sgs[0] = &req_sg; + sgs[1] = &res_sg; + + ret = virtqueue_add_sgs(vgpio->event_vq, sgs, 1, 1, irq_line, GFP_ATOMIC); + if (ret) { + dev_err(&vgpio->vdev->dev, "failed to add request to eventq\n"); + return; + } + + irq_line->queued = true; + virtqueue_kick(vgpio->event_vq); +} + +static void virtio_gpio_irq_enable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->disabled = false; + irq_line->masked = false; + irq_line->queue_pending = true; + raw_spin_unlock(&vgpio->eventq_lock); + + irq_line->update_pending = true; +} + +static void virtio_gpio_irq_disable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->disabled = true; + irq_line->masked = true; + irq_line->queue_pending = false; + raw_spin_unlock(&vgpio->eventq_lock); + + irq_line->update_pending = true; +} + +static void virtio_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->masked = true; + raw_spin_unlock(&vgpio->eventq_lock); +} + +static void virtio_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->masked = false; + + /* Queue the buffer unconditionally on unmask */ + virtio_gpio_irq_prepare(vgpio, d->hwirq); + raw_spin_unlock(&vgpio->eventq_lock); +} + +static int virtio_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_LOW: + type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW; + break; + case IRQ_TYPE_LEVEL_HIGH: + type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH; + break; + default: + dev_err(&vgpio->vdev->dev, "unsupported irq type: %u\n", type); + return -EINVAL; + } + + irq_line->type = type; + irq_line->update_pending = true; + + return 0; +} + +static void virtio_gpio_irq_bus_lock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + + mutex_lock(&vgpio->irq_lock); +} + +static void virtio_gpio_irq_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + u8 type = irq_line->disabled ? VIRTIO_GPIO_IRQ_TYPE_NONE : irq_line->type; + unsigned long flags; + + if (irq_line->update_pending) { + irq_line->update_pending = false; + virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_IRQ_TYPE, d->hwirq, type, + NULL); + + /* Queue the buffer only after interrupt is enabled */ + raw_spin_lock_irqsave(&vgpio->eventq_lock, flags); + if (irq_line->queue_pending) { + irq_line->queue_pending = false; + virtio_gpio_irq_prepare(vgpio, d->hwirq); + } + raw_spin_unlock_irqrestore(&vgpio->eventq_lock, flags); + } + + mutex_unlock(&vgpio->irq_lock); +} + +static struct irq_chip vgpio_irq_chip = { + .name = "virtio-gpio", + .irq_enable = virtio_gpio_irq_enable, + .irq_disable = virtio_gpio_irq_disable, + .irq_mask = virtio_gpio_irq_mask, + .irq_unmask = virtio_gpio_irq_unmask, + .irq_set_type = virtio_gpio_irq_set_type, + + /* These are required to implement irqchip for slow busses */ + .irq_bus_lock = virtio_gpio_irq_bus_lock, + .irq_bus_sync_unlock = virtio_gpio_irq_bus_sync_unlock, +}; + +static bool ignore_irq(struct virtio_gpio *vgpio, int gpio, + struct vgpio_irq_line *irq_line) +{ + bool ignore = false; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->queued = false; + + /* Interrupt is disabled currently */ + if (irq_line->masked || irq_line->disabled) { + ignore = true; + goto unlock; + } + + /* + * Buffer is returned as the interrupt was disabled earlier, but is + * enabled again now. Requeue the buffers. + */ + if (irq_line->ires.status == VIRTIO_GPIO_IRQ_STATUS_INVALID) { + virtio_gpio_irq_prepare(vgpio, gpio); + ignore = true; + goto unlock; + } + + if (WARN_ON(irq_line->ires.status != VIRTIO_GPIO_IRQ_STATUS_VALID)) + ignore = true; + +unlock: + raw_spin_unlock(&vgpio->eventq_lock); + + return ignore; +} + +static void virtio_gpio_event_vq(struct virtqueue *vq) +{ + struct virtio_gpio *vgpio = vq->vdev->priv; + struct device *dev = &vgpio->vdev->dev; + struct vgpio_irq_line *irq_line; + int gpio, ret; + unsigned int len; + + while (true) { + irq_line = virtqueue_get_buf(vgpio->event_vq, &len); + if (!irq_line) + break; + + if (len != sizeof(irq_line->ires)) { + dev_err(dev, "irq with incorrect length (%u : %u)\n", + len, (unsigned int)sizeof(irq_line->ires)); + continue; + } + + /* + * Find GPIO line number from the offset of irq_line within the + * irq_lines block. We can also get GPIO number from + * irq-request, but better not to rely on a buffer returned by + * remote. + */ + gpio = irq_line - vgpio->irq_lines; + WARN_ON(gpio >= vgpio->gc.ngpio); + + if (unlikely(ignore_irq(vgpio, gpio, irq_line))) + continue; + + ret = generic_handle_domain_irq(vgpio->gc.irq.domain, gpio); + if (ret) + dev_err(dev, "failed to handle interrupt: %d\n", ret); + }; +} + static void virtio_gpio_request_vq(struct virtqueue *vq) { struct virtio_gpio_line *line; @@ -210,14 +461,15 @@ static void virtio_gpio_free_vqs(struct virtio_device *vdev) static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio, struct virtio_device *vdev) { - const char * const names[] = { "requestq" }; + const char * const names[] = { "requestq", "eventq" }; vq_callback_t *cbs[] = { virtio_gpio_request_vq, + virtio_gpio_event_vq, }; - struct virtqueue *vqs[1] = { NULL }; + struct virtqueue *vqs[2] = { NULL, NULL }; int ret; - ret = virtio_find_vqs(vdev, 1, vqs, cbs, names, NULL); + ret = virtio_find_vqs(vdev, vgpio->irq_lines ? 2 : 1, vqs, cbs, names, NULL); if (ret) { dev_err(&vdev->dev, "failed to find vqs: %d\n", ret); return ret; @@ -225,11 +477,23 @@ static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio, if (!vqs[0]) { dev_err(&vdev->dev, "failed to find requestq vq\n"); - return -ENODEV; + goto out; } vgpio->request_vq = vqs[0]; + if (vgpio->irq_lines && !vqs[1]) { + dev_err(&vdev->dev, "failed to find eventq vq\n"); + goto out; + } + vgpio->event_vq = vqs[1]; + return 0; + +out: + if (vqs[0] || vqs[1]) + virtio_gpio_free_vqs(vdev); + + return -ENODEV; } static const char **virtio_gpio_get_names(struct virtio_gpio *vgpio, @@ -325,6 +589,30 @@ static int virtio_gpio_probe(struct virtio_device *vdev) vgpio->gc.owner = THIS_MODULE; vgpio->gc.can_sleep = true; + /* Interrupt support */ + if (virtio_has_feature(vdev, VIRTIO_GPIO_F_IRQ)) { + vgpio->irq_lines = devm_kcalloc(dev, ngpio, sizeof(*vgpio->irq_lines), GFP_KERNEL); + if (!vgpio->irq_lines) + return -ENOMEM; + + /* The event comes from the outside so no parent handler */ + vgpio->gc.irq.parent_handler = NULL; + vgpio->gc.irq.num_parents = 0; + vgpio->gc.irq.parents = NULL; + vgpio->gc.irq.default_type = IRQ_TYPE_NONE; + vgpio->gc.irq.handler = handle_level_irq; + vgpio->gc.irq.chip = &vgpio_irq_chip; + + for (i = 0; i < ngpio; i++) { + vgpio->irq_lines[i].type = VIRTIO_GPIO_IRQ_TYPE_NONE; + vgpio->irq_lines[i].disabled = true; + vgpio->irq_lines[i].masked = true; + } + + mutex_init(&vgpio->irq_lock); + raw_spin_lock_init(&vgpio->eventq_lock); + } + ret = virtio_gpio_alloc_vqs(vgpio, vdev); if (ret) return ret; @@ -357,7 +645,13 @@ static const struct virtio_device_id id_table[] = { }; MODULE_DEVICE_TABLE(virtio, id_table); +static const unsigned int features[] = { + VIRTIO_GPIO_F_IRQ, +}; + static struct virtio_driver virtio_gpio_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .id_table = id_table, .probe = virtio_gpio_probe, .remove = virtio_gpio_remove, diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index a1b66338d077..b6d3a57e27ed 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -371,8 +371,7 @@ static int __maybe_unused xgpio_resume(struct device *dev) static int __maybe_unused xgpio_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct xgpio_instance *gpio = platform_get_drvdata(pdev); + struct xgpio_instance *gpio = dev_get_drvdata(dev); clk_disable(gpio->clk); @@ -381,8 +380,7 @@ static int __maybe_unused xgpio_runtime_suspend(struct device *dev) static int __maybe_unused xgpio_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct xgpio_instance *gpio = platform_get_drvdata(pdev); + struct xgpio_instance *gpio = dev_get_drvdata(dev); return clk_enable(gpio->clk); } diff --git a/drivers/gpio/gpio-zynqmp-modepin.c b/drivers/gpio/gpio-zynqmp-modepin.c new file mode 100644 index 000000000000..a0d69387c153 --- /dev/null +++ b/drivers/gpio/gpio-zynqmp-modepin.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the ps-mode pin configuration. + * + * Copyright (c) 2021 Xilinx, Inc. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/firmware/xlnx-zynqmp.h> + +/* 4-bit boot mode pins */ +#define MODE_PINS 4 + +/** + * modepin_gpio_get_value - Get the state of the specified pin of GPIO device + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function reads the state of the specified pin of the GPIO device. + * + * Return: 0 if the pin is low, 1 if pin is high, -EINVAL wrong pin configured + * or error value. + */ +static int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin) +{ + u32 regval = 0; + int ret; + + ret = zynqmp_pm_bootmode_read(®val); + if (ret) + return ret; + + /* When [0:3] corresponding bit is set, then read output bit [8:11], + * if the bit is clear then read input bit [4:7] for status or value. + */ + if (regval & BIT(pin)) + return !!(regval & BIT(pin + 8)); + else + return !!(regval & BIT(pin + 4)); +} + +/** + * modepin_gpio_set_value - Modify the state of the pin with specified value + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * @state: value used to modify the state of the specified pin + * + * This function reads the state of the specified pin of the GPIO device, mask + * with the capture state of GPIO pin, and update pin of GPIO device. + * + * Return: None. + */ +static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, + int state) +{ + u32 bootpin_val = 0; + int ret; + + zynqmp_pm_bootmode_read(&bootpin_val); + + /* Configure pin as an output by set bit [0:3] */ + bootpin_val |= BIT(pin); + + if (state) + bootpin_val |= BIT(pin + 8); + else + bootpin_val &= ~BIT(pin + 8); + + /* Configure bootpin value */ + ret = zynqmp_pm_bootmode_write(bootpin_val); + if (ret) + pr_err("modepin: set value error %d for pin %d\n", ret, pin); +} + +/** + * modepin_gpio_dir_in - Set the direction of the specified GPIO pin as input + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * Return: 0 always + */ +static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) +{ + return 0; +} + +/** + * modepin_gpio_dir_out - Set the direction of the specified GPIO pin as output + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * @state: value to be written to specified pin + * + * Return: 0 always + */ +static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, + int state) +{ + return 0; +} + +/** + * modepin_gpio_probe - Initialization method for modepin_gpio + * @pdev: platform device instance + * + * Return: 0 on success, negative error otherwise. + */ +static int modepin_gpio_probe(struct platform_device *pdev) +{ + struct gpio_chip *chip; + int status; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + platform_set_drvdata(pdev, chip); + + /* configure the gpio chip */ + chip->base = -1; + chip->ngpio = MODE_PINS; + chip->owner = THIS_MODULE; + chip->parent = &pdev->dev; + chip->get = modepin_gpio_get_value; + chip->set = modepin_gpio_set_value; + chip->direction_input = modepin_gpio_dir_in; + chip->direction_output = modepin_gpio_dir_out; + chip->label = dev_name(&pdev->dev); + + /* modepin gpio registration */ + status = devm_gpiochip_add_data(&pdev->dev, chip, chip); + if (status) + return dev_err_probe(&pdev->dev, status, + "Failed to add GPIO chip\n"); + + return status; +} + +static const struct of_device_id modepin_platform_id[] = { + { .compatible = "xlnx,zynqmp-gpio-modepin", }, + { } +}; + +static struct platform_driver modepin_platform_driver = { + .driver = { + .name = "modepin-gpio", + .of_match_table = modepin_platform_id, + }, + .probe = modepin_gpio_probe, +}; + +module_platform_driver(modepin_platform_driver); + +MODULE_AUTHOR("Piyush Mehta <piyush.mehta@xilinx.com>"); +MODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration"); +MODULE_LICENSE("GPL v2"); |