aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig509
-rw-r--r--drivers/gpio/Makefile36
-rw-r--r--drivers/gpio/TODO142
-rw-r--r--drivers/gpio/gpio-104-dio-48e.c214
-rw-r--r--drivers/gpio/gpio-104-idi-48.c102
-rw-r--r--drivers/gpio/gpio-104-idio-16.c89
-rw-r--r--drivers/gpio/gpio-74x164.c31
-rw-r--r--drivers/gpio/gpio-74xx-mmio.c5
-rw-r--r--drivers/gpio/gpio-adnp.c94
-rw-r--r--drivers/gpio/gpio-adp5520.c18
-rw-r--r--drivers/gpio/gpio-adp5588.c58
-rw-r--r--drivers/gpio/gpio-aggregator.c536
-rw-r--r--drivers/gpio/gpio-altera-a10sr.c14
-rw-r--r--drivers/gpio/gpio-altera.c16
-rw-r--r--drivers/gpio/gpio-amd-fch.c2
-rw-r--r--drivers/gpio/gpio-amd8111.c11
-rw-r--r--drivers/gpio/gpio-amdpt.c26
-rw-r--r--drivers/gpio/gpio-arizona.c17
-rw-r--r--drivers/gpio/gpio-aspeed-sgpio.c612
-rw-r--r--drivers/gpio/gpio-aspeed.c192
-rw-r--r--drivers/gpio/gpio-ath79.c29
-rw-r--r--drivers/gpio/gpio-bcm-kona.c25
-rw-r--r--drivers/gpio/gpio-bd70528.c232
-rw-r--r--drivers/gpio/gpio-bd71815.c185
-rw-r--r--drivers/gpio/gpio-bd71828.c143
-rw-r--r--drivers/gpio/gpio-bd9571mwv.c50
-rw-r--r--drivers/gpio/gpio-brcmstb.c25
-rw-r--r--drivers/gpio/gpio-bt8xx.c8
-rw-r--r--drivers/gpio/gpio-cadence.c51
-rw-r--r--drivers/gpio/gpio-creg-snps.c10
-rw-r--r--drivers/gpio/gpio-crystalcove.c119
-rw-r--r--drivers/gpio/gpio-cs5535.c8
-rw-r--r--drivers/gpio/gpio-da9052.c11
-rw-r--r--drivers/gpio/gpio-da9055.c11
-rw-r--r--drivers/gpio/gpio-davinci.c19
-rw-r--r--drivers/gpio/gpio-dln2.c51
-rw-r--r--drivers/gpio/gpio-dwapb.c595
-rw-r--r--drivers/gpio/gpio-eic-sprd.c25
-rw-r--r--drivers/gpio/gpio-em.c67
-rw-r--r--drivers/gpio/gpio-en7523.c137
-rw-r--r--drivers/gpio/gpio-ep93xx.c336
-rw-r--r--drivers/gpio/gpio-exar.c153
-rw-r--r--drivers/gpio/gpio-f7188x.c38
-rw-r--r--drivers/gpio/gpio-ftgpio010.c27
-rw-r--r--drivers/gpio/gpio-ge.c1
-rw-r--r--drivers/gpio/gpio-gpio-mm.c113
-rw-r--r--drivers/gpio/gpio-grgpio.c42
-rw-r--r--drivers/gpio/gpio-gw-pld.c2
-rw-r--r--drivers/gpio/gpio-hisi.c323
-rw-r--r--drivers/gpio/gpio-hlwd.c83
-rw-r--r--drivers/gpio/gpio-htc-egpio.c56
-rw-r--r--drivers/gpio/gpio-ich.c35
-rw-r--r--drivers/gpio/gpio-idt3243x.c206
-rw-r--r--drivers/gpio/gpio-intel-mid.c412
-rw-r--r--drivers/gpio/gpio-it87.c22
-rw-r--r--drivers/gpio/gpio-ixp4xx.c274
-rw-r--r--drivers/gpio/gpio-kempld.c5
-rw-r--r--drivers/gpio/gpio-ks8695.c284
-rw-r--r--drivers/gpio/gpio-logicvc.c168
-rw-r--r--drivers/gpio/gpio-loongson.c2
-rw-r--r--drivers/gpio/gpio-loongson1.c8
-rw-r--r--drivers/gpio/gpio-lp873x.c2
-rw-r--r--drivers/gpio/gpio-lp87565.c11
-rw-r--r--drivers/gpio/gpio-lpc32xx.c120
-rw-r--r--drivers/gpio/gpio-lynxpoint.c458
-rw-r--r--drivers/gpio/gpio-madera.c17
-rw-r--r--drivers/gpio/gpio-max3191x.c27
-rw-r--r--drivers/gpio/gpio-max7300.c4
-rw-r--r--drivers/gpio/gpio-max7301.c4
-rw-r--r--drivers/gpio/gpio-max730x.c18
-rw-r--r--drivers/gpio/gpio-max732x.c67
-rw-r--r--drivers/gpio/gpio-max77620.c280
-rw-r--r--drivers/gpio/gpio-max77650.c1
-rw-r--r--drivers/gpio/gpio-mb86s7x.c29
-rw-r--r--drivers/gpio/gpio-mc33880.c6
-rw-r--r--drivers/gpio/gpio-mc9s08dz60.c112
-rw-r--r--drivers/gpio/gpio-menz127.c9
-rw-r--r--drivers/gpio/gpio-merrifield.c121
-rw-r--r--drivers/gpio/gpio-ml-ioh.c171
-rw-r--r--drivers/gpio/gpio-mlxbf.c2
-rw-r--r--drivers/gpio/gpio-mlxbf2.c462
-rw-r--r--drivers/gpio/gpio-mm-lantiq.c2
-rw-r--r--drivers/gpio/gpio-mmio.c87
-rw-r--r--drivers/gpio/gpio-mockup.c311
-rw-r--r--drivers/gpio/gpio-moxtet.c179
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c109
-rw-r--r--drivers/gpio/gpio-msc313.c718
-rw-r--r--drivers/gpio/gpio-msic.c314
-rw-r--r--drivers/gpio/gpio-mt7621.c52
-rw-r--r--drivers/gpio/gpio-mvebu.c258
-rw-r--r--drivers/gpio/gpio-mxc.c141
-rw-r--r--drivers/gpio/gpio-mxs.c30
-rw-r--r--drivers/gpio/gpio-omap.c107
-rw-r--r--drivers/gpio/gpio-palmas.c4
-rw-r--r--drivers/gpio/gpio-pca953x.c593
-rw-r--r--drivers/gpio/gpio-pca9570.c146
-rw-r--r--drivers/gpio/gpio-pcf857x.c76
-rw-r--r--drivers/gpio/gpio-pch.c133
-rw-r--r--drivers/gpio/gpio-pci-idio-16.c114
-rw-r--r--drivers/gpio/gpio-pcie-idio-24.c200
-rw-r--r--drivers/gpio/gpio-pisosr.c27
-rw-r--r--drivers/gpio/gpio-pl061.c63
-rw-r--r--drivers/gpio/gpio-pmic-eic-sprd.c7
-rw-r--r--drivers/gpio/gpio-pxa.c32
-rw-r--r--drivers/gpio/gpio-raspberrypi-exp.c8
-rw-r--r--drivers/gpio/gpio-rcar.c248
-rw-r--r--drivers/gpio/gpio-rda.c289
-rw-r--r--drivers/gpio/gpio-realtek-otto.c449
-rw-r--r--drivers/gpio/gpio-reg.c3
-rw-r--r--drivers/gpio/gpio-regmap.c342
-rw-r--r--drivers/gpio/gpio-rockchip.c823
-rw-r--r--drivers/gpio/gpio-sa1100.c5
-rw-r--r--drivers/gpio/gpio-sama5d2-piobu.c25
-rw-r--r--drivers/gpio/gpio-sch.c235
-rw-r--r--drivers/gpio/gpio-sch311x.c5
-rw-r--r--drivers/gpio/gpio-sifive.c275
-rw-r--r--drivers/gpio/gpio-sim.c1603
-rw-r--r--drivers/gpio/gpio-siox.c33
-rw-r--r--drivers/gpio/gpio-sl28cpld.c161
-rw-r--r--drivers/gpio/gpio-sodaville.c2
-rw-r--r--drivers/gpio/gpio-spear-spics.c12
-rw-r--r--drivers/gpio/gpio-sprd.c30
-rw-r--r--drivers/gpio/gpio-sta2x11.c12
-rw-r--r--drivers/gpio/gpio-stmpe.c92
-rw-r--r--drivers/gpio/gpio-stp-xway.c54
-rw-r--r--drivers/gpio/gpio-syscon.c61
-rw-r--r--drivers/gpio/gpio-tb10x.c11
-rw-r--r--drivers/gpio/gpio-tc3589x.c63
-rw-r--r--drivers/gpio/gpio-tegra.c302
-rw-r--r--drivers/gpio/gpio-tegra186.c870
-rw-r--r--drivers/gpio/gpio-thunderx.c182
-rw-r--r--drivers/gpio/gpio-tn48m.c100
-rw-r--r--drivers/gpio/gpio-tpic2810.c2
-rw-r--r--drivers/gpio/gpio-tps65086.c2
-rw-r--r--drivers/gpio/gpio-tps65218.c17
-rw-r--r--drivers/gpio/gpio-tps6586x.c18
-rw-r--r--drivers/gpio/gpio-tps65910.c30
-rw-r--r--drivers/gpio/gpio-tps65912.c16
-rw-r--r--drivers/gpio/gpio-tps68470.c23
-rw-r--r--drivers/gpio/gpio-tqmx86.c72
-rw-r--r--drivers/gpio/gpio-ts4900.c39
-rw-r--r--drivers/gpio/gpio-ts5500.c15
-rw-r--r--drivers/gpio/gpio-twl4030.c10
-rw-r--r--drivers/gpio/gpio-twl6040.c8
-rw-r--r--drivers/gpio/gpio-uniphier.c42
-rw-r--r--drivers/gpio/gpio-vf610.c37
-rw-r--r--drivers/gpio/gpio-viperboard.c34
-rw-r--r--drivers/gpio/gpio-virtio.c664
-rw-r--r--drivers/gpio/gpio-visconti.c207
-rw-r--r--drivers/gpio/gpio-vx855.c4
-rw-r--r--drivers/gpio/gpio-wcd934x.c124
-rw-r--r--drivers/gpio/gpio-wcove.c151
-rw-r--r--drivers/gpio/gpio-wm831x.c17
-rw-r--r--drivers/gpio/gpio-wm8350.c12
-rw-r--r--drivers/gpio/gpio-wm8994.c13
-rw-r--r--drivers/gpio/gpio-ws16c48.c168
-rw-r--r--drivers/gpio/gpio-xgene-sb.c15
-rw-r--r--drivers/gpio/gpio-xgene.c41
-rw-r--r--drivers/gpio/gpio-xgs-iproc.c319
-rw-r--r--drivers/gpio/gpio-xilinx.c610
-rw-r--r--drivers/gpio/gpio-xlp.c175
-rw-r--r--drivers/gpio/gpio-xra1403.c23
-rw-r--r--drivers/gpio/gpio-xtensa.c11
-rw-r--r--drivers/gpio/gpio-zevio.c25
-rw-r--r--drivers/gpio/gpio-zx.c292
-rw-r--r--drivers/gpio/gpio-zynq.c170
-rw-r--r--drivers/gpio/gpio-zynqmp-modepin.c162
-rw-r--r--drivers/gpio/gpiolib-acpi.c554
-rw-r--r--drivers/gpio/gpiolib-acpi.h102
-rw-r--r--drivers/gpio/gpiolib-cdev.c2568
-rw-r--r--drivers/gpio/gpiolib-cdev.h13
-rw-r--r--drivers/gpio/gpiolib-devprop.c60
-rw-r--r--drivers/gpio/gpiolib-devres.c107
-rw-r--r--drivers/gpio/gpiolib-of.c459
-rw-r--r--drivers/gpio/gpiolib-of.h45
-rw-r--r--drivers/gpio/gpiolib-sysfs.c142
-rw-r--r--drivers/gpio/gpiolib-sysfs.h24
-rw-r--r--drivers/gpio/gpiolib.c3350
-rw-r--r--drivers/gpio/gpiolib.h236
179 files changed, 20910 insertions, 9352 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index bb13c266c329..b01961999ced 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.
@@ -59,20 +59,50 @@ config DEBUG_GPIO
that are most common when setting up new platforms or boards.
config GPIO_SYSFS
- bool "/sys/class/gpio/... (sysfs interface)"
+ bool "/sys/class/gpio/... (sysfs interface)" if EXPERT
depends on SYSFS
+ select GPIO_CDEV # We need to encourage the new ABI
help
Say Y here to add the legacy sysfs interface for GPIOs.
This ABI is deprecated. If you want to use GPIO from userspace,
use the character device /dev/gpiochipN with the appropriate
- ioctl() operations instead. The character device is always
- available.
+ ioctl() operations instead.
+
+config GPIO_CDEV
+ bool
+ prompt "Character device (/dev/gpiochipN) support" if EXPERT
+ default y
+ help
+ Say Y here to add the character device /dev/gpiochipN interface
+ for GPIOs. The character device allows userspace to control GPIOs
+ using ioctl() operations.
+
+ Only say N if you are sure that the GPIO character device is not
+ required.
+
+ If unsure, say Y.
+
+config GPIO_CDEV_V1
+ bool "Support GPIO ABI Version 1"
+ default y
+ depends on GPIO_CDEV
+ help
+ Say Y here to support version 1 of the GPIO CDEV ABI.
+
+ This ABI version is deprecated.
+ Please use the latest ABI for new developments.
+
+ If unsure, say Y.
config GPIO_GENERIC
depends on HAS_IOMEM # Only for IOMEM drivers
tristate
+config GPIO_REGMAP
+ depends on REGMAP
+ tristate
+
# put drivers in the right section, in alphabetical order
# This symbol is selected by both I2C and SPI expanders
@@ -110,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"
@@ -120,6 +150,14 @@ config GPIO_ASPEED
help
Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers.
+config GPIO_ASPEED_SGPIO
+ bool "Aspeed SGPIO support"
+ depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ Say Y here to support Aspeed AST2500 SGPIO functionality.
+
config GPIO_ATH79
tristate "Atheros AR71XX/AR724X/AR913X GPIO support"
default y if ATH79
@@ -147,6 +185,15 @@ config GPIO_BCM_KONA
help
Turn on GPIO support for Broadcom "Kona" chips.
+config GPIO_BCM_XGS_IPROC
+ tristate "BRCM XGS iProc GPIO support"
+ depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST)
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ default ARCH_BCM_IPROC
+ help
+ Say yes here to enable GPIO support for Broadcom XGS iProc SoCs.
+
config GPIO_BRCMSTB
tristate "BRCMSTB GPIO support"
default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
@@ -181,7 +228,7 @@ config GPIO_DAVINCI
config GPIO_DWAPB
tristate "Synopsys DesignWare APB GPIO driver"
select GPIO_GENERIC
- select GENERIC_IRQ_CHIP
+ select GPIOLIB_IRQCHIP
help
Say Y or M here to build support for the Synopsys DesignWare APB
GPIO block.
@@ -200,6 +247,16 @@ config GPIO_EM
help
Say yes here to support GPIO on Renesas Emma Mobile SoCs.
+config GPIO_EN7523
+ tristate "Airoha GPIO support"
+ depends on ARCH_AIROHA
+ default ARCH_AIROHA
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ Say Y or M here to support the GPIO controller block on the
+ Airoha EN7523 SoC. It supports two banks of 32 GPIOs.
+
config GPIO_EP93XX
def_bool y
depends on ARCH_EP93XX
@@ -209,6 +266,7 @@ config GPIO_EP93XX
config GPIO_EXAR
tristate "Support for GPIO pins on XR17V352/354/358"
depends on SERIAL_8250_EXAR
+ select REGMAP_MMIO
help
Selecting this option will enable handling of GPIO pins present
on Exar XR17V352/354/358 chips.
@@ -250,6 +308,17 @@ config GPIO_GRGPIO
Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
VHDL IP core library.
+config GPIO_HISI
+ tristate "HiSilicon GPIO controller driver"
+ depends on (ARM64 && ACPI) || COMPILE_TEST
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ Say Y or M here to build support for the HiSilicon GPIO controller
+ driver GPIO block.
+ This GPIO controller supports double-edge interrupt and multi-core
+ concurrent access.
+
config GPIO_HLWD
tristate "Nintendo Wii (Hollywood) GPIO"
depends on OF_GPIO
@@ -262,33 +331,32 @@ config GPIO_HLWD
config GPIO_ICH
tristate "Intel ICH GPIO"
- depends on PCI && X86
- select MFD_CORE
- select LPC_ICH
+ depends on X86
+ depends on LPC_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.
config GPIO_IOP
tristate "Intel IOP GPIO"
- depends on ARCH_IOP32X || ARCH_IOP33X || COMPILE_TEST
+ depends on ARCH_IOP32X || COMPILE_TEST
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.
config GPIO_IXP4XX
bool "Intel IXP4xx GPIO"
- depends on ARM # For <asm/mach-types.h>
depends on ARCH_IXP4XX
+ depends on OF
select GPIO_GENERIC
- select IRQ_DOMAIN
+ select GPIOLIB_IRQCHIP
select IRQ_DOMAIN_HIERARCHY
help
Say yes here to support the GPIO functionality of a number of Intel
@@ -296,11 +364,18 @@ config GPIO_IXP4XX
If unsure, say N.
+config GPIO_LOGICVC
+ tristate "Xylon LogiCVC GPIO support"
+ depends on MFD_SYSCON && OF
+ help
+ Say yes here to support GPIO functionality of the Xylon LogiCVC
+ programmable logic block.
+
config GPIO_LOONGSON
bool "Loongson-2/3 GPIO support"
- depends on CPU_LOONGSON2 || CPU_LOONGSON3
+ 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"
@@ -311,13 +386,12 @@ config GPIO_LPC18XX
Select this option to enable GPIO driver for
NXP LPC18XX/43XX devices.
-config GPIO_LYNXPOINT
- tristate "Intel Lynxpoint GPIO support"
- depends on ACPI && X86
- select GPIOLIB_IRQCHIP
+config GPIO_LPC32XX
+ tristate "NXP LPC32XX GPIO support"
+ depends on OF_GPIO && (ARCH_LPC32XX || COMPILE_TEST)
help
- driver for GPIO functionality on Intel Lynxpoint PCH chipset
- Requires ACPI device enumeration code to set up a platform device.
+ Select this option to enable GPIO driver for
+ NXP LPC32XX devices.
config GPIO_MB86S7X
tristate "GPIO support for Fujitsu MB86S7x Platforms"
@@ -329,15 +403,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
@@ -361,7 +435,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
@@ -371,20 +445,21 @@ config GPIO_MVEBU
select REGMAP_MMIO
config GPIO_MXC
- def_bool y
- depends on ARCH_MXC
+ tristate "i.MX GPIO support"
+ depends on ARCH_MXC || COMPILE_TEST
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
config GPIO_MXS
- def_bool y
- depends on ARCH_MXS
+ bool "Freescale MXS GPIO support" if COMPILE_TEST
+ depends on ARCH_MXS || COMPILE_TEST
+ default y if ARCH_MXS
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
config GPIO_OCTEON
tristate "Cavium OCTEON GPIO"
- depends on GPIOLIB && CAVIUM_OCTEON_SOC
+ depends on CAVIUM_OCTEON_SOC
default y
help
Say yes here to support the on-chip GPIO lines on the OCTEON
@@ -400,12 +475,12 @@ config GPIO_OMAP
Say yes here to enable GPIO support for TI OMAP SoCs.
config GPIO_PL061
- bool "PrimeCell PL061 GPIO support"
+ tristate "PrimeCell PL061 GPIO support"
depends on ARM_AMBA
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"
@@ -417,16 +492,38 @@ config GPIO_PMIC_EIC_SPRD
config GPIO_PXA
bool "PXA GPIO support"
- depends on ARCH_PXA || ARCH_MMP
+ 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 GPIO"
+ tristate "Renesas R-Car and RZ/G GPIO support"
depends on ARCH_RENESAS || COMPILE_TEST
select GPIOLIB_IRQCHIP
help
- Say yes here to support GPIO on Renesas R-Car SoCs.
+ Say yes here to support GPIO on Renesas R-Car or RZ/G SoCs.
+
+config GPIO_RDA
+ bool "RDA Micro GPIO controller support"
+ depends on ARCH_RDA || COMPILE_TEST
+ depends on OF_GPIO
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ Say Y here to support RDA Micro GPIO controller.
+
+config GPIO_REALTEK_OTTO
+ tristate "Realtek Otto GPIO support"
+ depends on MACH_REALTEK_RTL
+ default MACH_REALTEK_RTL
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ The GPIO controller on the Otto MIPS platform supports up to two
+ banks of 32 GPIOs, with edge triggered interrupts. The 32 GPIOs
+ are grouped in four 8-bit wide ports.
+
+ When built as a module, the module will be called realtek_otto_gpio.
config GPIO_REG
bool
@@ -434,6 +531,15 @@ config GPIO_REG
A 32-bit single register GPIO fixed in/out implementation. This
can be used to represent any register as a set of GPIO signals.
+config GPIO_ROCKCHIP
+ tristate "Rockchip GPIO support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select GENERIC_IRQ_CHIP
+ select GPIOLIB_IRQCHIP
+ default ARCH_ROCKCHIP
+ help
+ Say yes here to support GPIO on Rockchip SoCs.
+
config GPIO_SAMA5D2_PIOBU
tristate "SAMA5D2 PIOBU GPIO support"
depends on MFD_SYSCON
@@ -446,6 +552,16 @@ config GPIO_SAMA5D2_PIOBU
The difference from regular GPIOs is that they
maintain their value during backup/self-refresh.
+config GPIO_SIFIVE
+ bool "SiFive GPIO support"
+ depends on OF_GPIO
+ select IRQ_DOMAIN_HIERARCHY
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ select REGMAP_MMIO
+ help
+ Say yes here to support the GPIO device on SiFive SoCs.
+
config GPIO_SIOX
tristate "SIOX GPIO support"
depends on SIOX
@@ -469,7 +585,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"
@@ -494,8 +610,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
@@ -511,19 +627,22 @@ config GPIO_TB10X
select OF_GPIO
config GPIO_TEGRA
- bool "NVIDIA Tegra GPIO support"
+ tristate "NVIDIA Tegra GPIO support"
default ARCH_TEGRA
depends on ARCH_TEGRA || COMPILE_TEST
depends on OF_GPIO
+ select GPIOLIB_IRQCHIP
+ select IRQ_DOMAIN_HIERARCHY
help
Say yes here to support GPIO pins on NVIDIA Tegra SoCs.
config GPIO_TEGRA186
tristate "NVIDIA Tegra186 GPIO support"
- default ARCH_TEGRA_186_SOC
- depends on ARCH_TEGRA_186_SOC || COMPILE_TEST
+ default ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC
+ depends on ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || COMPILE_TEST
depends on OF_GPIO
select GPIOLIB_IRQCHIP
+ select IRQ_DOMAIN_HIERARCHY
help
Say yes here to support GPIO pins on NVIDIA Tegra186 SoCs.
@@ -539,6 +658,7 @@ config GPIO_THUNDERX
tristate "Cavium ThunderX/OCTEON-TX GPIO"
depends on ARCH_THUNDER || (64BIT && COMPILE_TEST)
depends on PCI_MSI
+ select GPIOLIB_IRQCHIP
select IRQ_DOMAIN_HIERARCHY
select IRQ_FASTEOI_HIERARCHY_HANDLERS
help
@@ -555,16 +675,26 @@ config GPIO_UNIPHIER
config GPIO_VF610
def_bool y
- depends on ARCH_MXC && SOC_VF610
+ depends on ARCH_MXC
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to support i.MX or Vybrid vf610 GPIOs.
+
+config GPIO_VISCONTI
+ tristate "Toshiba Visconti GPIO support"
+ depends on ARCH_VISCONTI || COMPILE_TEST
+ depends on OF_GPIO
select GPIOLIB_IRQCHIP
+ select GPIO_GENERIC
+ select IRQ_DOMAIN_HIERARCHY
help
- Say yes here to support Vybrid vf610 GPIOs.
+ 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"
@@ -572,12 +702,19 @@ 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"
+ depends on MFD_WCD934X && OF_GPIO
+ help
+ This driver is to support GPIO block found on the Qualcomm Technologies
+ Inc WCD9340/WCD9341 Audio Codec.
+
config GPIO_XGENE
bool "APM X-Gene GPIO controller support"
depends on ARM64 && OF_GPIO
@@ -589,7 +726,7 @@ config GPIO_XGENE
config GPIO_XGENE_SB
tristate "APM X-Gene GPIO standby controller support"
- depends on ARCH_XGENE && OF_GPIO
+ depends on (ARCH_XGENE || COMPILE_TEST)
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
select IRQ_DOMAIN_HIERARCHY
@@ -599,18 +736,18 @@ config GPIO_XGENE_SB
config GPIO_XILINX
tristate "Xilinx GPIO support"
+ 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"
- depends on OF_GPIO && (CPU_XLP || ARCH_THUNDER2 || COMPILE_TEST)
+ tristate "Cavium ThunderX2 GPIO support"
+ depends on ARCH_THUNDER2 || COMPILE_TEST
select GPIOLIB_IRQCHIP
help
- This driver provides support for GPIO interface on Netlogic XLP MIPS64
- SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX,
- XLP9XX and XLP5XX. The same GPIO controller block is also present in
- Cavium's ThunderX2 CN99XX SoCs.
+ This driver provides support for GPIO interface on Cavium's ThunderX2
+ CN99XX SoCs (Originally from Netlogic XLP).
If unsure, say N.
@@ -621,7 +758,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"
@@ -636,12 +773,17 @@ config GPIO_ZYNQ
help
Say yes here to support Xilinx Zynq GPIO controller.
-config GPIO_ZX
- bool "ZTE ZX GPIO support"
- depends on ARCH_ZX || COMPILE_TEST
- select GPIOLIB_IRQCHIP
+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 GPIO device on ZTE ZX SoCs.
+ 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"
@@ -653,12 +795,35 @@ 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 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"
+ depends on ARCH_MSTARV7
+ default ARCH_MSTARV7
+ select GPIOLIB_IRQCHIP
+ select IRQ_DOMAIN_HIERARCHY
+ help
+ Say Y here to support the main GPIO block on MStar/SigmaStar
+ ARMv7-based SoCs.
+
+config GPIO_IDT3243X
+ tristate "IDT 79RC3243X GPIO support"
+ depends on MIKROTIK_RB532 || COMPILE_TEST
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ Select this option to enable GPIO driver for
+ IDT 79RC3243X-based devices like Mikrotik RB532.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-idt3243x.
- Note: This driver doesn't registers itself automatically, as it
- needs to be provided with platform specific configuration.
- (See eg. CONFIG_PCENGINES_APU2.)
endmenu
menu "Port-mapped I/O GPIO drivers"
@@ -732,13 +897,13 @@ 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"
- depends on (X86 || COMPILE_TEST) && PCI
- select MFD_CORE
- select LPC_SCH
+ depends on (X86 || COMPILE_TEST) && ACPI
+ depends on LPC_SCH
+ select GPIOLIB_IRQCHIP
help
Say yes here to support GPIO interface on Intel Poulsbo SCH,
Intel Tunnel Creek processor, Intel Centerton processor or
@@ -748,7 +913,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
@@ -875,12 +1040,6 @@ config GPIO_MAX732X_IRQ
Say yes here to enable the max732x to be used as an interrupt
controller. It requires the driver to be built in the kernel.
-config GPIO_MC9S08DZ60
- bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
- depends on I2C=y && MACH_MX35_3DS
- help
- Select this to enable the MC9S08DZ60 GPIO driver
-
config GPIO_PCA953X
tristate "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
select REGMAP_I2C
@@ -903,11 +1062,19 @@ config GPIO_PCA953X
config GPIO_PCA953X_IRQ
bool "Interrupt controller support for PCA953x"
- depends on GPIO_PCA953X=y
+ depends on GPIO_PCA953X
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"
+ help
+ Say yes here to enable the GPO driver for the NXP PCA9570 chip.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-pca9570.
config GPIO_PCF857X
tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
@@ -975,16 +1142,27 @@ config GPIO_ARIZONA
help
Support for GPIOs on Wolfson Arizona class devices.
-config GPIO_BD70528
- tristate "ROHM BD70528 GPIO support"
- depends on MFD_ROHM_BD70528
+config GPIO_BD71815
+ tristate "ROHM BD71815 PMIC GPIO support"
+ depends on MFD_ROHM_BD71828
help
- Support for GPIOs on ROHM BD70528 PMIC. There are four GPIOs
- available on the ROHM PMIC in total. The GPIOs can also
- generate interrupts.
+ Support for GPO(s) on ROHM BD71815 PMIC. There are two GPOs
+ available on the ROHM PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-bd71815.
+
+config GPIO_BD71828
+ tristate "ROHM BD71828 GPIO support"
+ depends on MFD_ROHM_BD71828
+ help
+ Support for GPIOs on ROHM BD71828 PMIC. There are three GPIOs
+ available on the ROHM PMIC in total. The GPIOs are limited to
+ outputs only and pins must be configured to GPIO outputs by
+ OTP. Enable this only if you want to use these pins as outputs.
This driver can also be built as a module. If so, the module
- will be called gpio-bd70528.
+ will be called gpio-bd71828.
config GPIO_BD9571MWV
tristate "ROHM BD9571 GPIO support"
@@ -1004,7 +1182,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
@@ -1034,7 +1212,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.
@@ -1052,11 +1230,11 @@ config GPIO_DLN2
config HTC_EGPIO
bool "HTC EGPIO support"
- depends on GPIOLIB && ARM
+ depends on ARM
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"
@@ -1113,11 +1291,12 @@ config GPIO_MADERA
config GPIO_MAX77620
tristate "GPIO support for PMIC MAX77620 and MAX20024"
depends on MFD_MAX77620
+ select GPIOLIB_IRQCHIP
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"
@@ -1126,13 +1305,6 @@ config GPIO_MAX77650
GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor.
These chips have a single pin that can be configured as GPIO.
-config GPIO_MSIC
- bool "Intel MSIC mixed signal gpio support"
- depends on (X86 || COMPILE_TEST) && MFD_INTEL_MSIC
- help
- Enable support for GPIO on intel MSIC controllers found in
- intel MID devices
-
config GPIO_PALMAS
bool "TI PALMAS series PMICs GPIO"
depends on MFD_PALMAS
@@ -1146,8 +1318,20 @@ 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"
+ depends on MFD_SL28CPLD || COMPILE_TEST
+ select GPIO_REGMAP
+ select GPIOLIB_IRQCHIP
+ select REGMAP_IRQ
+ help
+ This enables support for the GPIOs found on the Kontron sl28 CPLD.
+
+ This driver can also be built as a module. If so, the module will be
+ called gpio-sl28cpld.
config GPIO_STMPE
bool "STMPE GPIOs"
@@ -1170,9 +1354,21 @@ config GPIO_TC3589X
config GPIO_TIMBERDALE
bool "Support for timberdale GPIO IP"
depends on MFD_TIMBERDALE
- ---help---
+ help
Add support for the GPIO IP in the timberdale FPGA.
+config GPIO_TN48M_CPLD
+ tristate "Delta Networks TN48M switch CPLD GPIO driver"
+ depends on MFD_TN48M_CPLD
+ select GPIO_REGMAP
+ help
+ This enables support for the GPIOs found on the Delta
+ Networks TN48M switch Lattice CPLD. It provides 12 pins in total,
+ they are input-only or output-only type.
+
+ This driver can also be built as a module. If so, the
+ module will be called gpio-tn48m.
+
config GPIO_TPS65086
tristate "TI TPS65086 GPO"
depends on MFD_TPS65086
@@ -1204,26 +1400,23 @@ 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"
- depends on MFD_TPS68470
+ tristate "TPS68470 GPIO"
+ depends on INTEL_SKL_INT3472
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.
- This driver config is bool, as the GPIO functionality
- of the TPS68470 must be available before dependent
- drivers are loaded.
-
config GPIO_TQMX86
tristate "TQ-Systems QTMX86 GPIO"
depends on MFD_TQMX86 || COMPILE_TEST
+ depends on HAS_IOPORT_MAP
select GPIOLIB_IRQCHIP
help
This driver supports GPIO on the TQMX86 IO controller.
@@ -1256,7 +1449,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
@@ -1291,11 +1484,12 @@ menu "PCI GPIO expanders"
config GPIO_AMD8111
tristate "AMD 8111 GPIO driver"
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
@@ -1312,17 +1506,10 @@ config GPIO_BT8XX
The card needs to be physically altered for using it as a
GPIO card. For more information on how to build a GPIO card
from a BT8xx TV card, see the documentation file at
- Documentation/driver-api/bt8xxgpio.rst
+ Documentation/driver-api/gpio/bt8xxgpio.rst
If unsure, say N.
-config GPIO_INTEL_MID
- bool "Intel MID GPIO support"
- depends on X86_INTEL_MID
- select GPIOLIB_IRQCHIP
- help
- Say Y here to support Intel MID GPIO.
-
config GPIO_MERRIFIELD
tristate "Intel Merrifield GPIO support"
depends on X86_INTEL_MID
@@ -1337,28 +1524,35 @@ config GPIO_MLXBF
help
Say Y here if you want GPIO support on Mellanox BlueField SoC.
+config GPIO_MLXBF2
+ tristate "Mellanox BlueField 2 SoC GPIO"
+ depends on (MELLANOX_PLATFORM && ARM64 && ACPI) || (64BIT && COMPILE_TEST)
+ select GPIO_GENERIC
+ help
+ Say Y here if you want GPIO support on Mellanox BlueField 2 SoC.
+
config GPIO_ML_IOH
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
depends on X86 || COMPILE_TEST
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.
@@ -1409,7 +1603,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"
@@ -1445,6 +1639,15 @@ config GPIO_XRA1403
help
GPIO driver for EXAR XRA1403 16-bit SPI-based GPIO expander.
+config GPIO_MOXTET
+ tristate "Turris Mox Moxtet bus GPIO expander"
+ depends on MOXTET
+ help
+ Say yes here if you are building for the Turris Mox router.
+ This is the driver needed for configuring the GPIOs via the Moxtet
+ bus. For example the Mox module with SFP cage needs this driver
+ so that phylink can use corresponding GPIOs.
+
endmenu
menu "USB GPIO expanders"
@@ -1463,16 +1666,48 @@ config GPIO_VIPERBOARD
endmenu
+menu "Virtual GPIO drivers"
+
+config GPIO_AGGREGATOR
+ tristate "GPIO Aggregator"
+ help
+ Say yes here to enable the GPIO Aggregator, which provides a way to
+ aggregate existing GPIO lines into a new virtual GPIO chip.
+ This can serve the following purposes:
+ - Assign permissions for a collection of GPIO lines to a user,
+ - Export a collection of GPIO lines to a virtual machine,
+ - Provide a generic driver for a GPIO-operated device in an
+ industrial control context, to be operated from userspace using
+ the GPIO chardev interface.
+
config GPIO_MOCKUP
tristate "GPIO Testing Driver"
- depends on GPIOLIB
select IRQ_SIM
help
This enables GPIO Testing driver, which provides a way to test GPIO
- subsystem through sysfs(or char device) and debugfs. GPIO_SYSFS
- must be selected for this test.
+ subsystem through sysfs (or char device) and debugfs.
User could use it through the script in
tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in
it.
+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.
+
+ These virtual GPIOs can be routed to real GPIOs or attached to
+ simulators on the host (like QEMU).
+
+config GPIO_SIM
+ tristate "GPIO Simulator Module"
+ select IRQ_SIM
+ select CONFIGFS_FS
+ help
+ This enables the GPIO simulator - a configfs-based GPIO testing
+ driver.
+
+endmenu
+
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a4e91175c708..14352f6dfe8e 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -6,12 +6,13 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
obj-$(CONFIG_GPIOLIB) += gpiolib.o
obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o
obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o
-obj-$(CONFIG_GPIOLIB) += gpiolib-devprop.o
obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
+obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o
obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
# Device drivers. Generally keep list sorted alphabetically
+obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
# directly supported by gpio-generic
@@ -25,6 +26,7 @@ obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
+obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
@@ -32,9 +34,12 @@ obj-$(CONFIG_GPIO_AMD_FCH) += gpio-amd-fch.o
obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
+obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.o
obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
-obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o
+obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o
+obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o
+obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
@@ -50,6 +55,7 @@ obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o
obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o
+obj-$(CONFIG_GPIO_EN7523) += gpio-en7523.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o
obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
@@ -58,24 +64,24 @@ obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o
+obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o
obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
-obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o
+obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
-obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o
+obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o
obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o
obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o
obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o
obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o
obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o
-obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
-obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
+obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o
obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o
obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o
obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
@@ -86,16 +92,17 @@ obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o
obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o
obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o
obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o
-obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o
obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o
+obj-$(CONFIG_GPIO_MLXBF2) += gpio-mlxbf2.o
obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o
obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o
+obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o
obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o
-obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o
+obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o
obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
@@ -104,6 +111,7 @@ obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
+obj-$(CONFIG_GPIO_PCA9570) += gpio-pca9570.o
obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o
obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o
@@ -115,13 +123,19 @@ obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
+obj-$(CONFIG_GPIO_RDA) += gpio-rda.o
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
+obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
+obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
+obj-$(CONFIG_GPIO_SIM) += gpio-sim.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
+obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
@@ -135,6 +149,7 @@ obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
+obj-$(CONFIG_GPIO_TN48M_CPLD) += gpio-tn48m.o
obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o
obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o
obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o
@@ -152,8 +167,11 @@ obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
+obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o
+obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
+obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o
obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o
obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o
obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
@@ -167,5 +185,5 @@ obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o
obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
-obj-$(CONFIG_GPIO_ZX) += gpio-zx.o
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
+obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN) += gpio-zynqmp-modepin.o
diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO
index 9c048f10c9ad..f87ff3fa8a53 100644
--- a/drivers/gpio/TODO
+++ b/drivers/gpio/TODO
@@ -5,11 +5,33 @@ subsystem.
GPIO descriptors
Starting with commit 79a9becda894 the GPIO subsystem embarked on a journey
-to move away from the global GPIO numberspace and toward a decriptor-based
+to move away from the global GPIO numberspace and toward a descriptor-based
approach. This means that GPIO consumers, drivers and machine descriptions
ideally have no use or idea of the global GPIO numberspace that has/was
used in the inception of the GPIO subsystem.
+The numberspace issue is the same as to why irq is moving away from irq
+numbers to IRQ descriptors.
+
+The underlying motivation for this is that the GPIO numberspace has become
+unmanageable: machine board files tend to become full of macros trying to
+establish the numberspace at compile-time, making it hard to add any numbers
+in the middle (such as if you missed a pin on a chip) without the numberspace
+breaking.
+
+Machine descriptions such as device tree or ACPI does not have a concept of the
+Linux GPIO number as those descriptions are external to the Linux kernel
+and treat GPIO lines as abstract entities.
+
+The runtime-assigned GPIO numberspace (what you get if you assign the GPIO
+base as -1 in struct gpio_chip) has also became unpredictable due to factors
+such as probe ordering and the introduction of -EPROBE_DEFER making probe
+ordering of independent GPIO chips essentially unpredictable, as their base
+number will be assigned on a first come first serve basis.
+
+The best way to get out of the problem is to make the global GPIO numbers
+unimportant by simply not using them. GPIO descriptors deal with this.
+
Work items:
- Convert all GPIO device drivers to only #include <linux/gpio/driver.h>
@@ -33,7 +55,7 @@ This header and helpers appeared at one point when there was no proper
driver infrastructure for doing simpler MMIO GPIO devices and there was
no core support for parsing device tree GPIOs from the core library with
the [devm_]gpiod_get() calls we have today that will implicitly go into
-the device tree back-end.
+the device tree back-end. It is legacy and should not be used in new code.
Work items:
@@ -59,6 +81,15 @@ Work items:
uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead.
+Get rid of <linux/gpio.h>
+
+This legacy header is a one stop shop for anything GPIO is closely tied
+to the global GPIO numberspace. The endgame of the above refactorings will
+be the removal of <linux/gpio.h> and from that point only the specialized
+headers under <linux/gpio/*.h> will be used. This requires all the above to
+be completed and is expected to take a long time.
+
+
Collect drivers
Collect GPIO drivers from arch/* and other places that should be placed
@@ -68,6 +99,10 @@ similar and probe a proper driver in the gpiolib subsystem.
In some cases it makes sense to create a GPIO chip from the local driver
for a few GPIOs. Those should stay where they are.
+At the same time it makes sense to get rid of code duplication in existing or
+new coming drivers. For example, gpio-ml-ioh should be incorporated into
+gpio-pch.
+
Generic MMIO GPIO
@@ -80,6 +115,10 @@ Work items:
- Look over and identify any remaining easily converted drivers and
dry-code conversions to MMIO GPIO for maintainers to test
+- Expand the MMIO GPIO or write a new library for regmap-based I/O
+ helpers for GPIO drivers on regmap that simply use offsets
+ 0..n in some register to drive GPIO lines
+
- Expand the MMIO GPIO or write a new library for port-mapped I/O
helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use
this with dry-coding and sending to maintainers to test
@@ -90,53 +129,9 @@ GPIOLIB irqchip
The GPIOLIB irqchip is a helper irqchip for "simple cases" that should
try to cover any generic kind of irqchip cascaded from a GPIO.
-- Convert all the GPIOLIB_IRQCHIP users to pass an irqchip template,
- parent and flags before calling [devm_]gpiochip_add[_data]().
- Currently we set up the irqchip after setting up the gpiochip
- using gpiochip_irqchip_add() and gpiochip_set_[chained|nested]_irqchip().
- This is too complex, so convert all users over to just set up
- the irqchip before registering the gpio_chip, typical example:
-
- /* Typical state container with dynamic irqchip */
- struct my_gpio {
- struct gpio_chip gc;
- struct irq_chip irq;
- };
-
- int irq; /* from platform etc */
- struct my_gpio *g;
- struct gpio_irq_chip *girq
-
- /* Set up the irqchip dynamically */
- g->irq.name = "my_gpio_irq";
- g->irq.irq_ack = my_gpio_ack_irq;
- g->irq.irq_mask = my_gpio_mask_irq;
- g->irq.irq_unmask = my_gpio_unmask_irq;
- g->irq.irq_set_type = my_gpio_set_irq_type;
-
- /* Get a pointer to the gpio_irq_chip */
- girq = &g->gc.irq;
- girq->chip = &g->irq;
- girq->parent_handler = ftgpio_gpio_irq_handler;
- girq->num_parents = 1;
- girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
- GFP_KERNEL);
- if (!girq->parents)
- return -ENOMEM;
- girq->default_type = IRQ_TYPE_NONE;
- girq->handler = handle_bad_irq;
- girq->parents[0] = irq;
-
- When this is done, we will delete the old APIs for instatiating
- GPIOLIB_IRQCHIP and simplify the code.
-
- Look over and identify any remaining easily converted drivers and
dry-code conversions to gpiolib irqchip for maintainers to test
-- Support generic hierarchical GPIO interrupts: these are for the
- non-cascading case where there is one IRQ per GPIO line, there is
- currently no common infrastructure for this.
-
Increase integration with pin control
@@ -147,3 +142,58 @@ use of the global GPIO numbers. Once the above is complete, it may
make sense to simply join the subsystems into one and make pin
multiplexing, pin configuration, GPIO, etc selectable options in one
and the same pin control and GPIO subsystem.
+
+
+Debugfs in place of sysfs
+
+The old sysfs code that enables simple uses of GPIOs from the
+command line is still popular despite the existance of the proper
+character device. The reason is that it is simple to use on
+root filesystems where you only have a minimal set of tools such
+as "cat", "echo" etc.
+
+The old sysfs still need to be strongly deprecated and removed
+as it relies on the global GPIO numberspace that assume a strict
+order of global GPIO numbers that do not change between boots
+and is independent of probe order.
+
+To solve this and provide an ABI that people can use for hacks
+and development, implement a debugfs interface to manipulate
+GPIO lines that can do everything that sysfs can do today: one
+directory per gpiochip and one file entry per line:
+
+/sys/kernel/debug/gpiochip/gpiochip0
+/sys/kernel/debug/gpiochip/gpiochip0/gpio0
+/sys/kernel/debug/gpiochip/gpiochip0/gpio1
+/sys/kernel/debug/gpiochip/gpiochip0/gpio2
+/sys/kernel/debug/gpiochip/gpiochip0/gpio3
+...
+/sys/kernel/debug/gpiochip/gpiochip1
+/sys/kernel/debug/gpiochip/gpiochip1/gpio0
+/sys/kernel/debug/gpiochip/gpiochip1/gpio1
+...
+
+The exact files and design of the debugfs interface can be
+discussed but the idea is to provide a low-level access point
+for debugging and hacking and to expose all lines without the
+need of any exporting. Also provide ample ammunition to shoot
+oneself in the foot, because this is debugfs after all.
+
+
+Moving over to immutable irq_chip structures
+
+Most of the gpio chips implementing interrupt support rely on gpiolib
+intercepting some of the irq_chip callbacks, preventing the structures
+from being made read-only and forcing duplication of structures that
+should otherwise be unique.
+
+The solution is to call into the gpiolib code when needed (resource
+management, enable/disable or unmask/mask callbacks), and to let the
+core code know about that by exposing a flag (IRQCHIP_IMMUTABLE) in
+the irq_chip structure. The irq_chip structure can then be made unique
+and const.
+
+A small number of drivers have been converted (pl061, tegra186, msm,
+amd, apple), and can be used as examples of how to proceed with this
+conversion. Note that drivers using the generic irqchip framework
+cannot be converted yet, but watch this space!
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index a44fa8af5b0d..f118ad9bcd33 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -49,27 +49,30 @@ struct dio48e_gpio {
unsigned char out_state[6];
unsigned char control[2];
raw_spinlock_t lock;
- unsigned base;
+ void __iomem *base;
unsigned char irq_mask;
};
-static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- const unsigned port = offset / 8;
- const unsigned mask = BIT(offset % 8);
+ const unsigned int port = offset / 8;
+ const unsigned int mask = BIT(offset % 8);
- return !!(dio48egpio->io_state[port] & mask);
+ if (dio48egpio->io_state[port] & mask)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
-static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- const unsigned io_port = offset / 8;
+ const unsigned int io_port = offset / 8;
const unsigned int control_port = io_port / 3;
- const unsigned control_addr = dio48egpio->base + 3 + control_port*4;
+ void __iomem *const control_addr = dio48egpio->base + 3 + control_port * 4;
unsigned long flags;
- unsigned control;
+ unsigned int control;
raw_spin_lock_irqsave(&dio48egpio->lock, flags);
@@ -92,26 +95,26 @@ static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
}
control = BIT(7) | dio48egpio->control[control_port];
- outb(control, control_addr);
+ iowrite8(control, control_addr);
control &= ~BIT(7);
- outb(control, control_addr);
+ iowrite8(control, control_addr);
raw_spin_unlock_irqrestore(&dio48egpio->lock, flags);
return 0;
}
-static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
- int value)
+static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- const unsigned io_port = offset / 8;
+ const unsigned int io_port = offset / 8;
const unsigned int control_port = io_port / 3;
- const unsigned mask = BIT(offset % 8);
- const unsigned control_addr = dio48egpio->base + 3 + control_port*4;
- const unsigned out_port = (io_port > 2) ? io_port + 1 : io_port;
+ const unsigned int mask = BIT(offset % 8);
+ void __iomem *const control_addr = dio48egpio->base + 3 + control_port * 4;
+ const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port;
unsigned long flags;
- unsigned control;
+ unsigned int control;
raw_spin_lock_irqsave(&dio48egpio->lock, flags);
@@ -139,26 +142,26 @@ static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
dio48egpio->out_state[io_port] &= ~mask;
control = BIT(7) | dio48egpio->control[control_port];
- outb(control, control_addr);
+ iowrite8(control, control_addr);
- outb(dio48egpio->out_state[io_port], dio48egpio->base + out_port);
+ iowrite8(dio48egpio->out_state[io_port], dio48egpio->base + out_port);
control &= ~BIT(7);
- outb(control, control_addr);
+ iowrite8(control, control_addr);
raw_spin_unlock_irqrestore(&dio48egpio->lock, flags);
return 0;
}
-static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset)
+static int dio48e_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- const unsigned port = offset / 8;
- const unsigned mask = BIT(offset % 8);
- const unsigned in_port = (port > 2) ? port + 1 : port;
+ const unsigned int port = offset / 8;
+ const unsigned int mask = BIT(offset % 8);
+ const unsigned int in_port = (port > 2) ? port + 1 : port;
unsigned long flags;
- unsigned port_state;
+ unsigned int port_state;
raw_spin_lock_irqsave(&dio48egpio->lock, flags);
@@ -168,64 +171,43 @@ static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset)
return -EINVAL;
}
- port_state = inb(dio48egpio->base + in_port);
+ port_state = ioread8(dio48egpio->base + in_port);
raw_spin_unlock_irqrestore(&dio48egpio->lock, flags);
return !!(port_state & mask);
}
+static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+
static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- size_t i;
- static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
- const unsigned int gpio_reg_size = 8;
- unsigned int bits_offset;
- size_t word_index;
- unsigned int word_offset;
- unsigned long word_mask;
- const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long offset;
+ unsigned long gpio_mask;
+ void __iomem *port_addr;
unsigned long port_state;
/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);
- /* get bits are evaluated a gpio port register at a time */
- for (i = 0; i < ARRAY_SIZE(ports); i++) {
- /* gpio offset in bits array */
- bits_offset = i * gpio_reg_size;
-
- /* word index for bits array */
- word_index = BIT_WORD(bits_offset);
-
- /* gpio offset within current word of bits array */
- word_offset = bits_offset % BITS_PER_LONG;
-
- /* mask of get bits for current gpio within current word */
- word_mask = mask[word_index] & (port_mask << word_offset);
- if (!word_mask) {
- /* no get bits in this port so skip to next one */
- continue;
- }
-
- /* read bits from current gpio port */
- port_state = inb(dio48egpio->base + ports[i]);
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ port_addr = dio48egpio->base + ports[offset / 8];
+ port_state = ioread8(port_addr) & gpio_mask;
- /* store acquired bits at respective bits array offset */
- bits[word_index] |= (port_state << word_offset) & word_mask;
+ bitmap_set_value8(bits, port_state, offset);
}
return 0;
}
-static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static void dio48e_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- const unsigned port = offset / 8;
- const unsigned mask = BIT(offset % 8);
- const unsigned out_port = (port > 2) ? port + 1 : port;
+ const unsigned int port = offset / 8;
+ const unsigned int mask = BIT(offset % 8);
+ const unsigned int out_port = (port > 2) ? port + 1 : port;
unsigned long flags;
raw_spin_lock_irqsave(&dio48egpio->lock, flags);
@@ -235,7 +217,7 @@ static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
else
dio48egpio->out_state[port] &= ~mask;
- outb(dio48egpio->out_state[port], dio48egpio->base + out_port);
+ iowrite8(dio48egpio->out_state[port], dio48egpio->base + out_port);
raw_spin_unlock_irqrestore(&dio48egpio->lock, flags);
}
@@ -244,37 +226,27 @@ static void dio48e_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- unsigned int i;
- const unsigned int gpio_reg_size = 8;
- unsigned int port;
- unsigned int out_port;
- unsigned int bitmask;
+ unsigned long offset;
+ unsigned long gpio_mask;
+ size_t index;
+ void __iomem *port_addr;
+ unsigned long bitmask;
unsigned long flags;
- /* set bits are evaluated a gpio register size at a time */
- for (i = 0; i < chip->ngpio; i += gpio_reg_size) {
- /* no more set bits in this mask word; skip to the next word */
- if (!mask[BIT_WORD(i)]) {
- i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size;
- continue;
- }
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ index = offset / 8;
+ port_addr = dio48egpio->base + ports[index];
- port = i / gpio_reg_size;
- out_port = (port > 2) ? port + 1 : port;
- bitmask = mask[BIT_WORD(i)] & bits[BIT_WORD(i)];
+ bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
raw_spin_lock_irqsave(&dio48egpio->lock, flags);
/* update output state data and set device gpio register */
- dio48egpio->out_state[port] &= ~mask[BIT_WORD(i)];
- dio48egpio->out_state[port] |= bitmask;
- outb(dio48egpio->out_state[port], dio48egpio->base + out_port);
+ dio48egpio->out_state[index] &= ~gpio_mask;
+ dio48egpio->out_state[index] |= bitmask;
+ iowrite8(dio48egpio->out_state[index], port_addr);
raw_spin_unlock_irqrestore(&dio48egpio->lock, flags);
-
- /* prepare for next gpio register set */
- mask[BIT_WORD(i)] >>= gpio_reg_size;
- bits[BIT_WORD(i)] >>= gpio_reg_size;
}
}
@@ -302,7 +274,7 @@ static void dio48e_irq_mask(struct irq_data *data)
if (!dio48egpio->irq_mask)
/* disable interrupts */
- inb(dio48egpio->base + 0xB);
+ ioread8(dio48egpio->base + 0xB);
raw_spin_unlock_irqrestore(&dio48egpio->lock, flags);
}
@@ -322,8 +294,8 @@ static void dio48e_irq_unmask(struct irq_data *data)
if (!dio48egpio->irq_mask) {
/* enable interrupts */
- outb(0x00, dio48egpio->base + 0xF);
- outb(0x00, dio48egpio->base + 0xB);
+ iowrite8(0x00, dio48egpio->base + 0xF);
+ iowrite8(0x00, dio48egpio->base + 0xB);
}
if (offset == 19)
@@ -334,7 +306,7 @@ static void dio48e_irq_unmask(struct irq_data *data)
raw_spin_unlock_irqrestore(&dio48egpio->lock, flags);
}
-static int dio48e_irq_set_type(struct irq_data *data, unsigned flow_type)
+static int dio48e_irq_set_type(struct irq_data *data, unsigned int flow_type)
{
const unsigned long offset = irqd_to_hwirq(data);
@@ -364,12 +336,12 @@ static irqreturn_t dio48e_irq_handler(int irq, void *dev_id)
unsigned long gpio;
for_each_set_bit(gpio, &irq_mask, 2)
- generic_handle_irq(irq_find_mapping(chip->irq.domain,
- 19 + gpio*24));
+ generic_handle_domain_irq(chip->irq.domain,
+ 19 + gpio*24);
raw_spin_lock(&dio48egpio->lock);
- outb(0x00, dio48egpio->base + 0xF);
+ iowrite8(0x00, dio48egpio->base + 0xF);
raw_spin_unlock(&dio48egpio->lock);
@@ -396,10 +368,21 @@ static const char *dio48e_names[DIO48E_NGPIO] = {
"PPI Group 1 Port C 5", "PPI Group 1 Port C 6", "PPI Group 1 Port C 7"
};
+static int dio48e_irq_init_hw(struct gpio_chip *gc)
+{
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(gc);
+
+ /* Disable IRQ by default */
+ ioread8(dio48egpio->base + 0xB);
+
+ return 0;
+}
+
static int dio48e_probe(struct device *dev, unsigned int id)
{
struct dio48e_gpio *dio48egpio;
const char *const name = dev_name(dev);
+ struct gpio_irq_chip *girq;
int err;
dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
@@ -412,6 +395,10 @@ static int dio48e_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
+ dio48egpio->base = devm_ioport_map(dev, base[id], DIO48E_EXTENT);
+ if (!dio48egpio->base)
+ return -ENOMEM;
+
dio48egpio->chip.label = name;
dio48egpio->chip.parent = dev;
dio48egpio->chip.owner = THIS_MODULE;
@@ -425,38 +412,37 @@ static int dio48e_probe(struct device *dev, unsigned int id)
dio48egpio->chip.get_multiple = dio48e_gpio_get_multiple;
dio48egpio->chip.set = dio48e_gpio_set;
dio48egpio->chip.set_multiple = dio48e_gpio_set_multiple;
- dio48egpio->base = base[id];
+
+ girq = &dio48egpio->chip.irq;
+ girq->chip = &dio48e_irqchip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_edge_irq;
+ girq->init_hw = dio48e_irq_init_hw;
raw_spin_lock_init(&dio48egpio->lock);
+ /* initialize all GPIO as output */
+ iowrite8(0x80, dio48egpio->base + 3);
+ iowrite8(0x00, dio48egpio->base);
+ iowrite8(0x00, dio48egpio->base + 1);
+ iowrite8(0x00, dio48egpio->base + 2);
+ iowrite8(0x00, dio48egpio->base + 3);
+ iowrite8(0x80, dio48egpio->base + 7);
+ iowrite8(0x00, dio48egpio->base + 4);
+ iowrite8(0x00, dio48egpio->base + 5);
+ iowrite8(0x00, dio48egpio->base + 6);
+ iowrite8(0x00, dio48egpio->base + 7);
+
err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
return err;
}
- /* initialize all GPIO as output */
- outb(0x80, base[id] + 3);
- outb(0x00, base[id]);
- outb(0x00, base[id] + 1);
- outb(0x00, base[id] + 2);
- outb(0x00, base[id] + 3);
- outb(0x80, base[id] + 7);
- outb(0x00, base[id] + 4);
- outb(0x00, base[id] + 5);
- outb(0x00, base[id] + 6);
- outb(0x00, base[id] + 7);
-
- /* disable IRQ by default */
- inb(base[id] + 0xB);
-
- err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0,
- handle_edge_irq, IRQ_TYPE_NONE);
- if (err) {
- dev_err(dev, "Could not add irqchip (%d)\n", err);
- return err;
- }
-
err = devm_request_irq(dev, irq[id], dio48e_irq_handler, 0, name,
dio48egpio);
if (err) {
diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index ff53887bdaa8..9521ece3ebef 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -47,13 +47,13 @@ struct idi_48_gpio {
raw_spinlock_t lock;
spinlock_t ack_lock;
unsigned char irq_mask[6];
- unsigned base;
+ void __iomem *base;
unsigned char cos_enb;
};
static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
}
static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -65,16 +65,16 @@ static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
unsigned i;
- const unsigned register_offset[6] = { 0, 1, 2, 4, 5, 6 };
- unsigned base_offset;
+ static const unsigned int register_offset[6] = { 0, 1, 2, 4, 5, 6 };
+ void __iomem *port_addr;
unsigned mask;
for (i = 0; i < 48; i += 8)
if (offset < i + 8) {
- base_offset = register_offset[i / 8];
+ port_addr = idi48gpio->base + register_offset[i / 8];
mask = BIT(offset - i);
- return !!(inb(idi48gpio->base + base_offset) & mask);
+ return !!(ioread8(port_addr) & mask);
}
/* The following line should never execute since offset < 48 */
@@ -85,42 +85,20 @@ static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
- size_t i;
+ unsigned long offset;
+ unsigned long gpio_mask;
static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
- const unsigned int gpio_reg_size = 8;
- unsigned int bits_offset;
- size_t word_index;
- unsigned int word_offset;
- unsigned long word_mask;
- const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ void __iomem *port_addr;
unsigned long port_state;
/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);
- /* get bits are evaluated a gpio port register at a time */
- for (i = 0; i < ARRAY_SIZE(ports); i++) {
- /* gpio offset in bits array */
- bits_offset = i * gpio_reg_size;
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ port_addr = idi48gpio->base + ports[offset / 8];
+ port_state = ioread8(port_addr) & gpio_mask;
- /* word index for bits array */
- word_index = BIT_WORD(bits_offset);
-
- /* gpio offset within current word of bits array */
- word_offset = bits_offset % BITS_PER_LONG;
-
- /* mask of get bits for current gpio within current word */
- word_mask = mask[word_index] & (port_mask << word_offset);
- if (!word_mask) {
- /* no get bits in this port so skip to next one */
- continue;
- }
-
- /* read bits from current gpio port */
- port_state = inb(idi48gpio->base + ports[i]);
-
- /* store acquired bits at respective bits array offset */
- bits[word_index] |= (port_state << word_offset) & word_mask;
+ bitmap_set_value8(bits, port_state, offset);
}
return 0;
@@ -152,10 +130,9 @@ static void idi_48_irq_mask(struct irq_data *data)
raw_spin_lock_irqsave(&idi48gpio->lock, flags);
- outb(idi48gpio->cos_enb, idi48gpio->base + 7);
+ iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7);
- raw_spin_unlock_irqrestore(&idi48gpio->lock,
- flags);
+ raw_spin_unlock_irqrestore(&idi48gpio->lock, flags);
}
return;
@@ -186,10 +163,9 @@ static void idi_48_irq_unmask(struct irq_data *data)
raw_spin_lock_irqsave(&idi48gpio->lock, flags);
- outb(idi48gpio->cos_enb, idi48gpio->base + 7);
+ iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7);
- raw_spin_unlock_irqrestore(&idi48gpio->lock,
- flags);
+ raw_spin_unlock_irqrestore(&idi48gpio->lock, flags);
}
return;
@@ -228,7 +204,7 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id)
raw_spin_lock(&idi48gpio->lock);
- cos_status = inb(idi48gpio->base + 7);
+ cos_status = ioread8(idi48gpio->base + 7);
raw_spin_unlock(&idi48gpio->lock);
@@ -247,8 +223,8 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id)
for_each_set_bit(bit_num, &irq_mask, 8) {
gpio = bit_num + boundary * 8;
- generic_handle_irq(irq_find_mapping(chip->irq.domain,
- gpio));
+ generic_handle_domain_irq(chip->irq.domain,
+ gpio);
}
}
@@ -269,10 +245,22 @@ static const char *idi48_names[IDI48_NGPIO] = {
"Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B"
};
+static int idi_48_irq_init_hw(struct gpio_chip *gc)
+{
+ struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc);
+
+ /* Disable IRQ by default */
+ iowrite8(0, idi48gpio->base + 7);
+ ioread8(idi48gpio->base + 7);
+
+ return 0;
+}
+
static int idi_48_probe(struct device *dev, unsigned int id)
{
struct idi_48_gpio *idi48gpio;
const char *const name = dev_name(dev);
+ struct gpio_irq_chip *girq;
int err;
idi48gpio = devm_kzalloc(dev, sizeof(*idi48gpio), GFP_KERNEL);
@@ -285,6 +273,10 @@ static int idi_48_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
+ idi48gpio->base = devm_ioport_map(dev, base[id], IDI_48_EXTENT);
+ if (!idi48gpio->base)
+ return -ENOMEM;
+
idi48gpio->chip.label = name;
idi48gpio->chip.parent = dev;
idi48gpio->chip.owner = THIS_MODULE;
@@ -295,7 +287,16 @@ static int idi_48_probe(struct device *dev, unsigned int id)
idi48gpio->chip.direction_input = idi_48_gpio_direction_input;
idi48gpio->chip.get = idi_48_gpio_get;
idi48gpio->chip.get_multiple = idi_48_gpio_get_multiple;
- idi48gpio->base = base[id];
+
+ girq = &idi48gpio->chip.irq;
+ girq->chip = &idi_48_irqchip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_edge_irq;
+ girq->init_hw = idi_48_irq_init_hw;
raw_spin_lock_init(&idi48gpio->lock);
spin_lock_init(&idi48gpio->ack_lock);
@@ -306,17 +307,6 @@ static int idi_48_probe(struct device *dev, unsigned int id)
return err;
}
- /* Disable IRQ by default */
- outb(0, base[id] + 7);
- inb(base[id] + 7);
-
- err = gpiochip_irqchip_add(&idi48gpio->chip, &idi_48_irqchip, 0,
- handle_edge_irq, IRQ_TYPE_NONE);
- if (err) {
- dev_err(dev, "Could not add irqchip (%d)\n", err);
- return err;
- }
-
err = devm_request_irq(dev, irq[id], idi_48_irq_handler, IRQF_SHARED,
name, idi48gpio);
if (err) {
diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c
index 8d2f51cd9d91..45f7ad8573e1 100644
--- a/drivers/gpio/gpio-104-idio-16.c
+++ b/drivers/gpio/gpio-104-idio-16.c
@@ -44,42 +44,44 @@ struct idio_16_gpio {
struct gpio_chip chip;
raw_spinlock_t lock;
unsigned long irq_mask;
- unsigned base;
- unsigned out_state;
+ void __iomem *base;
+ unsigned int out_state;
};
-static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+static int idio_16_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
{
if (offset > 15)
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
}
-static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+static int idio_16_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
{
return 0;
}
static int idio_16_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
+ unsigned int offset, int value)
{
chip->set(chip, offset, value);
return 0;
}
-static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
+static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- const unsigned mask = BIT(offset-16);
+ const unsigned int mask = BIT(offset-16);
if (offset < 16)
return -EINVAL;
if (offset < 24)
- return !!(inb(idio16gpio->base + 1) & mask);
+ return !!(ioread8(idio16gpio->base + 1) & mask);
- return !!(inb(idio16gpio->base + 5) & (mask>>8));
+ return !!(ioread8(idio16gpio->base + 5) & (mask>>8));
}
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
@@ -89,17 +91,18 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
*bits = 0;
if (*mask & GENMASK(23, 16))
- *bits |= (unsigned long)inb(idio16gpio->base + 1) << 16;
+ *bits |= (unsigned long)ioread8(idio16gpio->base + 1) << 16;
if (*mask & GENMASK(31, 24))
- *bits |= (unsigned long)inb(idio16gpio->base + 5) << 24;
+ *bits |= (unsigned long)ioread8(idio16gpio->base + 5) << 24;
return 0;
}
-static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- const unsigned mask = BIT(offset);
+ const unsigned int mask = BIT(offset);
unsigned long flags;
if (offset > 15)
@@ -113,9 +116,9 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
idio16gpio->out_state &= ~mask;
if (offset > 7)
- outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
+ iowrite8(idio16gpio->out_state >> 8, idio16gpio->base + 4);
else
- outb(idio16gpio->out_state, idio16gpio->base);
+ iowrite8(idio16gpio->out_state, idio16gpio->base);
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
}
@@ -132,9 +135,9 @@ static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
idio16gpio->out_state |= *mask & *bits;
if (*mask & 0xFF)
- outb(idio16gpio->out_state, idio16gpio->base);
+ iowrite8(idio16gpio->out_state, idio16gpio->base);
if ((*mask >> 8) & 0xFF)
- outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
+ iowrite8(idio16gpio->out_state >> 8, idio16gpio->base + 4);
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
}
@@ -155,7 +158,7 @@ static void idio_16_irq_mask(struct irq_data *data)
if (!idio16gpio->irq_mask) {
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
- outb(0, idio16gpio->base + 2);
+ iowrite8(0, idio16gpio->base + 2);
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
}
@@ -174,13 +177,13 @@ static void idio_16_irq_unmask(struct irq_data *data)
if (!prev_irq_mask) {
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
- inb(idio16gpio->base + 2);
+ ioread8(idio16gpio->base + 2);
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
}
}
-static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type)
+static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type)
{
/* The only valid irq types are none and both-edges */
if (flow_type != IRQ_TYPE_NONE &&
@@ -205,11 +208,11 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
int gpio;
for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
- generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio));
+ generic_handle_domain_irq(chip->irq.domain, gpio);
raw_spin_lock(&idio16gpio->lock);
- outb(0, idio16gpio->base + 1);
+ iowrite8(0, idio16gpio->base + 1);
raw_spin_unlock(&idio16gpio->lock);
@@ -224,10 +227,22 @@ static const char *idio_16_names[IDIO_16_NGPIO] = {
"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
};
+static int idio_16_irq_init_hw(struct gpio_chip *gc)
+{
+ struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc);
+
+ /* Disable IRQ by default */
+ iowrite8(0, idio16gpio->base + 2);
+ iowrite8(0, idio16gpio->base + 1);
+
+ return 0;
+}
+
static int idio_16_probe(struct device *dev, unsigned int id)
{
struct idio_16_gpio *idio16gpio;
const char *const name = dev_name(dev);
+ struct gpio_irq_chip *girq;
int err;
idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
@@ -240,6 +255,10 @@ static int idio_16_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
+ idio16gpio->base = devm_ioport_map(dev, base[id], IDIO_16_EXTENT);
+ if (!idio16gpio->base)
+ return -ENOMEM;
+
idio16gpio->chip.label = name;
idio16gpio->chip.parent = dev;
idio16gpio->chip.owner = THIS_MODULE;
@@ -253,9 +272,18 @@ static int idio_16_probe(struct device *dev, unsigned int id)
idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
- idio16gpio->base = base[id];
idio16gpio->out_state = 0xFFFF;
+ girq = &idio16gpio->chip.irq;
+ girq->chip = &idio_16_irqchip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_edge_irq;
+ girq->init_hw = idio_16_irq_init_hw;
+
raw_spin_lock_init(&idio16gpio->lock);
err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
@@ -264,17 +292,6 @@ static int idio_16_probe(struct device *dev, unsigned int id)
return err;
}
- /* Disable IRQ by default */
- outb(0, base[id] + 2);
- outb(0, base[id] + 1);
-
- err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
- handle_edge_irq, IRQ_TYPE_NONE);
- if (err) {
- dev_err(dev, "Could not add irqchip (%d)\n", err);
- return err;
- }
-
err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name,
idio16gpio);
if (err) {
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index e81307f9754e..e00c33310517 100644
--- a/drivers/gpio/gpio-74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -6,6 +6,7 @@
* Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com>
*/
+#include <linux/bitops.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
@@ -72,20 +73,18 @@ static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct gen_74x164_chip *chip = gpiochip_get_data(gc);
- unsigned int i, idx, shift;
- u8 bank, bankmask;
+ unsigned long offset;
+ unsigned long bankmask;
+ size_t bank;
+ unsigned long bitmask;
mutex_lock(&chip->lock);
- for (i = 0, bank = chip->registers - 1; i < chip->registers;
- i++, bank--) {
- idx = i / sizeof(*mask);
- shift = i % sizeof(*mask) * BITS_PER_BYTE;
- bankmask = mask[idx] >> shift;
- if (!bankmask)
- continue;
+ for_each_set_clump8(offset, bankmask, mask, chip->registers * 8) {
+ bank = chip->registers - 1 - offset / 8;
+ bitmask = bitmap_get_value8(bits, offset) & bankmask;
chip->buffer[bank] &= ~bankmask;
- chip->buffer[bank] |= bankmask & (bits[idx] >> shift);
+ chip->buffer[bank] |= bitmask;
}
__gen_74x164_write_config(chip);
mutex_unlock(&chip->lock);
@@ -164,17 +163,22 @@ exit_destroy:
return ret;
}
-static int gen_74x164_remove(struct spi_device *spi)
+static void gen_74x164_remove(struct spi_device *spi)
{
struct gen_74x164_chip *chip = spi_get_drvdata(spi);
gpiod_set_value_cansleep(chip->gpiod_oe, 0);
gpiochip_remove(&chip->gpio_chip);
mutex_destroy(&chip->lock);
-
- return 0;
}
+static const struct spi_device_id gen_74x164_spi_ids[] = {
+ { .name = "74hc595" },
+ { .name = "74lvc594" },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, gen_74x164_spi_ids);
+
static const struct of_device_id gen_74x164_dt_ids[] = {
{ .compatible = "fairchild,74hc595" },
{ .compatible = "nxp,74lvc594" },
@@ -189,6 +193,7 @@ static struct spi_driver gen_74x164_driver = {
},
.probe = gen_74x164_probe,
.remove = gen_74x164_remove,
+ .id_table = gen_74x164_spi_ids,
};
module_spi_driver(gen_74x164_driver);
diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c
index 83a2286d93f6..173e06758e6c 100644
--- a/drivers/gpio/gpio-74xx-mmio.c
+++ b/drivers/gpio/gpio-74xx-mmio.c
@@ -77,7 +77,10 @@ static int mmio_74xx_get_direction(struct gpio_chip *gc, unsigned offset)
{
struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc);
- return !(priv->flags & MMIO_74XX_DIR_OUT);
+ if (priv->flags & MMIO_74XX_DIR_OUT)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio)
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
index b9fcaab2a931..cc349d4e4973 100644
--- a/drivers/gpio/gpio-adnp.c
+++ b/drivers/gpio/gpio-adnp.c
@@ -238,36 +238,6 @@ unlock:
mutex_unlock(&adnp->i2c_lock);
}
-static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
-{
- struct gpio_chip *chip = &adnp->gpio;
- int err;
-
- adnp->reg_shift = get_count_order(num_gpios) - 3;
-
- chip->direction_input = adnp_gpio_direction_input;
- chip->direction_output = adnp_gpio_direction_output;
- chip->get = adnp_gpio_get;
- chip->set = adnp_gpio_set;
- chip->can_sleep = true;
-
- if (IS_ENABLED(CONFIG_DEBUG_FS))
- chip->dbg_show = adnp_gpio_dbg_show;
-
- chip->base = -1;
- chip->ngpio = num_gpios;
- chip->label = adnp->client->name;
- chip->parent = &adnp->client->dev;
- chip->of_node = chip->parent->of_node;
- chip->owner = THIS_MODULE;
-
- err = devm_gpiochip_add_data(&adnp->client->dev, chip, adnp);
- if (err)
- return err;
-
- return 0;
-}
-
static irqreturn_t adnp_irq(int irq, void *data)
{
struct adnp *adnp = data;
@@ -464,18 +434,53 @@ static int adnp_irq_setup(struct adnp *adnp)
return err;
}
- err = gpiochip_irqchip_add_nested(chip,
- &adnp_irq_chip,
- 0,
- handle_simple_irq,
- IRQ_TYPE_NONE);
- if (err) {
- dev_err(chip->parent,
- "could not connect irqchip to gpiochip\n");
- return err;
+ return 0;
+}
+
+static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios,
+ bool is_irq_controller)
+{
+ struct gpio_chip *chip = &adnp->gpio;
+ int err;
+
+ adnp->reg_shift = get_count_order(num_gpios) - 3;
+
+ chip->direction_input = adnp_gpio_direction_input;
+ chip->direction_output = adnp_gpio_direction_output;
+ chip->get = adnp_gpio_get;
+ chip->set = adnp_gpio_set;
+ chip->can_sleep = true;
+
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ chip->dbg_show = adnp_gpio_dbg_show;
+
+ chip->base = -1;
+ chip->ngpio = num_gpios;
+ chip->label = adnp->client->name;
+ chip->parent = &adnp->client->dev;
+ chip->owner = THIS_MODULE;
+
+ if (is_irq_controller) {
+ struct gpio_irq_chip *girq;
+
+ err = adnp_irq_setup(adnp);
+ if (err)
+ return err;
+
+ girq = &chip->irq;
+ girq->chip = &adnp_irq_chip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->threaded = true;
}
- gpiochip_set_nested_irqchip(chip, &adnp_irq_chip, adnp->client->irq);
+ err = devm_gpiochip_add_data(&adnp->client->dev, chip, adnp);
+ if (err)
+ return err;
return 0;
}
@@ -503,16 +508,11 @@ static int adnp_i2c_probe(struct i2c_client *client,
mutex_init(&adnp->i2c_lock);
adnp->client = client;
- err = adnp_gpio_setup(adnp, num_gpios);
+ err = adnp_gpio_setup(adnp, num_gpios,
+ of_property_read_bool(np, "interrupt-controller"));
if (err)
return err;
- if (of_find_property(np, "interrupt-controller", NULL)) {
- err = adnp_irq_setup(adnp);
- if (err)
- return err;
- }
-
i2c_set_clientdata(client, adnp);
return 0;
diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c
index 0386ede53f3a..c55e821c63b6 100644
--- a/drivers/gpio/gpio-adp5520.c
+++ b/drivers/gpio/gpio-adp5520.c
@@ -113,10 +113,8 @@ static int adp5520_gpio_probe(struct platform_device *pdev)
if (pdata->gpio_en_mask & (1 << i))
dev->lut[gpios++] = 1 << i;
- if (gpios < 1) {
- ret = -EINVAL;
- goto err;
- }
+ if (gpios < 1)
+ return -EINVAL;
gc = &dev->gpio_chip;
gc->direction_input = adp5520_gpio_direction_input;
@@ -148,18 +146,10 @@ static int adp5520_gpio_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "failed to write\n");
- goto err;
+ return ret;
}
- ret = devm_gpiochip_add_data(&pdev->dev, &dev->gpio_chip, dev);
- if (ret)
- goto err;
-
- platform_set_drvdata(pdev, dev);
- return 0;
-
-err:
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &dev->gpio_chip, dev);
}
static struct platform_driver adp5520_gpio_driver = {
diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c
index 49f423d7beba..e388e75103f4 100644
--- a/drivers/gpio/gpio-adp5588.c
+++ b/drivers/gpio/gpio-adp5588.c
@@ -272,13 +272,24 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid)
return IRQ_HANDLED;
}
+
+static int adp5588_irq_init_hw(struct gpio_chip *gc)
+{
+ struct adp5588_gpio *dev = gpiochip_get_data(gc);
+ /* Enable IRQs after registering chip */
+ adp5588_gpio_write(dev->client, CFG,
+ ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN);
+
+ return 0;
+}
+
static int adp5588_irq_setup(struct adp5588_gpio *dev)
{
struct i2c_client *client = dev->client;
int ret;
struct adp5588_gpio_platform_data *pdata =
dev_get_platdata(&client->dev);
- int irq_base = pdata ? pdata->irq_base : 0;
+ struct gpio_irq_chip *girq;
adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC);
adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */
@@ -294,21 +305,19 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev)
client->irq);
return ret;
}
- ret = gpiochip_irqchip_add_nested(&dev->gpio_chip,
- &adp5588_irq_chip, irq_base,
- handle_simple_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(&client->dev,
- "could not connect irqchip to gpiochip\n");
- return ret;
- }
- gpiochip_set_nested_irqchip(&dev->gpio_chip,
- &adp5588_irq_chip,
- client->irq);
- adp5588_gpio_write(client, CFG,
- ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN);
+ /* This will be registered in the call to devm_gpiochip_add_data() */
+ girq = &dev->gpio_chip.irq;
+ girq->chip = &adp5588_irq_chip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->first = pdata ? pdata->irq_base : 0;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->init_hw = adp5588_irq_init_hw;
+ girq->threaded = true;
return 0;
}
@@ -397,12 +406,6 @@ static int adp5588_gpio_probe(struct i2c_client *client)
if (ret)
return ret;
- if (pdata && pdata->setup) {
- ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context);
- if (ret < 0)
- dev_warn(&client->dev, "setup failed, %d\n", ret);
- }
-
i2c_set_clientdata(client, dev);
return 0;
@@ -410,20 +413,7 @@ static int adp5588_gpio_probe(struct i2c_client *client)
static int adp5588_gpio_remove(struct i2c_client *client)
{
- struct adp5588_gpio_platform_data *pdata =
- dev_get_platdata(&client->dev);
struct adp5588_gpio *dev = i2c_get_clientdata(client);
- int ret;
-
- if (pdata && pdata->teardown) {
- ret = pdata->teardown(client,
- dev->gpio_chip.base, dev->gpio_chip.ngpio,
- pdata->context);
- if (ret < 0) {
- dev_err(&client->dev, "teardown failed %d\n", ret);
- return ret;
- }
- }
if (dev->client->irq)
free_irq(dev->client->irq, dev);
diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
new file mode 100644
index 000000000000..0cb2664085cf
--- /dev/null
+++ b/drivers/gpio/gpio-aggregator.c
@@ -0,0 +1,536 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// GPIO Aggregator
+//
+// Copyright (C) 2019-2020 Glider bv
+
+#define DRV_NAME "gpio-aggregator"
+#define pr_fmt(fmt) DRV_NAME ": " fmt
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/ctype.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+
+/*
+ * GPIO Aggregator sysfs interface
+ */
+
+struct gpio_aggregator {
+ struct gpiod_lookup_table *lookups;
+ struct platform_device *pdev;
+ char args[];
+};
+
+static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */
+static DEFINE_IDR(gpio_aggregator_idr);
+
+static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
+ int hwnum, unsigned int *n)
+{
+ struct gpiod_lookup_table *lookups;
+
+ lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2),
+ GFP_KERNEL);
+ if (!lookups)
+ return -ENOMEM;
+
+ lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
+
+ (*n)++;
+ memset(&lookups->table[*n], 0, sizeof(lookups->table[*n]));
+
+ aggr->lookups = lookups;
+ return 0;
+}
+
+static int aggr_parse(struct gpio_aggregator *aggr)
+{
+ char *args = skip_spaces(aggr->args);
+ char *name, *offsets, *p;
+ unsigned long *bitmap;
+ unsigned int i, n = 0;
+ int error = 0;
+
+ bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL);
+ if (!bitmap)
+ return -ENOMEM;
+
+ args = next_arg(args, &name, &p);
+ while (*args) {
+ args = next_arg(args, &offsets, &p);
+
+ p = get_options(offsets, 0, &error);
+ if (error == 0 || *p) {
+ /* Named GPIO line */
+ error = aggr_add_gpio(aggr, name, U16_MAX, &n);
+ if (error)
+ goto free_bitmap;
+
+ name = offsets;
+ continue;
+ }
+
+ /* GPIO chip + offset(s) */
+ error = bitmap_parselist(offsets, bitmap, ARCH_NR_GPIOS);
+ if (error) {
+ pr_err("Cannot parse %s: %d\n", offsets, error);
+ goto free_bitmap;
+ }
+
+ for_each_set_bit(i, bitmap, ARCH_NR_GPIOS) {
+ error = aggr_add_gpio(aggr, name, i, &n);
+ if (error)
+ goto free_bitmap;
+ }
+
+ args = next_arg(args, &name, &p);
+ }
+
+ if (!n) {
+ pr_err("No GPIOs specified\n");
+ error = -EINVAL;
+ }
+
+free_bitmap:
+ bitmap_free(bitmap);
+ return error;
+}
+
+static ssize_t new_device_store(struct device_driver *driver, const char *buf,
+ size_t count)
+{
+ struct gpio_aggregator *aggr;
+ struct platform_device *pdev;
+ int res, id;
+
+ /* kernfs guarantees string termination, so count + 1 is safe */
+ aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL);
+ if (!aggr)
+ return -ENOMEM;
+
+ memcpy(aggr->args, buf, count + 1);
+
+ aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1),
+ GFP_KERNEL);
+ if (!aggr->lookups) {
+ res = -ENOMEM;
+ goto free_ga;
+ }
+
+ mutex_lock(&gpio_aggregator_lock);
+ id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL);
+ mutex_unlock(&gpio_aggregator_lock);
+
+ if (id < 0) {
+ res = id;
+ goto free_table;
+ }
+
+ aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id);
+ if (!aggr->lookups->dev_id) {
+ res = -ENOMEM;
+ goto remove_idr;
+ }
+
+ res = aggr_parse(aggr);
+ if (res)
+ goto free_dev_id;
+
+ gpiod_add_lookup_table(aggr->lookups);
+
+ pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0);
+ if (IS_ERR(pdev)) {
+ res = PTR_ERR(pdev);
+ goto remove_table;
+ }
+
+ aggr->pdev = pdev;
+ return count;
+
+remove_table:
+ gpiod_remove_lookup_table(aggr->lookups);
+free_dev_id:
+ kfree(aggr->lookups->dev_id);
+remove_idr:
+ mutex_lock(&gpio_aggregator_lock);
+ idr_remove(&gpio_aggregator_idr, id);
+ mutex_unlock(&gpio_aggregator_lock);
+free_table:
+ kfree(aggr->lookups);
+free_ga:
+ kfree(aggr);
+ return res;
+}
+
+static DRIVER_ATTR_WO(new_device);
+
+static void gpio_aggregator_free(struct gpio_aggregator *aggr)
+{
+ platform_device_unregister(aggr->pdev);
+ gpiod_remove_lookup_table(aggr->lookups);
+ kfree(aggr->lookups->dev_id);
+ kfree(aggr->lookups);
+ kfree(aggr);
+}
+
+static ssize_t delete_device_store(struct device_driver *driver,
+ const char *buf, size_t count)
+{
+ struct gpio_aggregator *aggr;
+ unsigned int id;
+ int error;
+
+ if (!str_has_prefix(buf, DRV_NAME "."))
+ return -EINVAL;
+
+ error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id);
+ if (error)
+ return error;
+
+ mutex_lock(&gpio_aggregator_lock);
+ aggr = idr_remove(&gpio_aggregator_idr, id);
+ mutex_unlock(&gpio_aggregator_lock);
+ if (!aggr)
+ return -ENOENT;
+
+ gpio_aggregator_free(aggr);
+ return count;
+}
+static DRIVER_ATTR_WO(delete_device);
+
+static struct attribute *gpio_aggregator_attrs[] = {
+ &driver_attr_new_device.attr,
+ &driver_attr_delete_device.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(gpio_aggregator);
+
+static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data)
+{
+ gpio_aggregator_free(p);
+ return 0;
+}
+
+static void __exit gpio_aggregator_remove_all(void)
+{
+ mutex_lock(&gpio_aggregator_lock);
+ idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL);
+ idr_destroy(&gpio_aggregator_idr);
+ mutex_unlock(&gpio_aggregator_lock);
+}
+
+
+/*
+ * GPIO Forwarder
+ */
+
+struct gpiochip_fwd {
+ struct gpio_chip chip;
+ struct gpio_desc **descs;
+ union {
+ struct mutex mlock; /* protects tmp[] if can_sleep */
+ spinlock_t slock; /* protects tmp[] if !can_sleep */
+ };
+ 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);
+
+ return gpiod_get_direction(fwd->descs[offset]);
+}
+
+static int gpio_fwd_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return gpiod_direction_input(fwd->descs[offset]);
+}
+
+static int gpio_fwd_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return gpiod_direction_output(fwd->descs[offset], value);
+}
+
+static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return chip->can_sleep ? gpiod_get_value_cansleep(fwd->descs[offset])
+ : gpiod_get_value(fwd->descs[offset]);
+}
+
+static int gpio_fwd_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpio_desc **descs = fwd_tmp_descs(fwd);
+ unsigned long *values = fwd_tmp_values(fwd);
+ unsigned int i, j = 0;
+ int error;
+
+ bitmap_clear(values, 0, fwd->chip.ngpio);
+ for_each_set_bit(i, mask, fwd->chip.ngpio)
+ descs[j++] = fwd->descs[i];
+
+ if (fwd->chip.can_sleep)
+ error = gpiod_get_array_value_cansleep(j, descs, NULL, values);
+ else
+ error = gpiod_get_array_value(j, descs, NULL, values);
+ if (error)
+ return error;
+
+ j = 0;
+ for_each_set_bit(i, mask, fwd->chip.ngpio)
+ __assign_bit(i, bits, test_bit(j++, values));
+
+ return 0;
+}
+
+static int gpio_fwd_get_multiple_locked(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+ unsigned long flags;
+ int error;
+
+ if (chip->can_sleep) {
+ mutex_lock(&fwd->mlock);
+ error = gpio_fwd_get_multiple(fwd, mask, bits);
+ mutex_unlock(&fwd->mlock);
+ } else {
+ spin_lock_irqsave(&fwd->slock, flags);
+ error = gpio_fwd_get_multiple(fwd, mask, bits);
+ spin_unlock_irqrestore(&fwd->slock, flags);
+ }
+
+ return error;
+}
+
+static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ if (chip->can_sleep)
+ gpiod_set_value_cansleep(fwd->descs[offset], value);
+ else
+ gpiod_set_value(fwd->descs[offset], value);
+}
+
+static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpio_desc **descs = fwd_tmp_descs(fwd);
+ unsigned long *values = fwd_tmp_values(fwd);
+ unsigned int i, j = 0;
+
+ for_each_set_bit(i, mask, fwd->chip.ngpio) {
+ __assign_bit(j, values, test_bit(i, bits));
+ descs[j++] = fwd->descs[i];
+ }
+
+ if (fwd->chip.can_sleep)
+ gpiod_set_array_value_cansleep(j, descs, NULL, values);
+ else
+ gpiod_set_array_value(j, descs, NULL, values);
+}
+
+static void gpio_fwd_set_multiple_locked(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+ unsigned long flags;
+
+ if (chip->can_sleep) {
+ mutex_lock(&fwd->mlock);
+ gpio_fwd_set_multiple(fwd, mask, bits);
+ mutex_unlock(&fwd->mlock);
+ } else {
+ spin_lock_irqsave(&fwd->slock, flags);
+ gpio_fwd_set_multiple(fwd, mask, bits);
+ spin_unlock_irqrestore(&fwd->slock, flags);
+ }
+}
+
+static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return gpiod_set_config(fwd->descs[offset], config);
+}
+
+static int gpio_fwd_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return gpiod_to_irq(fwd->descs[offset]);
+}
+
+/**
+ * gpiochip_fwd_create() - Create a new GPIO forwarder
+ * @dev: Parent device pointer
+ * @ngpios: Number of GPIOs in the forwarder.
+ * @descs: Array containing the GPIO descriptors to forward to.
+ * This array must contain @ngpios entries, and must not be deallocated
+ * before the forwarder has been destroyed again.
+ *
+ * This function creates a new gpiochip, which forwards all GPIO operations to
+ * the passed GPIO descriptors.
+ *
+ * Return: An opaque object pointer, or an ERR_PTR()-encoded negative error
+ * code on failure.
+ */
+static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
+ unsigned int ngpios,
+ struct gpio_desc *descs[])
+{
+ const char *label = dev_name(dev);
+ struct gpiochip_fwd *fwd;
+ struct gpio_chip *chip;
+ unsigned int i;
+ int error;
+
+ fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)),
+ GFP_KERNEL);
+ if (!fwd)
+ return ERR_PTR(-ENOMEM);
+
+ chip = &fwd->chip;
+
+ /*
+ * If any of the GPIO lines are sleeping, then the entire forwarder
+ * will be sleeping.
+ * If any of the chips support .set_config(), then the forwarder will
+ * support setting configs.
+ */
+ for (i = 0; i < ngpios; i++) {
+ struct gpio_chip *parent = gpiod_to_chip(descs[i]);
+
+ dev_dbg(dev, "%u => gpio %d irq %d\n", i,
+ desc_to_gpio(descs[i]), gpiod_to_irq(descs[i]));
+
+ if (gpiod_cansleep(descs[i]))
+ chip->can_sleep = true;
+ if (parent && parent->set_config)
+ chip->set_config = gpio_fwd_set_config;
+ }
+
+ chip->label = label;
+ chip->parent = dev;
+ chip->owner = THIS_MODULE;
+ chip->get_direction = gpio_fwd_get_direction;
+ chip->direction_input = gpio_fwd_direction_input;
+ chip->direction_output = gpio_fwd_direction_output;
+ chip->get = gpio_fwd_get;
+ chip->get_multiple = gpio_fwd_get_multiple_locked;
+ chip->set = gpio_fwd_set;
+ chip->set_multiple = gpio_fwd_set_multiple_locked;
+ chip->to_irq = gpio_fwd_to_irq;
+ chip->base = -1;
+ chip->ngpio = ngpios;
+ fwd->descs = descs;
+
+ if (chip->can_sleep)
+ mutex_init(&fwd->mlock);
+ else
+ spin_lock_init(&fwd->slock);
+
+ error = devm_gpiochip_add_data(dev, chip, fwd);
+ if (error)
+ return ERR_PTR(error);
+
+ return fwd;
+}
+
+
+/*
+ * GPIO Aggregator platform device
+ */
+
+static int gpio_aggregator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_desc **descs;
+ struct gpiochip_fwd *fwd;
+ int i, n;
+
+ n = gpiod_count(dev, NULL);
+ if (n < 0)
+ return n;
+
+ descs = devm_kmalloc_array(dev, n, sizeof(*descs), GFP_KERNEL);
+ if (!descs)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
+ if (IS_ERR(descs[i]))
+ return PTR_ERR(descs[i]);
+ }
+
+ fwd = gpiochip_fwd_create(dev, n, descs);
+ if (IS_ERR(fwd))
+ return PTR_ERR(fwd);
+
+ platform_set_drvdata(pdev, fwd);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpio_aggregator_dt_ids[] = {
+ /*
+ * Add GPIO-operated devices controlled from userspace below,
+ * or use "driver_override" in sysfs
+ */
+ {}
+};
+MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids);
+#endif
+
+static struct platform_driver gpio_aggregator_driver = {
+ .probe = gpio_aggregator_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .groups = gpio_aggregator_groups,
+ .of_match_table = of_match_ptr(gpio_aggregator_dt_ids),
+ },
+};
+
+static int __init gpio_aggregator_init(void)
+{
+ return platform_driver_register(&gpio_aggregator_driver);
+}
+module_init(gpio_aggregator_init);
+
+static void __exit gpio_aggregator_exit(void)
+{
+ gpio_aggregator_remove_all();
+ platform_driver_unregister(&gpio_aggregator_driver);
+}
+module_exit(gpio_aggregator_exit);
+
+MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
+MODULE_DESCRIPTION("GPIO Aggregator");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c
index b5917c48e4dc..be1ed7ee5225 100644
--- a/drivers/gpio/gpio-altera-a10sr.c
+++ b/drivers/gpio/gpio-altera-a10sr.c
@@ -10,6 +10,7 @@
#include <linux/gpio/driver.h>
#include <linux/mfd/altera-a10sr.h>
#include <linux/module.h>
+#include <linux/property.h>
/**
* struct altr_a10sr_gpio - Altera Max5 GPIO device private data structure
@@ -78,7 +79,6 @@ static const struct gpio_chip altr_a10sr_gc = {
static int altr_a10sr_gpio_probe(struct platform_device *pdev)
{
struct altr_a10sr_gpio *gpio;
- int ret;
struct altr_a10sr *a10sr = dev_get_drvdata(pdev->dev.parent);
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
@@ -89,17 +89,9 @@ static int altr_a10sr_gpio_probe(struct platform_device *pdev)
gpio->gp = altr_a10sr_gc;
gpio->gp.parent = pdev->dev.parent;
- gpio->gp.of_node = pdev->dev.of_node;
+ gpio->gp.fwnode = dev_fwnode(&pdev->dev);
- ret = devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, gpio);
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
}
static const struct of_device_id altr_a10sr_gpio_of_match[] = {
diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c
index 9f2e6b04c361..b59fae993626 100644
--- a/drivers/gpio/gpio-altera.c
+++ b/drivers/gpio/gpio-altera.c
@@ -24,6 +24,7 @@
* @interrupt_trigger : specifies the hardware configured IRQ trigger type
* (rising, falling, both, high)
* @mapped_irq : kernel mapped irq number.
+* @irq_chip : IRQ chip configuration
*/
struct altera_gpio_chip {
struct of_mm_gpio_chip mmchip;
@@ -69,7 +70,7 @@ static void altera_gpio_irq_mask(struct irq_data *d)
raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
}
-/**
+/*
* This controller's IRQ type is synthesized in hardware, so this function
* just checks if the requested set_type matches the synthesized IRQ type
*/
@@ -200,9 +201,8 @@ static void altera_gpio_irq_edge_handler(struct irq_desc *desc)
(readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) &
readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) {
writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP);
- for_each_set_bit(i, &status, mm_gc->gc.ngpio) {
- generic_handle_irq(irq_find_mapping(irqdomain, i));
- }
+ for_each_set_bit(i, &status, mm_gc->gc.ngpio)
+ generic_handle_domain_irq(irqdomain, i);
}
chained_irq_exit(chip, desc);
@@ -227,9 +227,9 @@ static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc)
status = readl(mm_gc->regs + ALTERA_GPIO_DATA);
status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
- for_each_set_bit(i, &status, mm_gc->gc.ngpio) {
- generic_handle_irq(irq_find_mapping(irqdomain, i));
- }
+ for_each_set_bit(i, &status, mm_gc->gc.ngpio)
+ generic_handle_domain_irq(irqdomain, i);
+
chained_irq_exit(chip, desc);
}
@@ -266,7 +266,7 @@ static int altera_gpio_probe(struct platform_device *pdev)
altera_gc->mmchip.gc.owner = THIS_MODULE;
altera_gc->mmchip.gc.parent = &pdev->dev;
- altera_gc->mapped_irq = platform_get_irq(pdev, 0);
+ altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0);
if (altera_gc->mapped_irq < 0)
goto skip_irq;
diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c
index 181df1581df5..2a21354ed6a0 100644
--- a/drivers/gpio/gpio-amd-fch.c
+++ b/drivers/gpio/gpio-amd-fch.c
@@ -92,7 +92,7 @@ static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION);
spin_unlock_irqrestore(&priv->lock, flags);
- return ret;
+ return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}
static void amd_fch_gpio_set(struct gpio_chip *gc,
diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c
index fdcebe59510d..14e6b3e64add 100644
--- a/drivers/gpio/gpio-amd8111.c
+++ b/drivers/gpio/gpio-amd8111.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* GPIO driver for AMD 8111 south bridges
*
@@ -20,10 +21,6 @@
* Hardware driver for Intel i810 Random Number Generator (RNG)
* Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <linux/ioport.h>
#include <linux/module.h>
@@ -179,7 +176,6 @@ static int __init amd_gpio_init(void)
struct pci_dev *pdev = NULL;
const struct pci_device_id *ent;
-
/* We look for our device - AMD South Bridge
* I don't know about a system with two such bridges,
* so we can assume that there is max. one device.
@@ -223,11 +219,10 @@ found:
spin_lock_init(&gp.lock);
- printk(KERN_INFO "AMD-8111 GPIO detected\n");
+ dev_info(&pdev->dev, "AMD-8111 GPIO detected\n");
err = gpiochip_add_data(&gp.chip, &gp);
if (err) {
- printk(KERN_ERR "GPIO registering failed (%d)\n",
- err);
+ dev_err(&pdev->dev, "GPIO registering failed (%d)\n", err);
ioport_unmap(gp.pm);
goto out;
}
diff --git a/drivers/gpio/gpio-amdpt.c b/drivers/gpio/gpio-amdpt.c
index 44398992ae15..07c6d090058d 100644
--- a/drivers/gpio/gpio-amdpt.c
+++ b/drivers/gpio/gpio-amdpt.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#define PT_TOTAL_GPIO 8
+#define PT_TOTAL_GPIO_EX 24
/* PCI-E MMIO register offsets */
#define PT_DIRECTION_REG 0x00
@@ -35,19 +36,19 @@ static int pt_gpio_request(struct gpio_chip *gc, unsigned offset)
dev_dbg(gc->parent, "pt_gpio_request offset=%x\n", offset);
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
if (using_pins & BIT(offset)) {
dev_warn(gc->parent, "PT GPIO pin %x reconfigured\n",
offset);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return -EINVAL;
}
writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@@ -58,13 +59,13 @@ static void pt_gpio_free(struct gpio_chip *gc, unsigned offset)
unsigned long flags;
u32 using_pins;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
using_pins &= ~BIT(offset);
writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
dev_dbg(gc->parent, "pt_gpio_free offset=%x\n", offset);
}
@@ -72,12 +73,10 @@ static void pt_gpio_free(struct gpio_chip *gc, unsigned offset)
static int pt_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct acpi_device *acpi_dev;
- acpi_handle handle = ACPI_HANDLE(dev);
struct pt_gpio_chip *pt_gpio;
int ret = 0;
- if (acpi_bus_get_device(handle, &acpi_dev)) {
+ if (!ACPI_COMPANION(dev)) {
dev_err(dev, "PT GPIO device node not found\n");
return -ENODEV;
}
@@ -105,10 +104,8 @@ static int pt_gpio_probe(struct platform_device *pdev)
pt_gpio->gc.owner = THIS_MODULE;
pt_gpio->gc.request = pt_gpio_request;
pt_gpio->gc.free = pt_gpio_free;
- pt_gpio->gc.ngpio = PT_TOTAL_GPIO;
-#if defined(CONFIG_OF_GPIO)
- pt_gpio->gc.of_node = dev->of_node;
-#endif
+ pt_gpio->gc.ngpio = (uintptr_t)device_get_match_data(dev);
+
ret = gpiochip_add_data(&pt_gpio->gc, pt_gpio);
if (ret) {
dev_err(dev, "Failed to register GPIO lib\n");
@@ -135,8 +132,9 @@ static int pt_gpio_remove(struct platform_device *pdev)
}
static const struct acpi_device_id pt_gpio_acpi_match[] = {
- { "AMDF030", 0 },
- { "AMDIF030", 0 },
+ { "AMDF030", PT_TOTAL_GPIO },
+ { "AMDIF030", PT_TOTAL_GPIO },
+ { "AMDIF031", PT_TOTAL_GPIO_EX },
{ },
};
MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match);
diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c
index c07fad975049..02f9ae19cd44 100644
--- a/drivers/gpio/gpio-arizona.c
+++ b/drivers/gpio/gpio-arizona.c
@@ -64,6 +64,7 @@ static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)
ret = pm_runtime_get_sync(chip->parent);
if (ret < 0) {
dev_err(chip->parent, "Failed to resume: %d\n", ret);
+ pm_runtime_put_autosuspend(chip->parent);
return ret;
}
@@ -72,12 +73,15 @@ static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)
if (ret < 0) {
dev_err(chip->parent, "Failed to drop cache: %d\n",
ret);
+ pm_runtime_put_autosuspend(chip->parent);
return ret;
}
ret = regmap_read(arizona->regmap, reg, &val);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_autosuspend(chip->parent);
return ret;
+ }
pm_runtime_mark_last_busy(chip->parent);
pm_runtime_put_autosuspend(chip->parent);
@@ -106,6 +110,7 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip,
ret = pm_runtime_get_sync(chip->parent);
if (ret < 0) {
dev_err(chip->parent, "Failed to resume: %d\n", ret);
+ pm_runtime_put(chip->parent);
return ret;
}
}
@@ -142,10 +147,12 @@ static const struct gpio_chip template_chip = {
static int arizona_gpio_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
- struct arizona_pdata *pdata = dev_get_platdata(arizona->dev);
+ struct arizona_pdata *pdata = &arizona->pdata;
struct arizona_gpio *arizona_gpio;
int ret;
+ device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent));
+
arizona_gpio = devm_kzalloc(&pdev->dev, sizeof(*arizona_gpio),
GFP_KERNEL);
if (!arizona_gpio)
@@ -154,9 +161,6 @@ static int arizona_gpio_probe(struct platform_device *pdev)
arizona_gpio->arizona = arizona;
arizona_gpio->gpio_chip = template_chip;
arizona_gpio->gpio_chip.parent = &pdev->dev;
-#ifdef CONFIG_OF_GPIO
- arizona_gpio->gpio_chip.of_node = arizona->dev->of_node;
-#endif
switch (arizona->type) {
case WM5102:
@@ -177,7 +181,7 @@ static int arizona_gpio_probe(struct platform_device *pdev)
return -EINVAL;
}
- if (pdata && pdata->gpio_base)
+ if (pdata->gpio_base)
arizona_gpio->gpio_chip.base = pdata->gpio_base;
else
arizona_gpio->gpio_chip.base = -1;
@@ -187,6 +191,7 @@ static int arizona_gpio_probe(struct platform_device *pdev)
ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip,
arizona_gpio);
if (ret < 0) {
+ pm_runtime_disable(&pdev->dev);
dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
ret);
return ret;
diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c
new file mode 100644
index 000000000000..454cefbeecf0
--- /dev/null
+++ b/drivers/gpio/gpio-aspeed-sgpio.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 American Megatrends International LLC.
+ *
+ * Author: Karthikeyan Mani <karthikeyanm@amiindia.co.in>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/gpio/driver.h>
+#include <linux/hashtable.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#define ASPEED_SGPIO_CTRL 0x54
+
+#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16)
+#define ASPEED_SGPIO_ENABLE BIT(0)
+#define ASPEED_SGPIO_PINS_SHIFT 6
+
+struct aspeed_sgpio_pdata {
+ const u32 pin_mask;
+};
+
+struct aspeed_sgpio {
+ struct gpio_chip chip;
+ struct irq_chip intc;
+ struct clk *pclk;
+ raw_spinlock_t lock;
+ void __iomem *base;
+ int irq;
+};
+
+struct aspeed_sgpio_bank {
+ u16 val_regs;
+ u16 rdata_reg;
+ u16 irq_regs;
+ u16 tolerance_regs;
+ const char names[4][3];
+};
+
+/*
+ * Note: The "value" register returns the input value when the GPIO is
+ * configured as an input.
+ *
+ * The "rdata" register returns the output value when the GPIO is
+ * configured as an output.
+ */
+static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = {
+ {
+ .val_regs = 0x0000,
+ .rdata_reg = 0x0070,
+ .irq_regs = 0x0004,
+ .tolerance_regs = 0x0018,
+ .names = { "A", "B", "C", "D" },
+ },
+ {
+ .val_regs = 0x001C,
+ .rdata_reg = 0x0074,
+ .irq_regs = 0x0020,
+ .tolerance_regs = 0x0034,
+ .names = { "E", "F", "G", "H" },
+ },
+ {
+ .val_regs = 0x0038,
+ .rdata_reg = 0x0078,
+ .irq_regs = 0x003C,
+ .tolerance_regs = 0x0050,
+ .names = { "I", "J", "K", "L" },
+ },
+ {
+ .val_regs = 0x0090,
+ .rdata_reg = 0x007C,
+ .irq_regs = 0x0094,
+ .tolerance_regs = 0x00A8,
+ .names = { "M", "N", "O", "P" },
+ },
+};
+
+enum aspeed_sgpio_reg {
+ reg_val,
+ reg_rdata,
+ reg_irq_enable,
+ reg_irq_type0,
+ reg_irq_type1,
+ reg_irq_type2,
+ reg_irq_status,
+ reg_tolerance,
+};
+
+#define GPIO_VAL_VALUE 0x00
+#define GPIO_IRQ_ENABLE 0x00
+#define GPIO_IRQ_TYPE0 0x04
+#define GPIO_IRQ_TYPE1 0x08
+#define GPIO_IRQ_TYPE2 0x0C
+#define GPIO_IRQ_STATUS 0x10
+
+static void __iomem *bank_reg(struct aspeed_sgpio *gpio,
+ const struct aspeed_sgpio_bank *bank,
+ const enum aspeed_sgpio_reg reg)
+{
+ switch (reg) {
+ case reg_val:
+ return gpio->base + bank->val_regs + GPIO_VAL_VALUE;
+ case reg_rdata:
+ return gpio->base + bank->rdata_reg;
+ case reg_irq_enable:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE;
+ case reg_irq_type0:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0;
+ case reg_irq_type1:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1;
+ case reg_irq_type2:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2;
+ case reg_irq_status:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS;
+ case reg_tolerance:
+ return gpio->base + bank->tolerance_regs;
+ default:
+ /* acturally if code runs to here, it's an error case */
+ BUG();
+ }
+}
+
+#define GPIO_BANK(x) ((x) >> 6)
+#define GPIO_OFFSET(x) ((x) & GENMASK(5, 0))
+#define GPIO_BIT(x) BIT(GPIO_OFFSET(x) >> 1)
+
+static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
+{
+ unsigned int bank;
+
+ bank = GPIO_BANK(offset);
+
+ WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks));
+ return &aspeed_sgpio_banks[bank];
+}
+
+static int aspeed_sgpio_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask, unsigned int ngpios)
+{
+ bitmap_set(valid_mask, 0, ngpios);
+ return 0;
+}
+
+static void aspeed_sgpio_irq_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask, unsigned int ngpios)
+{
+ unsigned int i;
+
+ /* input GPIOs are even bits */
+ for (i = 0; i < ngpios; i++) {
+ if (i % 2)
+ clear_bit(i, valid_mask);
+ }
+}
+
+static bool aspeed_sgpio_is_input(unsigned int offset)
+{
+ return !(offset % 2);
+}
+
+static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+ const struct aspeed_sgpio_bank *bank = to_bank(offset);
+ unsigned long flags;
+ enum aspeed_sgpio_reg reg;
+ int rc = 0;
+
+ raw_spin_lock_irqsave(&gpio->lock, flags);
+
+ reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata;
+ rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset));
+
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+
+ return rc;
+}
+
+static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+ const struct aspeed_sgpio_bank *bank = to_bank(offset);
+ void __iomem *addr_r, *addr_w;
+ u32 reg = 0;
+
+ if (aspeed_sgpio_is_input(offset))
+ return -EINVAL;
+
+ /* Since this is an output, read the cached value from rdata, then
+ * update val. */
+ addr_r = bank_reg(gpio, bank, reg_rdata);
+ addr_w = bank_reg(gpio, bank, reg_val);
+
+ reg = ioread32(addr_r);
+
+ if (val)
+ reg |= GPIO_BIT(offset);
+ else
+ reg &= ~GPIO_BIT(offset);
+
+ iowrite32(reg, addr_w);
+
+ return 0;
+}
+
+static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&gpio->lock, flags);
+
+ sgpio_set_value(gc, offset, val);
+
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
+static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset)
+{
+ return aspeed_sgpio_is_input(offset) ? 0 : -EINVAL;
+}
+
+static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+ unsigned long flags;
+ int rc;
+
+ /* No special action is required for setting the direction; we'll
+ * error-out in sgpio_set_value if this isn't an output GPIO */
+
+ raw_spin_lock_irqsave(&gpio->lock, flags);
+ rc = sgpio_set_value(gc, offset, val);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+
+ return rc;
+}
+
+static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ return !!aspeed_sgpio_is_input(offset);
+}
+
+static void irqd_to_aspeed_sgpio_data(struct irq_data *d,
+ struct aspeed_sgpio **gpio,
+ const struct aspeed_sgpio_bank **bank,
+ u32 *bit, int *offset)
+{
+ struct aspeed_sgpio *internal;
+
+ *offset = irqd_to_hwirq(d);
+ internal = irq_data_get_irq_chip_data(d);
+ WARN_ON(!internal);
+
+ *gpio = internal;
+ *bank = to_bank(*offset);
+ *bit = GPIO_BIT(*offset);
+}
+
+static void aspeed_sgpio_irq_ack(struct irq_data *d)
+{
+ const struct aspeed_sgpio_bank *bank;
+ struct aspeed_sgpio *gpio;
+ unsigned long flags;
+ void __iomem *status_addr;
+ int offset;
+ u32 bit;
+
+ irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
+
+ status_addr = bank_reg(gpio, bank, reg_irq_status);
+
+ raw_spin_lock_irqsave(&gpio->lock, flags);
+
+ iowrite32(bit, status_addr);
+
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
+static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set)
+{
+ const struct aspeed_sgpio_bank *bank;
+ struct aspeed_sgpio *gpio;
+ unsigned long flags;
+ u32 reg, bit;
+ void __iomem *addr;
+ int offset;
+
+ irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
+ addr = bank_reg(gpio, bank, reg_irq_enable);
+
+ raw_spin_lock_irqsave(&gpio->lock, flags);
+
+ reg = ioread32(addr);
+ if (set)
+ reg |= bit;
+ else
+ reg &= ~bit;
+
+ iowrite32(reg, addr);
+
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
+static void aspeed_sgpio_irq_mask(struct irq_data *d)
+{
+ aspeed_sgpio_irq_set_mask(d, false);
+}
+
+static void aspeed_sgpio_irq_unmask(struct irq_data *d)
+{
+ aspeed_sgpio_irq_set_mask(d, true);
+}
+
+static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type)
+{
+ u32 type0 = 0;
+ u32 type1 = 0;
+ u32 type2 = 0;
+ u32 bit, reg;
+ const struct aspeed_sgpio_bank *bank;
+ irq_flow_handler_t handler;
+ struct aspeed_sgpio *gpio;
+ unsigned long flags;
+ void __iomem *addr;
+ int offset;
+
+ irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_BOTH:
+ type2 |= bit;
+ fallthrough;
+ case IRQ_TYPE_EDGE_RISING:
+ type0 |= bit;
+ fallthrough;
+ case IRQ_TYPE_EDGE_FALLING:
+ handler = handle_edge_irq;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ type0 |= bit;
+ fallthrough;
+ case IRQ_TYPE_LEVEL_LOW:
+ type1 |= bit;
+ handler = handle_level_irq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ raw_spin_lock_irqsave(&gpio->lock, flags);
+
+ addr = bank_reg(gpio, bank, reg_irq_type0);
+ reg = ioread32(addr);
+ reg = (reg & ~bit) | type0;
+ iowrite32(reg, addr);
+
+ addr = bank_reg(gpio, bank, reg_irq_type1);
+ reg = ioread32(addr);
+ reg = (reg & ~bit) | type1;
+ iowrite32(reg, addr);
+
+ addr = bank_reg(gpio, bank, reg_irq_type2);
+ reg = ioread32(addr);
+ reg = (reg & ~bit) | type2;
+ iowrite32(reg, addr);
+
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+
+ irq_set_handler_locked(d, handler);
+
+ return 0;
+}
+
+static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct irq_chip *ic = irq_desc_get_chip(desc);
+ struct aspeed_sgpio *data = gpiochip_get_data(gc);
+ unsigned int i, p;
+ unsigned long reg;
+
+ chained_irq_enter(ic, desc);
+
+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
+ const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i];
+
+ reg = ioread32(bank_reg(data, bank, reg_irq_status));
+
+ for_each_set_bit(p, &reg, 32)
+ generic_handle_domain_irq(gc->irq.domain, (i * 32 + p) * 2);
+ }
+
+ chained_irq_exit(ic, desc);
+}
+
+static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
+ struct platform_device *pdev)
+{
+ int rc, i;
+ const struct aspeed_sgpio_bank *bank;
+ struct gpio_irq_chip *irq;
+
+ rc = platform_get_irq(pdev, 0);
+ if (rc < 0)
+ return rc;
+
+ gpio->irq = rc;
+
+ /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */
+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
+ bank = &aspeed_sgpio_banks[i];
+ /* disable irq enable bits */
+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable));
+ /* clear status bits */
+ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status));
+ }
+
+ gpio->intc.name = dev_name(&pdev->dev);
+ gpio->intc.irq_ack = aspeed_sgpio_irq_ack;
+ gpio->intc.irq_mask = aspeed_sgpio_irq_mask;
+ gpio->intc.irq_unmask = aspeed_sgpio_irq_unmask;
+ gpio->intc.irq_set_type = aspeed_sgpio_set_type;
+
+ irq = &gpio->chip.irq;
+ irq->chip = &gpio->intc;
+ irq->init_valid_mask = aspeed_sgpio_irq_init_valid_mask;
+ irq->handler = handle_bad_irq;
+ irq->default_type = IRQ_TYPE_NONE;
+ irq->parent_handler = aspeed_sgpio_irq_handler;
+ irq->parent_handler_data = gpio;
+ irq->parents = &gpio->irq;
+ irq->num_parents = 1;
+
+ /* Apply default IRQ settings */
+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
+ bank = &aspeed_sgpio_banks[i];
+ /* set falling or level-low irq */
+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0));
+ /* trigger type is edge */
+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1));
+ /* single edge trigger */
+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2));
+ }
+
+ return 0;
+}
+
+static const struct aspeed_sgpio_pdata ast2400_sgpio_pdata = {
+ .pin_mask = GENMASK(9, 6),
+};
+
+static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip,
+ unsigned int offset, bool enable)
+{
+ struct aspeed_sgpio *gpio = gpiochip_get_data(chip);
+ unsigned long flags;
+ void __iomem *reg;
+ u32 val;
+
+ reg = bank_reg(gpio, to_bank(offset), reg_tolerance);
+
+ raw_spin_lock_irqsave(&gpio->lock, flags);
+
+ val = readl(reg);
+
+ if (enable)
+ val |= GPIO_BIT(offset);
+ else
+ val &= ~GPIO_BIT(offset);
+
+ writel(val, reg);
+
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+
+ return 0;
+}
+
+static int aspeed_sgpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ unsigned long param = pinconf_to_config_param(config);
+ u32 arg = pinconf_to_config_argument(config);
+
+ if (param == PIN_CONFIG_PERSIST_STATE)
+ return aspeed_sgpio_reset_tolerance(chip, offset, arg);
+
+ return -ENOTSUPP;
+}
+
+static const struct aspeed_sgpio_pdata ast2600_sgpiom_pdata = {
+ .pin_mask = GENMASK(10, 6),
+};
+
+static const struct of_device_id aspeed_sgpio_of_table[] = {
+ { .compatible = "aspeed,ast2400-sgpio", .data = &ast2400_sgpio_pdata, },
+ { .compatible = "aspeed,ast2500-sgpio", .data = &ast2400_sgpio_pdata, },
+ { .compatible = "aspeed,ast2600-sgpiom", .data = &ast2600_sgpiom_pdata, },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table);
+
+static int __init aspeed_sgpio_probe(struct platform_device *pdev)
+{
+ u32 nr_gpios, sgpio_freq, sgpio_clk_div, gpio_cnt_regval, pin_mask;
+ const struct aspeed_sgpio_pdata *pdata;
+ struct aspeed_sgpio *gpio;
+ unsigned long apb_freq;
+ int rc;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(gpio->base))
+ return PTR_ERR(gpio->base);
+
+ pdata = device_get_match_data(&pdev->dev);
+ if (!pdata)
+ return -EINVAL;
+
+ pin_mask = pdata->pin_mask;
+
+ rc = device_property_read_u32(&pdev->dev, "ngpios", &nr_gpios);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Could not read ngpios property\n");
+ return -EINVAL;
+ } else if (nr_gpios % 8) {
+ dev_err(&pdev->dev, "Number of GPIOs not multiple of 8: %d\n",
+ nr_gpios);
+ return -EINVAL;
+ }
+
+ rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Could not read bus-frequency property\n");
+ return -EINVAL;
+ }
+
+ gpio->pclk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(gpio->pclk)) {
+ dev_err(&pdev->dev, "devm_clk_get failed\n");
+ return PTR_ERR(gpio->pclk);
+ }
+
+ apb_freq = clk_get_rate(gpio->pclk);
+
+ /*
+ * From the datasheet,
+ * SGPIO period = 1/PCLK * 2 * (GPIO254[31:16] + 1)
+ * period = 2 * (GPIO254[31:16] + 1) / PCLK
+ * frequency = 1 / (2 * (GPIO254[31:16] + 1) / PCLK)
+ * frequency = PCLK / (2 * (GPIO254[31:16] + 1))
+ * frequency * 2 * (GPIO254[31:16] + 1) = PCLK
+ * GPIO254[31:16] = PCLK / (frequency * 2) - 1
+ */
+ if (sgpio_freq == 0)
+ return -EINVAL;
+
+ sgpio_clk_div = (apb_freq / (sgpio_freq * 2)) - 1;
+
+ if (sgpio_clk_div > (1 << 16) - 1)
+ return -EINVAL;
+
+ gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask;
+ iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval |
+ ASPEED_SGPIO_ENABLE, gpio->base + ASPEED_SGPIO_CTRL);
+
+ raw_spin_lock_init(&gpio->lock);
+
+ gpio->chip.parent = &pdev->dev;
+ gpio->chip.ngpio = nr_gpios * 2;
+ gpio->chip.init_valid_mask = aspeed_sgpio_init_valid_mask;
+ gpio->chip.direction_input = aspeed_sgpio_dir_in;
+ gpio->chip.direction_output = aspeed_sgpio_dir_out;
+ gpio->chip.get_direction = aspeed_sgpio_get_direction;
+ gpio->chip.request = NULL;
+ gpio->chip.free = NULL;
+ gpio->chip.get = aspeed_sgpio_get;
+ gpio->chip.set = aspeed_sgpio_set;
+ gpio->chip.set_config = aspeed_sgpio_set_config;
+ gpio->chip.label = dev_name(&pdev->dev);
+ gpio->chip.base = -1;
+
+ aspeed_sgpio_setup_irqs(gpio, pdev);
+
+ rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static struct platform_driver aspeed_sgpio_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = aspeed_sgpio_of_table,
+ },
+};
+
+module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe);
+MODULE_DESCRIPTION("Aspeed Serial GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 13d80bfbc3b6..318a7d95a1a8 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -52,7 +52,8 @@ struct aspeed_gpio_config {
*/
struct aspeed_gpio {
struct gpio_chip chip;
- spinlock_t lock;
+ struct irq_chip irqc;
+ raw_spinlock_t lock;
void __iomem *base;
int irq;
const struct aspeed_gpio_config *config;
@@ -412,14 +413,14 @@ static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
unsigned long flags;
bool copro;
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
copro = aspeed_gpio_copro_request(gpio, offset);
__aspeed_gpio_set(gc, offset, val);
if (copro)
aspeed_gpio_copro_release(gpio, offset);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
}
static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
@@ -434,7 +435,7 @@ static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
if (!have_input(gpio, offset))
return -ENOTSUPP;
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
reg = ioread32(addr);
reg &= ~GPIO_BIT(offset);
@@ -444,7 +445,7 @@ static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
if (copro)
aspeed_gpio_copro_release(gpio, offset);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
}
@@ -462,7 +463,7 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
if (!have_output(gpio, offset))
return -ENOTSUPP;
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
reg = ioread32(addr);
reg |= GPIO_BIT(offset);
@@ -473,7 +474,7 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
if (copro)
aspeed_gpio_copro_release(gpio, offset);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
}
@@ -486,19 +487,18 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
u32 val;
if (!have_input(gpio, offset))
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
if (!have_output(gpio, offset))
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
val = ioread32(bank_reg(gpio, bank, reg_dir)) & GPIO_BIT(offset);
- spin_unlock_irqrestore(&gpio->lock, flags);
-
- return !val;
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+ return val ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}
static inline int irqd_to_aspeed_gpio_data(struct irq_data *d,
@@ -539,14 +539,14 @@ static void aspeed_gpio_irq_ack(struct irq_data *d)
status_addr = bank_reg(gpio, bank, reg_irq_status);
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
copro = aspeed_gpio_copro_request(gpio, offset);
iowrite32(bit, status_addr);
if (copro)
aspeed_gpio_copro_release(gpio, offset);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
}
static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
@@ -565,7 +565,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
addr = bank_reg(gpio, bank, reg_irq_enable);
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
copro = aspeed_gpio_copro_request(gpio, offset);
reg = ioread32(addr);
@@ -577,7 +577,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
if (copro)
aspeed_gpio_copro_release(gpio, offset);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
}
static void aspeed_gpio_irq_mask(struct irq_data *d)
@@ -611,16 +611,16 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_BOTH:
type2 |= bit;
- /* fall through */
+ fallthrough;
case IRQ_TYPE_EDGE_RISING:
type0 |= bit;
- /* fall through */
+ fallthrough;
case IRQ_TYPE_EDGE_FALLING:
handler = handle_edge_irq;
break;
case IRQ_TYPE_LEVEL_HIGH:
type0 |= bit;
- /* fall through */
+ fallthrough;
case IRQ_TYPE_LEVEL_LOW:
type1 |= bit;
handler = handle_level_irq;
@@ -629,7 +629,7 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
copro = aspeed_gpio_copro_request(gpio, offset);
addr = bank_reg(gpio, bank, reg_irq_type0);
@@ -649,7 +649,7 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
if (copro)
aspeed_gpio_copro_release(gpio, offset);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
irq_set_handler_locked(d, handler);
@@ -661,36 +661,30 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc)
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct irq_chip *ic = irq_desc_get_chip(desc);
struct aspeed_gpio *data = gpiochip_get_data(gc);
- unsigned int i, p, girq;
+ unsigned int i, p, banks;
unsigned long reg;
+ struct aspeed_gpio *gpio = gpiochip_get_data(gc);
chained_irq_enter(ic, desc);
- for (i = 0; i < ARRAY_SIZE(aspeed_gpio_banks); i++) {
+ banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
+ for (i = 0; i < banks; i++) {
const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i];
reg = ioread32(bank_reg(data, bank, reg_irq_status));
- for_each_set_bit(p, &reg, 32) {
- girq = irq_find_mapping(gc->irq.domain, i * 32 + p);
- generic_handle_irq(girq);
- }
-
+ for_each_set_bit(p, &reg, 32)
+ generic_handle_domain_irq(gc->irq.domain, i * 32 + p);
}
chained_irq_exit(ic, desc);
}
-static struct irq_chip aspeed_gpio_irqchip = {
- .name = "aspeed-gpio",
- .irq_ack = aspeed_gpio_irq_ack,
- .irq_mask = aspeed_gpio_irq_mask,
- .irq_unmask = aspeed_gpio_irq_unmask,
- .irq_set_type = aspeed_gpio_set_type,
-};
-
-static void set_irq_valid_mask(struct aspeed_gpio *gpio)
+static void aspeed_init_irq_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
{
+ struct aspeed_gpio *gpio = gpiochip_get_data(gc);
const struct aspeed_bank_props *props = gpio->config->props;
while (!is_bank_props_sentinel(props)) {
@@ -701,42 +695,16 @@ static void set_irq_valid_mask(struct aspeed_gpio *gpio)
for_each_clear_bit(offset, &input, 32) {
unsigned int i = props->bank * 32 + offset;
- if (i >= gpio->config->nr_gpios)
+ if (i >= gpio->chip.ngpio)
break;
- clear_bit(i, gpio->chip.irq.valid_mask);
+ clear_bit(i, valid_mask);
}
props++;
}
}
-static int aspeed_gpio_setup_irqs(struct aspeed_gpio *gpio,
- struct platform_device *pdev)
-{
- int rc;
-
- rc = platform_get_irq(pdev, 0);
- if (rc < 0)
- return rc;
-
- gpio->irq = rc;
-
- set_irq_valid_mask(gpio);
-
- rc = gpiochip_irqchip_add(&gpio->chip, &aspeed_gpio_irqchip,
- 0, handle_bad_irq, IRQ_TYPE_NONE);
- if (rc) {
- dev_info(&pdev->dev, "Could not add irqchip\n");
- return rc;
- }
-
- gpiochip_set_chained_irqchip(&gpio->chip, &aspeed_gpio_irqchip,
- gpio->irq, aspeed_gpio_irq_handler);
-
- return 0;
-}
-
static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip,
unsigned int offset, bool enable)
{
@@ -748,7 +716,7 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip,
treg = bank_reg(gpio, to_bank(offset), reg_tolerance);
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
copro = aspeed_gpio_copro_request(gpio, offset);
val = readl(treg);
@@ -762,7 +730,7 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip,
if (copro)
aspeed_gpio_copro_release(gpio, offset);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
}
@@ -888,7 +856,7 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset,
return rc;
}
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
if (timer_allocation_registered(gpio, offset)) {
rc = unregister_allocated_timer(gpio, offset);
@@ -948,7 +916,7 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset,
configure_timer(gpio, offset, i);
out:
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
return rc;
}
@@ -959,13 +927,13 @@ static int disable_debounce(struct gpio_chip *chip, unsigned int offset)
unsigned long flags;
int rc;
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
rc = unregister_allocated_timer(gpio, offset);
if (!rc)
configure_timer(gpio, offset, 0);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
return rc;
}
@@ -1007,7 +975,7 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
}
/**
- * aspeed_gpio_copro_set_ops - Sets the callbacks used for handhsaking with
+ * aspeed_gpio_copro_set_ops - Sets the callbacks used for handshaking with
* the coprocessor for shared GPIO banks
* @ops: The callbacks
* @data: Pointer passed back to the callbacks
@@ -1040,14 +1008,14 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc,
unsigned long flags;
if (!gpio->cf_copro_bankmap)
- gpio->cf_copro_bankmap = kzalloc(gpio->config->nr_gpios >> 3, GFP_KERNEL);
+ gpio->cf_copro_bankmap = kzalloc(gpio->chip.ngpio >> 3, GFP_KERNEL);
if (!gpio->cf_copro_bankmap)
return -ENOMEM;
- if (offset < 0 || offset > gpio->config->nr_gpios)
+ if (offset < 0 || offset > gpio->chip.ngpio)
return -EINVAL;
bindex = offset >> 3;
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
/* Sanity check, this shouldn't happen */
if (gpio->cf_copro_bankmap[bindex] == 0xff) {
@@ -1068,7 +1036,7 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc,
if (bit)
*bit = GPIO_OFFSET(offset);
bail:
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
return rc;
}
EXPORT_SYMBOL_GPL(aspeed_gpio_copro_grab_gpio);
@@ -1088,11 +1056,11 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc)
if (!gpio->cf_copro_bankmap)
return -ENXIO;
- if (offset < 0 || offset > gpio->config->nr_gpios)
+ if (offset < 0 || offset > gpio->chip.ngpio)
return -EINVAL;
bindex = offset >> 3;
- spin_lock_irqsave(&gpio->lock, flags);
+ raw_spin_lock_irqsave(&gpio->lock, flags);
/* Sanity check, this shouldn't happen */
if (gpio->cf_copro_bankmap[bindex] == 0) {
@@ -1106,7 +1074,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc)
aspeed_gpio_change_cmd_source(gpio, bank, bindex,
GPIO_CMDSRC_ARM);
bail:
- spin_unlock_irqrestore(&gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
return rc;
}
EXPORT_SYMBOL_GPL(aspeed_gpio_copro_release_gpio);
@@ -1141,9 +1109,26 @@ static const struct aspeed_gpio_config ast2500_config =
/* 232 for simplicity, actual number is 228 (4-GPIO hole in GPIOAB) */
{ .nr_gpios = 232, .props = ast2500_bank_props, };
+static const struct aspeed_bank_props ast2600_bank_props[] = {
+ /* input output */
+ {4, 0xffffffff, 0x00ffffff}, /* Q/R/S/T */
+ {5, 0xffffffff, 0xffffff00}, /* U/V/W/X */
+ {6, 0x0000ffff, 0x0000ffff}, /* Y/Z */
+ { },
+};
+
+static const struct aspeed_gpio_config ast2600_config =
+ /*
+ * ast2600 has two controllers one with 208 GPIOs and one with 36 GPIOs.
+ * We expect ngpio being set in the device tree and this is a fallback
+ * option.
+ */
+ { .nr_gpios = 208, .props = ast2600_bank_props, };
+
static const struct of_device_id aspeed_gpio_of_table[] = {
{ .compatible = "aspeed,ast2400-gpio", .data = &ast2400_config, },
{ .compatible = "aspeed,ast2500-gpio", .data = &ast2500_config, },
+ { .compatible = "aspeed,ast2600-gpio", .data = &ast2600_config, },
{}
};
MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table);
@@ -1152,7 +1137,8 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
{
const struct of_device_id *gpio_id;
struct aspeed_gpio *gpio;
- int rc, i, banks;
+ int rc, i, banks, err;
+ u32 ngpio;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
@@ -1162,7 +1148,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpio->base))
return PTR_ERR(gpio->base);
- spin_lock_init(&gpio->lock);
+ raw_spin_lock_init(&gpio->lock);
gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node);
if (!gpio_id)
@@ -1178,7 +1164,10 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
gpio->config = gpio_id->data;
gpio->chip.parent = &pdev->dev;
- gpio->chip.ngpio = gpio->config->nr_gpios;
+ err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio);
+ gpio->chip.ngpio = (u16) ngpio;
+ if (err)
+ gpio->chip.ngpio = gpio->config->nr_gpios;
gpio->chip.direction_input = aspeed_gpio_dir_in;
gpio->chip.direction_output = aspeed_gpio_dir_out;
gpio->chip.get_direction = aspeed_gpio_get_direction;
@@ -1189,10 +1178,9 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
gpio->chip.set_config = aspeed_gpio_set_config;
gpio->chip.label = dev_name(&pdev->dev);
gpio->chip.base = -1;
- gpio->chip.irq.need_valid_mask = true;
/* Allocate a cache of the output registers */
- banks = gpio->config->nr_gpios >> 5;
+ banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
gpio->dcache = devm_kcalloc(&pdev->dev,
banks, sizeof(u32), GFP_KERNEL);
if (!gpio->dcache)
@@ -1212,16 +1200,42 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
aspeed_gpio_change_cmd_source(gpio, bank, 3, GPIO_CMDSRC_ARM);
}
- rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
- if (rc < 0)
- return rc;
+ /* Optionally set up an irqchip if there is an IRQ */
+ rc = platform_get_irq(pdev, 0);
+ if (rc > 0) {
+ struct gpio_irq_chip *girq;
+
+ gpio->irq = rc;
+ girq = &gpio->chip.irq;
+ girq->chip = &gpio->irqc;
+ girq->chip->name = dev_name(&pdev->dev);
+ girq->chip->irq_ack = aspeed_gpio_irq_ack;
+ girq->chip->irq_mask = aspeed_gpio_irq_mask;
+ girq->chip->irq_unmask = aspeed_gpio_irq_unmask;
+ girq->chip->irq_set_type = aspeed_gpio_set_type;
+ girq->parent_handler = aspeed_gpio_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = gpio->irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
+ girq->init_valid_mask = aspeed_init_irq_valid_mask;
+ }
gpio->offset_timer =
devm_kzalloc(&pdev->dev, gpio->chip.ngpio, GFP_KERNEL);
if (!gpio->offset_timer)
return -ENOMEM;
- return aspeed_gpio_setup_irqs(gpio, pdev);
+ rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
+ if (rc < 0)
+ return rc;
+
+ return 0;
}
static struct platform_driver aspeed_gpio_driver = {
diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index f1a5ea9b3de2..3958c6d97639 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -123,13 +123,14 @@ static int ath79_gpio_irq_set_type(struct irq_data *data,
switch (flow_type) {
case IRQ_TYPE_EDGE_RISING:
polarity |= mask;
+ fallthrough;
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_EDGE_BOTH:
break;
case IRQ_TYPE_LEVEL_HIGH:
polarity |= mask;
- /* fall through */
+ fallthrough;
case IRQ_TYPE_LEVEL_LOW:
type |= mask;
break;
@@ -203,11 +204,8 @@ static void ath79_gpio_irq_handler(struct irq_desc *desc)
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
- if (pending) {
- for_each_set_bit(irq, &pending, gc->ngpio)
- generic_handle_irq(
- irq_linear_revmap(gc->irq.domain, irq));
- }
+ for_each_set_bit(irq, &pending, gc->ngpio)
+ generic_handle_domain_irq(gc->irq.domain, irq);
chained_irq_exit(irqchip, desc);
}
@@ -226,7 +224,6 @@ static int ath79_gpio_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct ath79_gpio_ctrl *ctrl;
struct gpio_irq_chip *girq;
- struct resource *res;
u32 ath79_gpio_count;
bool oe_inverted;
int err;
@@ -234,7 +231,6 @@ static int ath79_gpio_probe(struct platform_device *pdev)
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return -ENOMEM;
- platform_set_drvdata(pdev, ctrl);
if (np) {
err = of_property_read_u32(np, "ngpios", &ath79_gpio_count);
@@ -256,12 +252,9 @@ static int ath79_gpio_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
- ctrl->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
- if (!ctrl->base)
- return -ENOMEM;
+ ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ctrl->base))
+ return PTR_ERR(ctrl->base);
raw_spin_lock_init(&ctrl->lock);
err = bgpio_init(&ctrl->gc, dev, 4,
@@ -293,13 +286,7 @@ static int ath79_gpio_probe(struct platform_device *pdev)
girq->handler = handle_simple_irq;
}
- err = devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
- if (err) {
- dev_err(dev,
- "cannot add AR71xx GPIO chip, error=%d", err);
- return err;
- }
- return 0;
+ return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
}
static struct platform_driver ath79_gpio_driver = {
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index 9fa6d3a967d2..e84474494429 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -19,7 +19,6 @@
#include <linux/io.h>
#include <linux/gpio/driver.h>
#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/init.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
@@ -127,7 +126,7 @@ static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio)
u32 val;
val = readl(reg_base + GPIO_CONTROL(gpio)) & GPIO_GPCTR0_IOTR_MASK;
- return !!val;
+ return val ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
}
static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
@@ -144,7 +143,7 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
raw_spin_lock_irqsave(&kona_gpio->lock, flags);
/* this function only applies to output pin */
- if (bcm_kona_gpio_get_dir(chip, gpio) == 1)
+ if (bcm_kona_gpio_get_dir(chip, gpio) == GPIO_LINE_DIRECTION_IN)
goto out;
reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id);
@@ -170,7 +169,7 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio)
reg_base = kona_gpio->reg_base;
raw_spin_lock_irqsave(&kona_gpio->lock, flags);
- if (bcm_kona_gpio_get_dir(chip, gpio) == 1)
+ if (bcm_kona_gpio_get_dir(chip, gpio) == GPIO_LINE_DIRECTION_IN)
reg_offset = GPIO_IN_STATUS(bank_id);
else
reg_offset = GPIO_OUT_STATUS(bank_id);
@@ -467,9 +466,6 @@ static void bcm_kona_gpio_irq_handler(struct irq_desc *desc)
(~(readl(reg_base + GPIO_INT_MASK(bank_id)))))) {
for_each_set_bit(bit, &sta, 32) {
int hwirq = GPIO_PER_BANK * bank_id + bit;
- int child_irq =
- irq_find_mapping(bank->kona_gpio->irq_domain,
- hwirq);
/*
* Clear interrupt before handler is called so we don't
* miss any interrupt occurred during executing them.
@@ -477,7 +473,8 @@ static void bcm_kona_gpio_irq_handler(struct irq_desc *desc)
writel(readl(reg_base + GPIO_INT_STATUS(bank_id)) |
BIT(bit), reg_base + GPIO_INT_STATUS(bank_id));
/* Invoke interrupt handler */
- generic_handle_irq(child_irq);
+ generic_handle_domain_irq(bank->kona_gpio->irq_domain,
+ hwirq);
}
}
@@ -586,11 +583,15 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
kona_gpio->gpio_chip = template_chip;
chip = &kona_gpio->gpio_chip;
- kona_gpio->num_bank = of_irq_count(dev->of_node);
- if (kona_gpio->num_bank == 0) {
+ ret = platform_irq_count(pdev);
+ if (!ret) {
dev_err(dev, "Couldn't determine # GPIO banks\n");
return -ENOENT;
+ } else if (ret < 0) {
+ return dev_err_probe(dev, ret, "Couldn't determine GPIO banks\n");
}
+ kona_gpio->num_bank = ret;
+
if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) {
dev_err(dev, "Too many GPIO banks configured (max=%d)\n",
GPIO_MAX_BANK_NUM);
@@ -605,7 +606,7 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
kona_gpio->pdev = pdev;
platform_set_drvdata(pdev, kona_gpio);
- chip->of_node = dev->of_node;
+ chip->parent = dev;
chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK;
kona_gpio->irq_domain = irq_domain_add_linear(dev->of_node,
@@ -619,7 +620,7 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
kona_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(kona_gpio->reg_base)) {
- ret = -ENXIO;
+ ret = PTR_ERR(kona_gpio->reg_base);
goto err_irq_domain;
}
diff --git a/drivers/gpio/gpio-bd70528.c b/drivers/gpio/gpio-bd70528.c
deleted file mode 100644
index fd85605d2dab..000000000000
--- a/drivers/gpio/gpio-bd70528.c
+++ /dev/null
@@ -1,232 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2018 ROHM Semiconductors
-// gpio-bd70528.c ROHM BD70528MWV gpio driver
-
-#include <linux/gpio/driver.h>
-#include <linux/mfd/rohm-bd70528.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-
-#define GPIO_IN_REG(offset) (BD70528_REG_GPIO1_IN + (offset) * 2)
-#define GPIO_OUT_REG(offset) (BD70528_REG_GPIO1_OUT + (offset) * 2)
-
-struct bd70528_gpio {
- struct rohm_regmap_dev chip;
- struct gpio_chip gpio;
-};
-
-static int bd70528_set_debounce(struct bd70528_gpio *bdgpio,
- unsigned int offset, unsigned int debounce)
-{
- u8 val;
-
- switch (debounce) {
- case 0:
- val = BD70528_DEBOUNCE_DISABLE;
- break;
- case 1 ... 15:
- val = BD70528_DEBOUNCE_15MS;
- break;
- case 16 ... 30:
- val = BD70528_DEBOUNCE_30MS;
- break;
- case 31 ... 50:
- val = BD70528_DEBOUNCE_50MS;
- break;
- default:
- dev_err(bdgpio->chip.dev,
- "Invalid debouce value %u\n", debounce);
- return -EINVAL;
- }
- return regmap_update_bits(bdgpio->chip.regmap, GPIO_IN_REG(offset),
- BD70528_DEBOUNCE_MASK, val);
-}
-
-static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset)
-{
- struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
- int val, ret;
-
- /* Do we need to do something to IRQs here? */
- ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val);
- if (ret) {
- dev_err(bdgpio->chip.dev, "Could not read gpio direction\n");
- return ret;
- }
-
- return !(val & BD70528_GPIO_OUT_EN_MASK);
-}
-
-static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
- unsigned long config)
-{
- struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
-
- switch (pinconf_to_config_param(config)) {
- case PIN_CONFIG_DRIVE_OPEN_DRAIN:
- return regmap_update_bits(bdgpio->chip.regmap,
- GPIO_OUT_REG(offset),
- BD70528_GPIO_DRIVE_MASK,
- BD70528_GPIO_OPEN_DRAIN);
- break;
- case PIN_CONFIG_DRIVE_PUSH_PULL:
- return regmap_update_bits(bdgpio->chip.regmap,
- GPIO_OUT_REG(offset),
- BD70528_GPIO_DRIVE_MASK,
- BD70528_GPIO_PUSH_PULL);
- break;
- case PIN_CONFIG_INPUT_DEBOUNCE:
- return bd70528_set_debounce(bdgpio, offset,
- pinconf_to_config_argument(config));
- break;
- default:
- break;
- }
- return -ENOTSUPP;
-}
-
-static int bd70528_direction_input(struct gpio_chip *chip, unsigned int offset)
-{
- struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
-
- /* Do we need to do something to IRQs here? */
- return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
- BD70528_GPIO_OUT_EN_MASK,
- BD70528_GPIO_OUT_DISABLE);
-}
-
-static void bd70528_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
-{
- int ret;
- struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
- u8 val = (value) ? BD70528_GPIO_OUT_HI : BD70528_GPIO_OUT_LO;
-
- ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
- BD70528_GPIO_OUT_MASK, val);
- if (ret)
- dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value);
-}
-
-static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset,
- int value)
-{
- struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
-
- bd70528_gpio_set(chip, offset, value);
- return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
- BD70528_GPIO_OUT_EN_MASK,
- BD70528_GPIO_OUT_ENABLE);
-}
-
-#define GPIO_IN_STATE_MASK(offset) (BD70528_GPIO_IN_STATE_BASE << (offset))
-
-static int bd70528_gpio_get_o(struct bd70528_gpio *bdgpio, unsigned int offset)
-{
- int ret;
- unsigned int val;
-
- ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val);
- if (!ret)
- ret = !!(val & BD70528_GPIO_OUT_MASK);
- else
- dev_err(bdgpio->chip.dev, "GPIO (out) state read failed\n");
-
- return ret;
-}
-
-static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset)
-{
- unsigned int val;
- int ret;
-
- ret = regmap_read(bdgpio->chip.regmap, BD70528_REG_GPIO_STATE, &val);
-
- if (!ret)
- ret = !(val & GPIO_IN_STATE_MASK(offset));
- else
- dev_err(bdgpio->chip.dev, "GPIO (in) state read failed\n");
-
- return ret;
-}
-
-static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset)
-{
- int ret = -EINVAL;
- struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
-
- /*
- * There is a race condition where someone might be changing the
- * GPIO direction after we get it but before we read the value. But
- * application design where GPIO direction may be changed just when
- * we read GPIO value would be pointless as reader could not know
- * whether the returned high/low state is caused by input or output.
- * Or then there must be other ways to mitigate the issue. Thus
- * locking would make no sense.
- */
- ret = bd70528_get_direction(chip, offset);
- if (ret == 0)
- ret = bd70528_gpio_get_o(bdgpio, offset);
- else if (ret == 1)
- ret = bd70528_gpio_get_i(bdgpio, offset);
- else
- dev_err(bdgpio->chip.dev, "failed to read GPIO direction\n");
-
- return ret;
-}
-
-static int bd70528_probe(struct platform_device *pdev)
-{
- struct bd70528_gpio *bdgpio;
- struct rohm_regmap_dev *bd70528;
- int ret;
-
- bd70528 = dev_get_drvdata(pdev->dev.parent);
- if (!bd70528) {
- dev_err(&pdev->dev, "No MFD driver data\n");
- return -EINVAL;
- }
-
- bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio),
- GFP_KERNEL);
- if (!bdgpio)
- return -ENOMEM;
- bdgpio->chip.dev = &pdev->dev;
- bdgpio->gpio.parent = pdev->dev.parent;
- bdgpio->gpio.label = "bd70528-gpio";
- bdgpio->gpio.owner = THIS_MODULE;
- bdgpio->gpio.get_direction = bd70528_get_direction;
- bdgpio->gpio.direction_input = bd70528_direction_input;
- bdgpio->gpio.direction_output = bd70528_direction_output;
- bdgpio->gpio.set_config = bd70528_gpio_set_config;
- bdgpio->gpio.can_sleep = true;
- bdgpio->gpio.get = bd70528_gpio_get;
- bdgpio->gpio.set = bd70528_gpio_set;
- bdgpio->gpio.ngpio = 4;
- bdgpio->gpio.base = -1;
-#ifdef CONFIG_OF_GPIO
- bdgpio->gpio.of_node = pdev->dev.parent->of_node;
-#endif
- bdgpio->chip.regmap = bd70528->regmap;
-
- ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
- bdgpio);
- if (ret)
- dev_err(&pdev->dev, "gpio_init: Failed to add bd70528-gpio\n");
-
- return ret;
-}
-
-static struct platform_driver bd70528_gpio = {
- .driver = {
- .name = "bd70528-gpio"
- },
- .probe = bd70528_probe,
-};
-
-module_platform_driver(bd70528_gpio);
-
-MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_DESCRIPTION("BD70528 voltage regulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-bd71815.c b/drivers/gpio/gpio-bd71815.c
new file mode 100644
index 000000000000..08ff2857256f
--- /dev/null
+++ b/drivers/gpio/gpio-bd71815.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support to GPOs on ROHM BD71815
+ * Copyright 2021 ROHM Semiconductors.
+ * Author: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
+ *
+ * Copyright 2014 Embest Technology Co. Ltd. Inc.
+ * Author: yanglsh@embest-tech.com
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+/* For the BD71815 register definitions */
+#include <linux/mfd/rohm-bd71815.h>
+
+struct bd71815_gpio {
+ /* chip.parent points the MFD which provides DT node and regmap */
+ struct gpio_chip chip;
+ /* dev points to the platform device for devm and prints */
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+static int bd71815gpo_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct bd71815_gpio *bd71815 = gpiochip_get_data(chip);
+ int ret, val;
+
+ ret = regmap_read(bd71815->regmap, BD71815_REG_GPO, &val);
+ if (ret)
+ return ret;
+
+ return (val >> offset) & 1;
+}
+
+static void bd71815gpo_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct bd71815_gpio *bd71815 = gpiochip_get_data(chip);
+ int ret, bit;
+
+ bit = BIT(offset);
+
+ if (value)
+ ret = regmap_set_bits(bd71815->regmap, BD71815_REG_GPO, bit);
+ else
+ ret = regmap_clear_bits(bd71815->regmap, BD71815_REG_GPO, bit);
+
+ if (ret)
+ dev_warn(bd71815->dev, "failed to toggle GPO\n");
+}
+
+static int bd71815_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ struct bd71815_gpio *bdgpio = gpiochip_get_data(chip);
+
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ return regmap_update_bits(bdgpio->regmap,
+ BD71815_REG_GPO,
+ BD71815_GPIO_DRIVE_MASK << offset,
+ BD71815_GPIO_OPEN_DRAIN << offset);
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ return regmap_update_bits(bdgpio->regmap,
+ BD71815_REG_GPO,
+ BD71815_GPIO_DRIVE_MASK << offset,
+ BD71815_GPIO_CMOS << offset);
+ default:
+ break;
+ }
+ return -ENOTSUPP;
+}
+
+/* BD71815 GPIO is actually GPO */
+static int bd71815gpo_direction_get(struct gpio_chip *gc, unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+/* Template for GPIO chip */
+static const struct gpio_chip bd71815gpo_chip = {
+ .label = "bd71815",
+ .owner = THIS_MODULE,
+ .get = bd71815gpo_get,
+ .get_direction = bd71815gpo_direction_get,
+ .set = bd71815gpo_set,
+ .set_config = bd71815_gpio_set_config,
+ .can_sleep = true,
+};
+
+#define BD71815_TWO_GPIOS GENMASK(1, 0)
+#define BD71815_ONE_GPIO BIT(0)
+
+/*
+ * Sigh. The BD71815 and BD71817 were originally designed to support two GPO
+ * pins. At some point it was noticed the second GPO pin which is the E5 pin
+ * located at the center of IC is hard to use on PCB (due to the location). It
+ * was decided to not promote this second GPO and the pin is marked as GND in
+ * the datasheet. The functionality is still there though! I guess driving a GPO
+ * connected to the ground is a bad idea. Thus we do not support it by default.
+ * OTOH - the original driver written by colleagues at Embest did support
+ * controlling this second GPO. It is thus possible this is used in some of the
+ * products.
+ *
+ * This driver does not by default support configuring this second GPO
+ * but allows using it by providing the DT property
+ * "rohm,enable-hidden-gpo".
+ */
+static int bd71815_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ if (ngpios != 2)
+ return 0;
+
+ if (gc->parent && device_property_present(gc->parent,
+ "rohm,enable-hidden-gpo"))
+ *valid_mask = BD71815_TWO_GPIOS;
+ else
+ *valid_mask = BD71815_ONE_GPIO;
+
+ return 0;
+}
+
+static int gpo_bd71815_probe(struct platform_device *pdev)
+{
+ struct bd71815_gpio *g;
+ struct device *parent, *dev;
+
+ /*
+ * Bind devm lifetime to this platform device => use dev for devm.
+ * also the prints should originate from this device.
+ */
+ dev = &pdev->dev;
+ /* The device-tree and regmap come from MFD => use parent for that */
+ parent = dev->parent;
+
+ g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
+ if (!g)
+ return -ENOMEM;
+
+ g->chip = bd71815gpo_chip;
+
+ /*
+ * FIXME: As writing of this the sysfs interface for GPIO control does
+ * not respect the valid_mask. Do not trust it but rather set the ngpios
+ * to 1 if "rohm,enable-hidden-gpo" is not given.
+ *
+ * This check can be removed later if the sysfs export is fixed and
+ * if the fix is backported.
+ *
+ * For now it is safest to just set the ngpios though.
+ */
+ if (device_property_present(parent, "rohm,enable-hidden-gpo"))
+ g->chip.ngpio = 2;
+ else
+ g->chip.ngpio = 1;
+
+ g->chip.init_valid_mask = bd71815_init_valid_mask;
+ g->chip.base = -1;
+ g->chip.parent = parent;
+ g->regmap = dev_get_regmap(parent, NULL);
+ g->dev = dev;
+
+ return devm_gpiochip_add_data(dev, &g->chip, g);
+}
+
+static struct platform_driver gpo_bd71815_driver = {
+ .driver = {
+ .name = "bd71815-gpo",
+ },
+ .probe = gpo_bd71815_probe,
+};
+module_platform_driver(gpo_bd71815_driver);
+
+MODULE_ALIAS("platform:bd71815-gpo");
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_AUTHOR("Peter Yang <yanglsh@embest-tech.com>");
+MODULE_DESCRIPTION("GPO interface for BD71815");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-bd71828.c b/drivers/gpio/gpio-bd71828.c
new file mode 100644
index 000000000000..b2ccc320c7b5
--- /dev/null
+++ b/drivers/gpio/gpio-bd71828.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2018 ROHM Semiconductors
+
+#include <linux/gpio/driver.h>
+#include <linux/mfd/rohm-bd71828.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define GPIO_OUT_REG(off) (BD71828_REG_GPIO_CTRL1 + (off))
+#define HALL_GPIO_OFFSET 3
+
+struct bd71828_gpio {
+ struct regmap *regmap;
+ struct device *dev;
+ struct gpio_chip gpio;
+};
+
+static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ int ret;
+ struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
+ u8 val = (value) ? BD71828_GPIO_OUT_HI : BD71828_GPIO_OUT_LO;
+
+ /*
+ * The HALL input pin can only be used as input. If this is the pin
+ * we are dealing with - then we are done
+ */
+ if (offset == HALL_GPIO_OFFSET)
+ return;
+
+ ret = regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset),
+ BD71828_GPIO_OUT_MASK, val);
+ if (ret)
+ dev_err(bdgpio->dev, "Could not set gpio to %d\n", value);
+}
+
+static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ int ret;
+ unsigned int val;
+ struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
+
+ if (offset == HALL_GPIO_OFFSET)
+ ret = regmap_read(bdgpio->regmap, BD71828_REG_IO_STAT,
+ &val);
+ else
+ ret = regmap_read(bdgpio->regmap, GPIO_OUT_REG(offset),
+ &val);
+ if (!ret)
+ ret = (val & BD71828_GPIO_OUT_MASK);
+
+ return ret;
+}
+
+static int bd71828_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
+
+ if (offset == HALL_GPIO_OFFSET)
+ return -ENOTSUPP;
+
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ return regmap_update_bits(bdgpio->regmap,
+ GPIO_OUT_REG(offset),
+ BD71828_GPIO_DRIVE_MASK,
+ BD71828_GPIO_OPEN_DRAIN);
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ return regmap_update_bits(bdgpio->regmap,
+ GPIO_OUT_REG(offset),
+ BD71828_GPIO_DRIVE_MASK,
+ BD71828_GPIO_PUSH_PULL);
+ default:
+ break;
+ }
+ return -ENOTSUPP;
+}
+
+static int bd71828_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ /*
+ * Pin usage is selected by OTP data. We can't read it runtime. Hence
+ * we trust that if the pin is not excluded by "gpio-reserved-ranges"
+ * the OTP configuration is set to OUT. (Other pins but HALL input pin
+ * on BD71828 can't really be used for general purpose input - input
+ * states are used for specific cases like regulator control or
+ * PMIC_ON_REQ.
+ */
+ if (offset == HALL_GPIO_OFFSET)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int bd71828_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bd71828_gpio *bdgpio;
+
+ bdgpio = devm_kzalloc(dev, sizeof(*bdgpio), GFP_KERNEL);
+ if (!bdgpio)
+ return -ENOMEM;
+
+ bdgpio->dev = dev;
+ bdgpio->gpio.parent = dev->parent;
+ bdgpio->gpio.label = "bd71828-gpio";
+ bdgpio->gpio.owner = THIS_MODULE;
+ bdgpio->gpio.get_direction = bd71828_get_direction;
+ bdgpio->gpio.set_config = bd71828_gpio_set_config;
+ bdgpio->gpio.can_sleep = true;
+ bdgpio->gpio.get = bd71828_gpio_get;
+ bdgpio->gpio.set = bd71828_gpio_set;
+ bdgpio->gpio.base = -1;
+
+ /*
+ * See if we need some implementation to mark some PINs as
+ * not controllable based on DT info or if core can handle
+ * "gpio-reserved-ranges" and exclude them from control
+ */
+ bdgpio->gpio.ngpio = 4;
+ bdgpio->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!bdgpio->regmap)
+ return -ENODEV;
+
+ return devm_gpiochip_add_data(dev, &bdgpio->gpio, bdgpio);
+}
+
+static struct platform_driver bd71828_gpio = {
+ .driver = {
+ .name = "bd71828-gpio"
+ },
+ .probe = bd71828_probe,
+};
+
+module_platform_driver(bd71828_gpio);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD71828 voltage regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bd71828-gpio");
diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c
index 5224a946e8ab..9a4d55f703bb 100644
--- a/drivers/gpio/gpio-bd9571mwv.c
+++ b/drivers/gpio/gpio-bd9571mwv.c
@@ -1,31 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * ROHM BD9571MWV-M GPIO driver
+ * ROHM BD9571MWV-M and BD9574MWF-M GPIO driver
*
* Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether expressed or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License version 2 for more details.
- *
* Based on the TPS65086 driver
*
* NOTE: Interrupts are not supported yet.
*/
#include <linux/gpio/driver.h>
+#include <linux/mfd/rohm-generic.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/bd9571mwv.h>
struct bd9571mwv_gpio {
+ struct regmap *regmap;
struct gpio_chip chip;
- struct bd9571mwv *bd;
};
static int bd9571mwv_gpio_get_direction(struct gpio_chip *chip,
@@ -34,11 +27,13 @@ static int bd9571mwv_gpio_get_direction(struct gpio_chip *chip,
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
int ret, val;
- ret = regmap_read(gpio->bd->regmap, BD9571MWV_GPIO_DIR, &val);
+ ret = regmap_read(gpio->regmap, BD9571MWV_GPIO_DIR, &val);
if (ret < 0)
return ret;
+ if (val & BIT(offset))
+ return GPIO_LINE_DIRECTION_IN;
- return val & BIT(offset);
+ return GPIO_LINE_DIRECTION_OUT;
}
static int bd9571mwv_gpio_direction_input(struct gpio_chip *chip,
@@ -46,8 +41,7 @@ static int bd9571mwv_gpio_direction_input(struct gpio_chip *chip,
{
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
- regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_DIR,
- BIT(offset), 0);
+ regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_DIR, BIT(offset), 0);
return 0;
}
@@ -58,9 +52,9 @@ static int bd9571mwv_gpio_direction_output(struct gpio_chip *chip,
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
/* Set the initial value */
- regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_OUT,
+ regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_OUT,
BIT(offset), value ? BIT(offset) : 0);
- regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_DIR,
+ regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_DIR,
BIT(offset), BIT(offset));
return 0;
@@ -71,7 +65,7 @@ static int bd9571mwv_gpio_get(struct gpio_chip *chip, unsigned int offset)
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
int ret, val;
- ret = regmap_read(gpio->bd->regmap, BD9571MWV_GPIO_IN, &val);
+ ret = regmap_read(gpio->regmap, BD9571MWV_GPIO_IN, &val);
if (ret < 0)
return ret;
@@ -83,7 +77,7 @@ static void bd9571mwv_gpio_set(struct gpio_chip *chip, unsigned int offset,
{
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
- regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_OUT,
+ regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_OUT,
BIT(offset), value ? BIT(offset) : 0);
}
@@ -103,29 +97,21 @@ static const struct gpio_chip template_chip = {
static int bd9571mwv_gpio_probe(struct platform_device *pdev)
{
struct bd9571mwv_gpio *gpio;
- int ret;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
- platform_set_drvdata(pdev, gpio);
-
- gpio->bd = dev_get_drvdata(pdev->dev.parent);
+ gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
gpio->chip = template_chip;
- gpio->chip.parent = gpio->bd->dev;
+ gpio->chip.parent = pdev->dev.parent;
- ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
}
static const struct platform_device_id bd9571mwv_gpio_id_table[] = {
- { "bd9571mwv-gpio", },
+ { "bd9571mwv-gpio", ROHM_CHIP_TYPE_BD9571 },
+ { "bd9574mwf-gpio", ROHM_CHIP_TYPE_BD9574 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, bd9571mwv_gpio_id_table);
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index af936dcca659..6b7439b44690 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -92,9 +92,9 @@ brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank)
unsigned long status;
unsigned long flags;
- spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
status = __brcmstb_gpio_get_active_irqs(bank);
- spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
return status;
}
@@ -114,14 +114,14 @@ static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
u32 imask;
unsigned long flags;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
imask = gc->read_reg(priv->reg_base + GIO_MASK(bank->id));
if (enable)
imask |= mask;
else
imask &= ~mask;
gc->write_reg(priv->reg_base + GIO_MASK(bank->id), imask);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
@@ -204,7 +204,7 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
iedge_config = bank->gc.read_reg(priv->reg_base +
GIO_EC(bank->id)) & ~mask;
@@ -220,7 +220,7 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type)
bank->gc.write_reg(priv->reg_base + GIO_LEVEL(bank->id),
ilevel | level);
- spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
return 0;
}
@@ -277,15 +277,14 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank)
unsigned long status;
while ((status = brcmstb_gpio_get_active_irqs(bank))) {
- unsigned int irq, offset;
+ unsigned int offset;
for_each_set_bit(offset, &status, 32) {
if (offset >= bank->width)
dev_warn(&priv->pdev->dev,
"IRQ for invalid GPIO (bank=%d, offset=%d)\n",
bank->id, offset);
- irq = irq_linear_revmap(domain, hwbase + offset);
- generic_handle_irq(irq);
+ generic_handle_domain_irq(domain, hwbase + offset);
}
}
}
@@ -636,10 +635,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
if (of_property_read_bool(np, "interrupt-controller")) {
priv->parent_irq = platform_get_irq(pdev, 0);
- if (priv->parent_irq <= 0) {
- dev_err(dev, "Couldn't get IRQ");
+ if (priv->parent_irq <= 0)
return -ENOENT;
- }
} else {
priv->parent_irq = -ENOENT;
}
@@ -706,9 +703,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
goto fail;
}
- gc->of_node = np;
gc->owner = THIS_MODULE;
- gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", dev->of_node);
+ gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np);
if (!gc->label) {
err = -ENOMEM;
goto fail;
@@ -718,6 +714,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
gc->of_xlate = brcmstb_gpio_of_xlate;
/* not all ngpio lines are valid, will use bank width later */
gc->ngpio = MAX_GPIO_PER_BANK;
+ gc->offset = bank->id * MAX_GPIO_PER_BANK;
if (priv->parent_irq > 0)
gc->to_irq = brcmstb_gpio_to_irq;
diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c
index a6f30ad6750f..7920cf256798 100644
--- a/drivers/gpio/gpio-bt8xx.c
+++ b/drivers/gpio/gpio-bt8xx.c
@@ -175,13 +175,13 @@ static int bt8xxgpio_probe(struct pci_dev *dev,
err = pci_enable_device(dev);
if (err) {
- printk(KERN_ERR "bt8xxgpio: Can't enable device.\n");
+ dev_err(&dev->dev, "can't enable device.\n");
return err;
}
if (!devm_request_mem_region(&dev->dev, pci_resource_start(dev, 0),
pci_resource_len(dev, 0),
"bt8xxgpio")) {
- printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n",
+ dev_warn(&dev->dev, "can't request iomem (0x%llx).\n",
(unsigned long long)pci_resource_start(dev, 0));
err = -EBUSY;
goto err_disable;
@@ -191,7 +191,7 @@ static int bt8xxgpio_probe(struct pci_dev *dev,
bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000);
if (!bg->mmio) {
- printk(KERN_ERR "bt8xxgpio: ioremap() failed\n");
+ dev_err(&dev->dev, "ioremap() failed\n");
err = -EIO;
goto err_disable;
}
@@ -207,7 +207,7 @@ static int bt8xxgpio_probe(struct pci_dev *dev,
bt8xxgpio_gpio_setup(bg);
err = gpiochip_add_data(&bg->gpio, bg);
if (err) {
- printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n");
+ dev_err(&dev->dev, "failed to register GPIOs\n");
goto err_disable;
}
diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c
index 712ae212b0b4..137aea49ba02 100644
--- a/drivers/gpio/gpio-cadence.c
+++ b/drivers/gpio/gpio-cadence.c
@@ -41,12 +41,12 @@ static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset)
struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
unsigned long flags;
- spin_lock_irqsave(&chip->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&chip->bgpio_lock, flags);
iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) & ~BIT(offset),
cgpio->regs + CDNS_GPIO_BYPASS_MODE);
- spin_unlock_irqrestore(&chip->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);
return 0;
}
@@ -55,13 +55,13 @@ static void cdns_gpio_free(struct gpio_chip *chip, unsigned int offset)
struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
unsigned long flags;
- spin_lock_irqsave(&chip->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&chip->bgpio_lock, flags);
iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) |
(BIT(offset) & cgpio->bypass_orig),
cgpio->regs + CDNS_GPIO_BYPASS_MODE);
- spin_unlock_irqrestore(&chip->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);
}
static void cdns_gpio_irq_mask(struct irq_data *d)
@@ -90,7 +90,7 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)
u32 mask = BIT(d->hwirq);
int ret = 0;
- spin_lock_irqsave(&chip->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&chip->bgpio_lock, flags);
int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask;
int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask;
@@ -115,7 +115,7 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)
iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE);
err_irq_type:
- spin_unlock_irqrestore(&chip->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);
return ret;
}
@@ -133,7 +133,7 @@ static void cdns_gpio_irq_handler(struct irq_desc *desc)
~ioread32(cgpio->regs + CDNS_GPIO_IRQ_MASK);
for_each_set_bit(hwirq, &status, chip->ngpio)
- generic_handle_irq(irq_find_mapping(chip->irq.domain, hwirq));
+ generic_handle_domain_irq(chip->irq.domain, hwirq);
chained_irq_exit(irqchip, desc);
}
@@ -214,27 +214,33 @@ static int cdns_gpio_probe(struct platform_device *pdev)
goto err_revert_dir;
}
- ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- goto err_disable_clk;
- }
-
/*
- * irq_chip support
+ * Optional irq_chip support
*/
irq = platform_get_irq(pdev, 0);
if (irq >= 0) {
- ret = gpiochip_irqchip_add(&cgpio->gc, &cdns_gpio_irqchip,
- 0, handle_level_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(&pdev->dev, "Could not add irqchip, %d\n",
- ret);
+ struct gpio_irq_chip *girq;
+
+ girq = &cgpio->gc.irq;
+ girq->chip = &cdns_gpio_irqchip;
+ girq->parent_handler = cdns_gpio_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents) {
+ ret = -ENOMEM;
goto err_disable_clk;
}
- gpiochip_set_chained_irqchip(&cgpio->gc, &cdns_gpio_irqchip,
- irq, cdns_gpio_irq_handler);
+ girq->parents[0] = irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
+ }
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ goto err_disable_clk;
}
cgpio->bypass_orig = ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE);
@@ -272,6 +278,7 @@ static const struct of_device_id cdns_of_ids[] = {
{ .compatible = "cdns,gpio-r1p02" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, cdns_of_ids);
static struct platform_driver cdns_gpio_driver = {
.driver = {
diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c
index 8cbc94d0d424..789384c6e178 100644
--- a/drivers/gpio/gpio-creg-snps.c
+++ b/drivers/gpio/gpio-creg-snps.c
@@ -64,11 +64,11 @@ static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg,
if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8)
return -EINVAL;
- /* Check that on valiue fits it's placeholder */
+ /* Check that on value fits its placeholder */
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i])
return -EINVAL;
- /* Check that off valiue fits it's placeholder */
+ /* Check that off value fits its placeholder */
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i])
return -EINVAL;
@@ -137,7 +137,6 @@ static int creg_gpio_probe(struct platform_device *pdev)
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct creg_gpio *hcg;
- struct resource *mem;
u32 ngpios;
int ret;
@@ -145,8 +144,7 @@ static int creg_gpio_probe(struct platform_device *pdev)
if (!hcg)
return -ENOMEM;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcg->regs = devm_ioremap_resource(dev, mem);
+ hcg->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hcg->regs))
return PTR_ERR(hcg->regs);
@@ -165,12 +163,12 @@ static int creg_gpio_probe(struct platform_device *pdev)
spin_lock_init(&hcg->lock);
+ hcg->gc.parent = dev;
hcg->gc.label = dev_name(dev);
hcg->gc.base = -1;
hcg->gc.ngpio = ngpios;
hcg->gc.set = creg_gpio_set;
hcg->gc.direction_output = creg_gpio_dir_out;
- hcg->gc.of_node = dev->of_node;
ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg);
if (ret)
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c
index 14d1f4c933b6..1ee62cd58582 100644
--- a/drivers/gpio/gpio-crystalcove.c
+++ b/drivers/gpio/gpio-crystalcove.c
@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>
+#include <linux/types.h>
#define CRYSTALCOVE_GPIO_NUM 16
#define CRYSTALCOVE_VGPIO_NUM 95
@@ -110,8 +111,7 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type)
return reg + gpio % 8;
}
-static void crystalcove_update_irq_mask(struct crystalcove_gpio *cg,
- int gpio)
+static void crystalcove_update_irq_mask(struct crystalcove_gpio *cg, int gpio)
{
u8 mirqs0 = gpio < 8 ? MGPIO0IRQS0 : MGPIO1IRQS0;
int mask = BIT(gpio % 8);
@@ -129,7 +129,7 @@ static void crystalcove_update_irq_ctrl(struct crystalcove_gpio *cg, int gpio)
regmap_update_bits(cg->regmap, reg, CTLI_INTCNT_BE, cg->intcnt_value);
}
-static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
+static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
int reg = to_reg(gpio, CTRL_OUT);
@@ -140,8 +140,7 @@ static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
return regmap_write(cg->regmap, reg, CTLO_INPUT_SET);
}
-static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio,
- int value)
+static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, int value)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
int reg = to_reg(gpio, CTRL_OUT);
@@ -152,7 +151,7 @@ static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio,
return regmap_write(cg->regmap, reg, CTLO_OUTPUT_SET | value);
}
-static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio)
+static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned int gpio)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
unsigned int val;
@@ -168,8 +167,7 @@ static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio)
return val & 0x1;
}
-static void crystalcove_gpio_set(struct gpio_chip *chip,
- unsigned gpio, int value)
+static void crystalcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
int reg = to_reg(gpio, CTRL_OUT);
@@ -183,12 +181,12 @@ static void crystalcove_gpio_set(struct gpio_chip *chip,
regmap_update_bits(cg->regmap, reg, 1, 0);
}
-static int crystalcove_irq_type(struct irq_data *data, unsigned type)
+static int crystalcove_irq_type(struct irq_data *data, unsigned int type)
{
- struct crystalcove_gpio *cg =
- gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ struct crystalcove_gpio *cg = gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
- if (data->hwirq >= CRYSTALCOVE_GPIO_NUM)
+ if (hwirq >= CRYSTALCOVE_GPIO_NUM)
return 0;
switch (type) {
@@ -215,22 +213,20 @@ static int crystalcove_irq_type(struct irq_data *data, unsigned type)
static void crystalcove_bus_lock(struct irq_data *data)
{
- struct crystalcove_gpio *cg =
- gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ struct crystalcove_gpio *cg = gpiochip_get_data(irq_data_get_irq_chip_data(data));
mutex_lock(&cg->buslock);
}
static void crystalcove_bus_sync_unlock(struct irq_data *data)
{
- struct crystalcove_gpio *cg =
- gpiochip_get_data(irq_data_get_irq_chip_data(data));
- int gpio = data->hwirq;
+ struct crystalcove_gpio *cg = gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
if (cg->update & UPDATE_IRQ_TYPE)
- crystalcove_update_irq_ctrl(cg, gpio);
+ crystalcove_update_irq_ctrl(cg, hwirq);
if (cg->update & UPDATE_IRQ_MASK)
- crystalcove_update_irq_mask(cg, gpio);
+ crystalcove_update_irq_mask(cg, hwirq);
cg->update = 0;
mutex_unlock(&cg->buslock);
@@ -238,34 +234,43 @@ static void crystalcove_bus_sync_unlock(struct irq_data *data)
static void crystalcove_irq_unmask(struct irq_data *data)
{
- struct crystalcove_gpio *cg =
- gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ struct crystalcove_gpio *cg = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
- if (data->hwirq < CRYSTALCOVE_GPIO_NUM) {
- cg->set_irq_mask = false;
- cg->update |= UPDATE_IRQ_MASK;
- }
+ if (hwirq >= CRYSTALCOVE_GPIO_NUM)
+ return;
+
+ gpiochip_enable_irq(gc, hwirq);
+
+ cg->set_irq_mask = false;
+ cg->update |= UPDATE_IRQ_MASK;
}
static void crystalcove_irq_mask(struct irq_data *data)
{
- struct crystalcove_gpio *cg =
- gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ struct crystalcove_gpio *cg = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
- if (data->hwirq < CRYSTALCOVE_GPIO_NUM) {
- cg->set_irq_mask = true;
- cg->update |= UPDATE_IRQ_MASK;
- }
+ if (hwirq >= CRYSTALCOVE_GPIO_NUM)
+ return;
+
+ cg->set_irq_mask = true;
+ cg->update |= UPDATE_IRQ_MASK;
+
+ gpiochip_disable_irq(gc, hwirq);
}
-static struct irq_chip crystalcove_irqchip = {
+static const struct irq_chip crystalcove_irqchip = {
.name = "Crystal Cove",
.irq_mask = crystalcove_irq_mask,
.irq_unmask = crystalcove_irq_unmask,
.irq_set_type = crystalcove_irq_type,
.irq_bus_lock = crystalcove_bus_lock,
.irq_bus_sync_unlock = crystalcove_bus_sync_unlock,
- .flags = IRQCHIP_SKIP_SET_WAKE,
+ .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
@@ -293,8 +298,7 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static void crystalcove_gpio_dbg_show(struct seq_file *s,
- struct gpio_chip *chip)
+static void crystalcove_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
int gpio, offset;
@@ -330,6 +334,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
int retval;
struct device *dev = pdev->dev.parent;
struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+ struct gpio_irq_chip *girq;
if (irq < 0)
return irq;
@@ -338,8 +343,6 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
if (!cg)
return -ENOMEM;
- platform_set_drvdata(pdev, cg);
-
mutex_init(&cg->buslock);
cg->chip.label = KBUILD_MODNAME;
cg->chip.direction_input = crystalcove_gpio_dir_in;
@@ -353,46 +356,40 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
cg->chip.dbg_show = crystalcove_gpio_dbg_show;
cg->regmap = pmic->regmap;
- retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg);
- if (retval) {
- dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval);
- return retval;
- }
-
- gpiochip_irqchip_add_nested(&cg->chip, &crystalcove_irqchip, 0,
- handle_simple_irq, IRQ_TYPE_NONE);
-
- retval = request_threaded_irq(irq, NULL, crystalcove_gpio_irq_handler,
- IRQF_ONESHOT, KBUILD_MODNAME, cg);
-
+ girq = &cg->chip.irq;
+ gpio_irq_chip_set_chip(girq, &crystalcove_irqchip);
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->threaded = true;
+
+ retval = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ crystalcove_gpio_irq_handler,
+ IRQF_ONESHOT, KBUILD_MODNAME, cg);
if (retval) {
dev_warn(&pdev->dev, "request irq failed: %d\n", retval);
return retval;
}
- gpiochip_set_nested_irqchip(&cg->chip, &crystalcove_irqchip, irq);
-
- return 0;
-}
+ retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg);
+ if (retval)
+ return retval;
-static int crystalcove_gpio_remove(struct platform_device *pdev)
-{
- struct crystalcove_gpio *cg = platform_get_drvdata(pdev);
- int irq = platform_get_irq(pdev, 0);
+ /* Distuingish IRQ domain from others sharing (MFD) the same fwnode */
+ irq_domain_update_bus_token(cg->chip.irq.domain, DOMAIN_BUS_WIRED);
- if (irq >= 0)
- free_irq(irq, cg);
return 0;
}
static struct platform_driver crystalcove_gpio_driver = {
.probe = crystalcove_gpio_probe,
- .remove = crystalcove_gpio_remove,
.driver = {
.name = "crystal_cove_gpio",
},
};
-
module_platform_driver(crystalcove_gpio_driver);
MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>");
diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c
index 53b24e3ae7de..6da3a247614a 100644
--- a/drivers/gpio/gpio-cs5535.c
+++ b/drivers/gpio/gpio-cs5535.c
@@ -345,12 +345,8 @@ static int cs5535_gpio_probe(struct platform_device *pdev)
mask_orig, mask);
/* finally, register with the generic GPIO API */
- err = devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip,
- &cs5535_gpio_chip);
- if (err)
- return err;
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip,
+ &cs5535_gpio_chip);
}
static struct platform_driver cs5535_gpio_driver = {
diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c
index 9aa59afdcbbf..559188d80c2b 100644
--- a/drivers/gpio/gpio-da9052.c
+++ b/drivers/gpio/gpio-da9052.c
@@ -196,7 +196,6 @@ static int da9052_gpio_probe(struct platform_device *pdev)
{
struct da9052_gpio *gpio;
struct da9052_pdata *pdata;
- int ret;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
@@ -209,15 +208,7 @@ static int da9052_gpio_probe(struct platform_device *pdev)
if (pdata && pdata->gpio_base)
gpio->gp.base = pdata->gpio_base;
- ret = devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, gpio);
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
}
static struct platform_driver da9052_gpio_driver = {
diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c
index 6ad0c37b862e..49446a030f10 100644
--- a/drivers/gpio/gpio-da9055.c
+++ b/drivers/gpio/gpio-da9055.c
@@ -133,7 +133,6 @@ static int da9055_gpio_probe(struct platform_device *pdev)
{
struct da9055_gpio *gpio;
struct da9055_pdata *pdata;
- int ret;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
@@ -146,15 +145,7 @@ static int da9055_gpio_probe(struct platform_device *pdev)
if (pdata && pdata->gpio_base)
gpio->gp.base = pdata->gpio_base;
- ret = devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, gpio);
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
}
static struct platform_driver da9055_gpio_driver = {
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index e0b025689625..f960587f86a3 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -237,12 +237,8 @@ static int davinci_gpio_probe(struct platform_device *pdev)
for (i = 0; i < nirq; i++) {
chips->irqs[i] = platform_get_irq(pdev, i);
- if (chips->irqs[i] < 0) {
- if (chips->irqs[i] != -EPROBE_DEFER)
- dev_info(dev, "IRQ not populated, err = %d\n",
- chips->irqs[i]);
- return chips->irqs[i];
- }
+ if (chips->irqs[i] < 0)
+ return dev_err_probe(dev, chips->irqs[i], "IRQ not populated\n");
}
chips->chip.label = dev_name(dev);
@@ -258,12 +254,8 @@ static int davinci_gpio_probe(struct platform_device *pdev)
#ifdef CONFIG_OF_GPIO
chips->chip.of_gpio_n_cells = 2;
chips->chip.parent = dev;
- chips->chip.of_node = dev->of_node;
-
- if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
- chips->chip.request = gpiochip_generic_request;
- chips->chip.free = gpiochip_generic_free;
- }
+ chips->chip.request = gpiochip_generic_request;
+ chips->chip.free = gpiochip_generic_free;
#endif
spin_lock_init(&chips->lock);
@@ -376,8 +368,7 @@ static void gpio_irq_handler(struct irq_desc *desc)
*/
hw_irq = (bank_num / 2) * 32 + bit;
- generic_handle_irq(
- irq_find_mapping(d->irq_domain, hw_irq));
+ generic_handle_domain_irq(d->irq_domain, hw_irq);
}
}
chained_irq_exit(irq_desc_get_chip(desc), desc);
diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c
index 8a33c2fc174d..71fa437b491f 100644
--- a/drivers/gpio/gpio-dln2.c
+++ b/drivers/gpio/gpio-dln2.c
@@ -200,9 +200,9 @@ static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
struct dln2_gpio *dln2 = gpiochip_get_data(chip);
if (test_bit(offset, dln2->output_enabled))
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
}
static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)
@@ -214,7 +214,7 @@ static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)
if (dir < 0)
return dir;
- if (dir == 1)
+ if (dir == GPIO_LINE_DIRECTION_IN)
return dln2_gpio_pin_get_in_val(dln2, offset);
return dln2_gpio_pin_get_out_val(dln2, offset);
@@ -305,6 +305,7 @@ static void dln2_irq_unmask(struct irq_data *irqd)
struct dln2_gpio *dln2 = gpiochip_get_data(gc);
int pin = irqd_to_hwirq(irqd);
+ gpiochip_enable_irq(gc, pin);
set_bit(pin, dln2->unmasked_irqs);
}
@@ -315,6 +316,7 @@ static void dln2_irq_mask(struct irq_data *irqd)
int pin = irqd_to_hwirq(irqd);
clear_bit(pin, dln2->unmasked_irqs);
+ gpiochip_disable_irq(gc, pin);
}
static int dln2_irq_set_type(struct irq_data *irqd, unsigned type)
@@ -383,19 +385,21 @@ static void dln2_irq_bus_unlock(struct irq_data *irqd)
mutex_unlock(&dln2->irq_lock);
}
-static struct irq_chip dln2_gpio_irqchip = {
+static const struct irq_chip dln2_irqchip = {
.name = "dln2-irq",
.irq_mask = dln2_irq_mask,
.irq_unmask = dln2_irq_unmask,
.irq_set_type = dln2_irq_set_type,
.irq_bus_lock = dln2_irq_bus_lock,
.irq_bus_sync_unlock = dln2_irq_bus_unlock,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void dln2_gpio_event(struct platform_device *pdev, u16 echo,
const void *data, int len)
{
- int pin, irq;
+ int pin, ret;
const struct {
__le16 count;
@@ -416,30 +420,27 @@ static void dln2_gpio_event(struct platform_device *pdev, u16 echo,
return;
}
- irq = irq_find_mapping(dln2->gpio.irq.domain, pin);
- if (!irq) {
- dev_err(dln2->gpio.parent, "pin %d not mapped to IRQ\n", pin);
- return;
- }
-
switch (dln2->irq_type[pin]) {
case DLN2_GPIO_EVENT_CHANGE_RISING:
- if (event->value)
- generic_handle_irq(irq);
+ if (!event->value)
+ return;
break;
case DLN2_GPIO_EVENT_CHANGE_FALLING:
- if (!event->value)
- generic_handle_irq(irq);
+ if (event->value)
+ return;
break;
- default:
- generic_handle_irq(irq);
}
+
+ ret = generic_handle_domain_irq(dln2->gpio.irq.domain, pin);
+ if (unlikely(ret))
+ dev_err(dln2->gpio.parent, "pin %d not mapped to IRQ\n", pin);
}
static int dln2_gpio_probe(struct platform_device *pdev)
{
struct dln2_gpio *dln2;
struct device *dev = &pdev->dev;
+ struct gpio_irq_chip *girq;
int pins;
int ret;
@@ -476,6 +477,15 @@ static int dln2_gpio_probe(struct platform_device *pdev)
dln2->gpio.direction_output = dln2_gpio_direction_output;
dln2->gpio.set_config = dln2_gpio_set_config;
+ girq = &dln2->gpio.irq;
+ gpio_irq_chip_set_chip(girq, &dln2_irqchip);
+ /* The event comes from the outside so no parent handler */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+
platform_set_drvdata(pdev, dln2);
ret = devm_gpiochip_add_data(dev, &dln2->gpio, dln2);
@@ -484,13 +494,6 @@ static int dln2_gpio_probe(struct platform_device *pdev)
return ret;
}
- ret = gpiochip_irqchip_add(&dln2->gpio, &dln2_gpio_irqchip, 0,
- handle_simple_irq, IRQ_TYPE_NONE);
- if (ret < 0) {
- dev_err(dev, "failed to add irq chip: %d\n", ret);
- return ret;
- }
-
ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV,
dln2_gpio_event);
if (ret) {
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index 3108be5e208c..c22fcaa44a61 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -13,20 +13,16 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
-#include <linux/irqdomain.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/reset.h>
-#include <linux/spinlock.h>
-#include <linux/platform_data/gpio-dwapb.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include "gpiolib.h"
+#include "gpiolib-acpi.h"
#define GPIO_SWPORTA_DR 0x00
#define GPIO_SWPORTA_DDR 0x04
@@ -48,12 +44,17 @@
#define GPIO_EXT_PORTC 0x58
#define GPIO_EXT_PORTD 0x5c
+#define DWAPB_DRIVER_NAME "gpio-dwapb"
#define DWAPB_MAX_PORTS 4
+#define DWAPB_MAX_GPIOS 32
+
#define GPIO_EXT_PORT_STRIDE 0x04 /* register stride 32 bits */
#define GPIO_SWPORT_DR_STRIDE 0x0c /* register stride 3*32 bits */
#define GPIO_SWPORT_DDR_STRIDE 0x0c /* register stride 3*32 bits */
+#define GPIO_REG_OFFSET_V1 0
#define GPIO_REG_OFFSET_V2 1
+#define GPIO_REG_OFFSET_MASK BIT(0)
#define GPIO_INTMASK_V2 0x44
#define GPIO_INTTYPE_LEVEL_V2 0x34
@@ -61,8 +62,23 @@
#define GPIO_INTSTATUS_V2 0x3c
#define GPIO_PORTA_EOI_V2 0x40
+#define DWAPB_NR_CLOCKS 2
+
struct dwapb_gpio;
+struct dwapb_port_property {
+ struct fwnode_handle *fwnode;
+ unsigned int idx;
+ unsigned int ngpio;
+ unsigned int gpio_base;
+ int irq[DWAPB_MAX_GPIOS];
+};
+
+struct dwapb_platform_data {
+ struct dwapb_port_property *properties;
+ unsigned int nports;
+};
+
#ifdef CONFIG_PM_SLEEP
/* Store GPIO context across system-wide suspend/resume transitions */
struct dwapb_context {
@@ -78,25 +94,31 @@ struct dwapb_context {
};
#endif
+struct dwapb_gpio_port_irqchip {
+ unsigned int nr_irqs;
+ unsigned int irq[DWAPB_MAX_GPIOS];
+};
+
struct dwapb_gpio_port {
struct gpio_chip gc;
- bool is_registered;
+ struct dwapb_gpio_port_irqchip *pirq;
struct dwapb_gpio *gpio;
#ifdef CONFIG_PM_SLEEP
struct dwapb_context *ctx;
#endif
unsigned int idx;
};
+#define to_dwapb_gpio(_gc) \
+ (container_of(_gc, struct dwapb_gpio_port, gc)->gpio)
struct dwapb_gpio {
struct device *dev;
void __iomem *regs;
struct dwapb_gpio_port *ports;
unsigned int nr_ports;
- struct irq_domain *domain;
unsigned int flags;
struct reset_control *rst;
- struct clk *clk;
+ struct clk_bulk_data clks[DWAPB_NR_CLOCKS];
};
static inline u32 gpio_reg_v2_convert(unsigned int offset)
@@ -119,7 +141,7 @@ static inline u32 gpio_reg_v2_convert(unsigned int offset)
static inline u32 gpio_reg_convert(struct dwapb_gpio *gpio, unsigned int offset)
{
- if (gpio->flags & GPIO_REG_OFFSET_V2)
+ if ((gpio->flags & GPIO_REG_OFFSET_MASK) == GPIO_REG_OFFSET_V2)
return gpio_reg_v2_convert(offset);
return offset;
@@ -142,14 +164,6 @@ static inline void dwapb_write(struct dwapb_gpio *gpio, unsigned int offset,
gc->write_reg(reg_base + gpio_reg_convert(gpio, offset), val);
}
-static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
-{
- struct dwapb_gpio_port *port = gpiochip_get_data(gc);
- struct dwapb_gpio *gpio = port->gpio;
-
- return irq_find_mapping(gpio->domain, offset);
-}
-
static struct dwapb_gpio_port *dwapb_offs_to_port(struct dwapb_gpio *gpio, unsigned int offs)
{
struct dwapb_gpio_port *port;
@@ -157,7 +171,7 @@ static struct dwapb_gpio_port *dwapb_offs_to_port(struct dwapb_gpio *gpio, unsig
for (i = 0; i < gpio->nr_ports; i++) {
port = &gpio->ports[i];
- if (port->idx == offs / 32)
+ if (port->idx == offs / DWAPB_MAX_GPIOS)
return port;
}
@@ -177,7 +191,7 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
pol = dwapb_read(gpio, GPIO_INT_POLARITY);
/* Just read the current value right out of the data register */
- val = gc->get(gc, offs % 32);
+ val = gc->get(gc, offs % DWAPB_MAX_GPIOS);
if (val)
pol &= ~BIT(offs);
else
@@ -188,22 +202,22 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
{
- u32 irq_status = dwapb_read(gpio, GPIO_INTSTATUS);
- u32 ret = irq_status;
+ struct gpio_chip *gc = &gpio->ports[0].gc;
+ unsigned long irq_status;
+ irq_hw_number_t hwirq;
- while (irq_status) {
- int hwirq = fls(irq_status) - 1;
- int gpio_irq = irq_find_mapping(gpio->domain, hwirq);
+ irq_status = dwapb_read(gpio, GPIO_INTSTATUS);
+ for_each_set_bit(hwirq, &irq_status, DWAPB_MAX_GPIOS) {
+ int gpio_irq = irq_find_mapping(gc->irq.domain, hwirq);
+ u32 irq_type = irq_get_trigger_type(gpio_irq);
generic_handle_irq(gpio_irq);
- irq_status &= ~BIT(hwirq);
- if ((irq_get_trigger_type(gpio_irq) & IRQ_TYPE_SENSE_MASK)
- == IRQ_TYPE_EDGE_BOTH)
+ if ((irq_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
dwapb_toggle_trigger(gpio, hwirq);
}
- return ret;
+ return irq_status;
}
static void dwapb_irq_handler(struct irq_desc *desc)
@@ -211,80 +225,96 @@ static void dwapb_irq_handler(struct irq_desc *desc)
struct dwapb_gpio *gpio = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
+ chained_irq_enter(chip, desc);
dwapb_do_irq(gpio);
+ chained_irq_exit(chip, desc);
+}
- if (chip->irq_eoi)
- chip->irq_eoi(irq_desc_get_irq_data(desc));
+static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id)
+{
+ return IRQ_RETVAL(dwapb_do_irq(dev_id));
}
-static void dwapb_irq_enable(struct irq_data *d)
+static void dwapb_irq_ack(struct irq_data *d)
{
- struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
- struct dwapb_gpio *gpio = igc->private;
- struct gpio_chip *gc = &gpio->ports[0].gc;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
+ u32 val = BIT(irqd_to_hwirq(d));
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+ dwapb_write(gpio, GPIO_PORTA_EOI, val);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+}
+
+static void dwapb_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
unsigned long flags;
u32 val;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
- val = dwapb_read(gpio, GPIO_INTEN);
- val |= BIT(d->hwirq);
- dwapb_write(gpio, GPIO_INTEN, val);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+ val = dwapb_read(gpio, GPIO_INTMASK) | BIT(hwirq);
+ dwapb_write(gpio, GPIO_INTMASK, val);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+
+ gpiochip_disable_irq(gc, hwirq);
}
-static void dwapb_irq_disable(struct irq_data *d)
+static void dwapb_irq_unmask(struct irq_data *d)
{
- struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
- struct dwapb_gpio *gpio = igc->private;
- struct gpio_chip *gc = &gpio->ports[0].gc;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
unsigned long flags;
u32 val;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
- val = dwapb_read(gpio, GPIO_INTEN);
- val &= ~BIT(d->hwirq);
- dwapb_write(gpio, GPIO_INTEN, val);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ gpiochip_enable_irq(gc, hwirq);
+
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+ val = dwapb_read(gpio, GPIO_INTMASK) & ~BIT(hwirq);
+ dwapb_write(gpio, GPIO_INTMASK, val);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
-static int dwapb_irq_reqres(struct irq_data *d)
+static void dwapb_irq_enable(struct irq_data *d)
{
- struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
- struct dwapb_gpio *gpio = igc->private;
- struct gpio_chip *gc = &gpio->ports[0].gc;
- int ret;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
+ unsigned long flags;
+ u32 val;
- ret = gpiochip_lock_as_irq(gc, irqd_to_hwirq(d));
- if (ret) {
- dev_err(gpio->dev, "unable to lock HW IRQ %lu for IRQ\n",
- irqd_to_hwirq(d));
- return ret;
- }
- return 0;
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+ val = dwapb_read(gpio, GPIO_INTEN);
+ val |= BIT(irqd_to_hwirq(d));
+ dwapb_write(gpio, GPIO_INTEN, val);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
-static void dwapb_irq_relres(struct irq_data *d)
+static void dwapb_irq_disable(struct irq_data *d)
{
- struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
- struct dwapb_gpio *gpio = igc->private;
- struct gpio_chip *gc = &gpio->ports[0].gc;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
+ unsigned long flags;
+ u32 val;
- gpiochip_unlock_as_irq(gc, irqd_to_hwirq(d));
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+ val = dwapb_read(gpio, GPIO_INTEN);
+ val &= ~BIT(irqd_to_hwirq(d));
+ dwapb_write(gpio, GPIO_INTEN, val);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static int dwapb_irq_set_type(struct irq_data *d, u32 type)
{
- struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
- struct dwapb_gpio *gpio = igc->private;
- struct gpio_chip *gc = &gpio->ports[0].gc;
- int bit = d->hwirq;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
+ irq_hw_number_t bit = irqd_to_hwirq(d);
unsigned long level, polarity, flags;
- if (type & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
- IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
- return -EINVAL;
-
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
level = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
polarity = dwapb_read(gpio, GPIO_INT_POLARITY);
@@ -311,12 +341,15 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
break;
}
- irq_setup_alt_chip(d, type);
+ if (type & IRQ_TYPE_LEVEL_MASK)
+ irq_set_handler_locked(d, handle_level_irq);
+ else if (type & IRQ_TYPE_EDGE_BOTH)
+ irq_set_handler_locked(d, handle_edge_irq);
dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level);
if (type != IRQ_TYPE_EDGE_BOTH)
dwapb_write(gpio, GPIO_INT_POLARITY, polarity);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@@ -324,19 +357,35 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
#ifdef CONFIG_PM_SLEEP
static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
{
- struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
- struct dwapb_gpio *gpio = igc->private;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
struct dwapb_context *ctx = gpio->ports[0].ctx;
+ irq_hw_number_t bit = irqd_to_hwirq(d);
if (enable)
- ctx->wake_en |= BIT(d->hwirq);
+ ctx->wake_en |= BIT(bit);
else
- ctx->wake_en &= ~BIT(d->hwirq);
+ ctx->wake_en &= ~BIT(bit);
return 0;
}
+#else
+#define dwapb_irq_set_wake NULL
#endif
+static const struct irq_chip dwapb_irq_chip = {
+ .name = DWAPB_DRIVER_NAME,
+ .irq_ack = dwapb_irq_ack,
+ .irq_mask = dwapb_irq_mask,
+ .irq_unmask = dwapb_irq_unmask,
+ .irq_set_type = dwapb_irq_set_type,
+ .irq_enable = dwapb_irq_enable,
+ .irq_disable = dwapb_irq_disable,
+ .irq_set_wake = dwapb_irq_set_wake,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
static int dwapb_gpio_set_debounce(struct gpio_chip *gc,
unsigned offset, unsigned debounce)
{
@@ -345,15 +394,16 @@ static int dwapb_gpio_set_debounce(struct gpio_chip *gc,
unsigned long flags, val_deb;
unsigned long mask = BIT(offset);
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
val_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
if (debounce)
- dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb | mask);
+ val_deb |= mask;
else
- dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb & ~mask);
+ val_deb &= ~mask;
+ dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@@ -370,120 +420,77 @@ static int dwapb_gpio_set_config(struct gpio_chip *gc, unsigned offset,
return dwapb_gpio_set_debounce(gc, offset, debounce);
}
-static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id)
+static int dwapb_convert_irqs(struct dwapb_gpio_port_irqchip *pirq,
+ struct dwapb_port_property *pp)
{
- u32 worked;
- struct dwapb_gpio *gpio = dev_id;
+ int i;
- worked = dwapb_do_irq(gpio);
+ /* Group all available IRQs into an array of parental IRQs. */
+ for (i = 0; i < pp->ngpio; ++i) {
+ if (!pp->irq[i])
+ continue;
+
+ pirq->irq[pirq->nr_irqs++] = pp->irq[i];
+ }
- return worked ? IRQ_HANDLED : IRQ_NONE;
+ return pirq->nr_irqs ? 0 : -ENOENT;
}
static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
struct dwapb_gpio_port *port,
struct dwapb_port_property *pp)
{
+ struct dwapb_gpio_port_irqchip *pirq;
struct gpio_chip *gc = &port->gc;
- struct fwnode_handle *fwnode = pp->fwnode;
- struct irq_chip_generic *irq_gc = NULL;
- unsigned int hwirq, ngpio = gc->ngpio;
- struct irq_chip_type *ct;
- int err, i;
-
- gpio->domain = irq_domain_create_linear(fwnode, ngpio,
- &irq_generic_chip_ops, gpio);
- if (!gpio->domain)
- return;
+ struct gpio_irq_chip *girq;
+ int err;
- err = irq_alloc_domain_generic_chips(gpio->domain, ngpio, 2,
- "gpio-dwapb", handle_level_irq,
- IRQ_NOREQUEST, 0,
- IRQ_GC_INIT_NESTED_LOCK);
- if (err) {
- dev_info(gpio->dev, "irq_alloc_domain_generic_chips failed\n");
- irq_domain_remove(gpio->domain);
- gpio->domain = NULL;
+ pirq = devm_kzalloc(gpio->dev, sizeof(*pirq), GFP_KERNEL);
+ if (!pirq)
return;
- }
- irq_gc = irq_get_domain_generic_chip(gpio->domain, 0);
- if (!irq_gc) {
- irq_domain_remove(gpio->domain);
- gpio->domain = NULL;
- return;
+ if (dwapb_convert_irqs(pirq, pp)) {
+ dev_warn(gpio->dev, "no IRQ for port%d\n", pp->idx);
+ goto err_kfree_pirq;
}
- irq_gc->reg_base = gpio->regs;
- irq_gc->private = gpio;
-
- for (i = 0; i < 2; i++) {
- ct = &irq_gc->chip_types[i];
- ct->chip.irq_ack = irq_gc_ack_set_bit;
- ct->chip.irq_mask = irq_gc_mask_set_bit;
- ct->chip.irq_unmask = irq_gc_mask_clr_bit;
- ct->chip.irq_set_type = dwapb_irq_set_type;
- ct->chip.irq_enable = dwapb_irq_enable;
- ct->chip.irq_disable = dwapb_irq_disable;
- ct->chip.irq_request_resources = dwapb_irq_reqres;
- ct->chip.irq_release_resources = dwapb_irq_relres;
-#ifdef CONFIG_PM_SLEEP
- ct->chip.irq_set_wake = dwapb_irq_set_wake;
-#endif
- ct->regs.ack = gpio_reg_convert(gpio, GPIO_PORTA_EOI);
- ct->regs.mask = gpio_reg_convert(gpio, GPIO_INTMASK);
- ct->type = IRQ_TYPE_LEVEL_MASK;
- }
+ girq = &gc->irq;
+ girq->handler = handle_bad_irq;
+ girq->default_type = IRQ_TYPE_NONE;
- irq_gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
- irq_gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
- irq_gc->chip_types[1].handler = handle_edge_irq;
+ port->pirq = pirq;
- if (!pp->irq_shared) {
- int i;
+ /*
+ * Intel ACPI-based platforms mostly have the DesignWare APB GPIO
+ * IRQ lane shared between several devices. In that case the parental
+ * IRQ has to be handled in the shared way so to be properly delivered
+ * to all the connected devices.
+ */
+ if (has_acpi_companion(gpio->dev)) {
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->parent_handler = NULL;
- for (i = 0; i < pp->ngpio; i++) {
- if (pp->irq[i] >= 0)
- irq_set_chained_handler_and_data(pp->irq[i],
- dwapb_irq_handler, gpio);
- }
- } else {
- /*
- * Request a shared IRQ since where MFD would have devices
- * using the same irq pin
- */
err = devm_request_irq(gpio->dev, pp->irq[0],
dwapb_irq_handler_mfd,
- IRQF_SHARED, "gpio-dwapb-mfd", gpio);
+ IRQF_SHARED, DWAPB_DRIVER_NAME, gpio);
if (err) {
dev_err(gpio->dev, "error requesting IRQ\n");
- irq_domain_remove(gpio->domain);
- gpio->domain = NULL;
- return;
+ goto err_kfree_pirq;
}
+ } else {
+ girq->num_parents = pirq->nr_irqs;
+ girq->parents = pirq->irq;
+ girq->parent_handler_data = gpio;
+ girq->parent_handler = dwapb_irq_handler;
}
- for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
- irq_create_mapping(gpio->domain, hwirq);
-
- port->gc.to_irq = dwapb_gpio_to_irq;
-}
-
-static void dwapb_irq_teardown(struct dwapb_gpio *gpio)
-{
- struct dwapb_gpio_port *port = &gpio->ports[0];
- struct gpio_chip *gc = &port->gc;
- unsigned int ngpio = gc->ngpio;
- irq_hw_number_t hwirq;
+ gpio_irq_chip_set_chip(girq, &dwapb_irq_chip);
- if (!gpio->domain)
- return;
+ return;
- for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
- irq_dispose_mapping(irq_find_mapping(gpio->domain, hwirq));
-
- irq_domain_remove(gpio->domain);
- gpio->domain = NULL;
+err_kfree_pirq:
+ devm_kfree(gpio->dev, pirq);
}
static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
@@ -504,10 +511,9 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
return -ENOMEM;
#endif
- dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_STRIDE);
- set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_STRIDE);
- dirout = gpio->regs + GPIO_SWPORTA_DDR +
- (pp->idx * GPIO_SWPORT_DDR_STRIDE);
+ dat = gpio->regs + GPIO_EXT_PORTA + pp->idx * GPIO_EXT_PORT_STRIDE;
+ set = gpio->regs + GPIO_SWPORTA_DR + pp->idx * GPIO_SWPORT_DR_STRIDE;
+ dirout = gpio->regs + GPIO_SWPORTA_DDR + pp->idx * GPIO_SWPORT_DDR_STRIDE;
/* This registers 32 GPIO lines per port */
err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
@@ -518,9 +524,7 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
return err;
}
-#ifdef CONFIG_OF_GPIO
- port->gc.of_node = to_of_node(pp->fwnode);
-#endif
+ port->gc.fwnode = pp->fwnode;
port->gc.ngpio = pp->ngpio;
port->gc.base = pp->gpio_base;
@@ -528,40 +532,42 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
if (pp->idx == 0)
port->gc.set_config = dwapb_gpio_set_config;
- if (pp->has_irq)
+ /* Only port A can provide interrupts in all configurations of the IP */
+ if (pp->idx == 0)
dwapb_configure_irqs(gpio, port, pp);
- err = gpiochip_add_data(&port->gc, port);
- if (err)
+ err = devm_gpiochip_add_data(gpio->dev, &port->gc, port);
+ if (err) {
dev_err(gpio->dev, "failed to register gpiochip for port%d\n",
port->idx);
- else
- port->is_registered = true;
-
- /* Add GPIO-signaled ACPI event support */
- if (pp->has_irq)
- acpi_gpiochip_request_interrupts(&port->gc);
+ return err;
+ }
- return err;
+ return 0;
}
-static void dwapb_gpio_unregister(struct dwapb_gpio *gpio)
+static void dwapb_get_irq(struct device *dev, struct fwnode_handle *fwnode,
+ struct dwapb_port_property *pp)
{
- unsigned int m;
-
- for (m = 0; m < gpio->nr_ports; ++m)
- if (gpio->ports[m].is_registered)
- gpiochip_remove(&gpio->ports[m].gc);
+ int irq, j;
+
+ for (j = 0; j < pp->ngpio; j++) {
+ if (has_acpi_companion(dev))
+ irq = platform_get_irq_optional(to_platform_device(dev), j);
+ else
+ irq = fwnode_irq_get(fwnode, j);
+ if (irq > 0)
+ pp->irq[j] = irq;
+ }
}
-static struct dwapb_platform_data *
-dwapb_gpio_get_pdata(struct device *dev)
+static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev)
{
struct fwnode_handle *fwnode;
struct dwapb_platform_data *pdata;
struct dwapb_port_property *pp;
int nports;
- int i, j;
+ int i;
nports = device_get_child_node_count(dev);
if (nports == 0)
@@ -579,8 +585,6 @@ dwapb_gpio_get_pdata(struct device *dev)
i = 0;
device_for_each_child_node(dev, fwnode) {
- struct device_node *np = NULL;
-
pp = &pdata->properties[i++];
pp->fwnode = fwnode;
@@ -592,58 +596,95 @@ dwapb_gpio_get_pdata(struct device *dev)
return ERR_PTR(-EINVAL);
}
- if (fwnode_property_read_u32(fwnode, "snps,nr-gpios",
- &pp->ngpio)) {
+ if (fwnode_property_read_u32(fwnode, "ngpios", &pp->ngpio) &&
+ fwnode_property_read_u32(fwnode, "snps,nr-gpios", &pp->ngpio)) {
dev_info(dev,
"failed to get number of gpios for port%d\n",
i);
- pp->ngpio = 32;
+ pp->ngpio = DWAPB_MAX_GPIOS;
}
- pp->irq_shared = false;
pp->gpio_base = -1;
+ /* For internal use only, new platforms mustn't exercise this */
+ if (is_software_node(fwnode))
+ fwnode_property_read_u32(fwnode, "gpio-base", &pp->gpio_base);
+
/*
* Only port A can provide interrupts in all configurations of
* the IP.
*/
- if (pp->idx != 0)
- continue;
+ if (pp->idx == 0)
+ dwapb_get_irq(dev, fwnode, pp);
+ }
- if (dev->of_node && fwnode_property_read_bool(fwnode,
- "interrupt-controller")) {
- np = to_of_node(fwnode);
- }
+ return pdata;
+}
- for (j = 0; j < pp->ngpio; j++) {
- pp->irq[j] = -ENXIO;
+static void dwapb_assert_reset(void *data)
+{
+ struct dwapb_gpio *gpio = data;
- if (np)
- pp->irq[j] = of_irq_get(np, j);
- else if (has_acpi_companion(dev))
- pp->irq[j] = platform_get_irq(to_platform_device(dev), j);
+ reset_control_assert(gpio->rst);
+}
- if (pp->irq[j] >= 0)
- pp->has_irq = true;
- }
+static int dwapb_get_reset(struct dwapb_gpio *gpio)
+{
+ int err;
+
+ gpio->rst = devm_reset_control_get_optional_shared(gpio->dev, NULL);
+ if (IS_ERR(gpio->rst))
+ return dev_err_probe(gpio->dev, PTR_ERR(gpio->rst),
+ "Cannot get reset descriptor\n");
- if (!pp->has_irq)
- dev_warn(dev, "no irq for port%d\n", pp->idx);
+ err = reset_control_deassert(gpio->rst);
+ if (err) {
+ dev_err(gpio->dev, "Cannot deassert reset lane\n");
+ return err;
}
- return pdata;
+ return devm_add_action_or_reset(gpio->dev, dwapb_assert_reset, gpio);
+}
+
+static void dwapb_disable_clks(void *data)
+{
+ struct dwapb_gpio *gpio = data;
+
+ clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
+}
+
+static int dwapb_get_clks(struct dwapb_gpio *gpio)
+{
+ int err;
+
+ /* Optional bus and debounce clocks */
+ gpio->clks[0].id = "bus";
+ gpio->clks[1].id = "db";
+ err = devm_clk_bulk_get_optional(gpio->dev, DWAPB_NR_CLOCKS,
+ gpio->clks);
+ if (err)
+ return dev_err_probe(gpio->dev, err,
+ "Cannot get APB/Debounce clocks\n");
+
+ err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks);
+ if (err) {
+ dev_err(gpio->dev, "Cannot enable APB/Debounce clocks\n");
+ return err;
+ }
+
+ return devm_add_action_or_reset(gpio->dev, dwapb_disable_clks, gpio);
}
static const struct of_device_id dwapb_of_match[] = {
- { .compatible = "snps,dw-apb-gpio", .data = (void *)0},
+ { .compatible = "snps,dw-apb-gpio", .data = (void *)GPIO_REG_OFFSET_V1},
{ .compatible = "apm,xgene-gpio-v2", .data = (void *)GPIO_REG_OFFSET_V2},
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dwapb_of_match);
static const struct acpi_device_id dwapb_acpi_match[] = {
- {"HISI0181", 0},
- {"APMC0D07", 0},
+ {"HISI0181", GPIO_REG_OFFSET_V1},
+ {"APMC0D07", GPIO_REG_OFFSET_V1},
{"APMC0D81", GPIO_REG_OFFSET_V2},
{ }
};
@@ -654,17 +695,12 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
unsigned int i;
struct dwapb_gpio *gpio;
int err;
+ struct dwapb_platform_data *pdata;
struct device *dev = &pdev->dev;
- struct dwapb_platform_data *pdata = dev_get_platdata(dev);
- if (!pdata) {
- pdata = dwapb_gpio_get_pdata(dev);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
- }
-
- if (!pdata->nports)
- return -ENODEV;
+ pdata = dwapb_gpio_get_pdata(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
@@ -673,11 +709,9 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
gpio->dev = &pdev->dev;
gpio->nr_ports = pdata->nports;
- gpio->rst = devm_reset_control_get_optional_shared(dev, NULL);
- if (IS_ERR(gpio->rst))
- return PTR_ERR(gpio->rst);
-
- reset_control_deassert(gpio->rst);
+ err = dwapb_get_reset(gpio);
+ if (err)
+ return err;
gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
sizeof(*gpio->ports), GFP_KERNEL);
@@ -688,54 +722,19 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpio->regs))
return PTR_ERR(gpio->regs);
- /* Optional bus clock */
- gpio->clk = devm_clk_get(&pdev->dev, "bus");
- if (!IS_ERR(gpio->clk)) {
- err = clk_prepare_enable(gpio->clk);
- if (err) {
- dev_info(&pdev->dev, "Cannot enable clock\n");
- return err;
- }
- }
+ err = dwapb_get_clks(gpio);
+ if (err)
+ return err;
- gpio->flags = 0;
- if (dev->of_node) {
- gpio->flags = (uintptr_t)of_device_get_match_data(dev);
- } else if (has_acpi_companion(dev)) {
- const struct acpi_device_id *acpi_id;
-
- acpi_id = acpi_match_device(dwapb_acpi_match, dev);
- if (acpi_id) {
- if (acpi_id->driver_data)
- gpio->flags = acpi_id->driver_data;
- }
- }
+ gpio->flags = (uintptr_t)device_get_match_data(dev);
for (i = 0; i < gpio->nr_ports; i++) {
err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i);
if (err)
- goto out_unregister;
+ return err;
}
- platform_set_drvdata(pdev, gpio);
-
- return 0;
-
-out_unregister:
- dwapb_gpio_unregister(gpio);
- dwapb_irq_teardown(gpio);
- clk_disable_unprepare(gpio->clk);
- return err;
-}
-
-static int dwapb_gpio_remove(struct platform_device *pdev)
-{
- struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
-
- dwapb_gpio_unregister(gpio);
- dwapb_irq_teardown(gpio);
- reset_control_assert(gpio->rst);
- clk_disable_unprepare(gpio->clk);
+ platform_set_drvdata(pdev, gpio);
return 0;
}
@@ -748,14 +747,12 @@ static int dwapb_gpio_suspend(struct device *dev)
unsigned long flags;
int i;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
for (i = 0; i < gpio->nr_ports; i++) {
unsigned int offset;
unsigned int idx = gpio->ports[i].idx;
struct dwapb_context *ctx = gpio->ports[i].ctx;
- BUG_ON(!ctx);
-
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
ctx->dir = dwapb_read(gpio, offset);
@@ -774,13 +771,12 @@ static int dwapb_gpio_suspend(struct device *dev)
ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
/* Mask out interrupts */
- dwapb_write(gpio, GPIO_INTMASK,
- 0xffffffff & ~ctx->wake_en);
+ dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en);
}
}
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
- clk_disable_unprepare(gpio->clk);
+ clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
return 0;
}
@@ -790,19 +786,20 @@ static int dwapb_gpio_resume(struct device *dev)
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags;
- int i;
+ int i, err;
- if (!IS_ERR(gpio->clk))
- clk_prepare_enable(gpio->clk);
+ err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks);
+ if (err) {
+ dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n");
+ return err;
+ }
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
for (i = 0; i < gpio->nr_ports; i++) {
unsigned int offset;
unsigned int idx = gpio->ports[i].idx;
struct dwapb_context *ctx = gpio->ports[i].ctx;
- BUG_ON(!ctx);
-
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
dwapb_write(gpio, offset, ctx->data);
@@ -824,7 +821,7 @@ static int dwapb_gpio_resume(struct device *dev)
dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff);
}
}
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@@ -835,13 +832,12 @@ static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend,
static struct platform_driver dwapb_gpio_driver = {
.driver = {
- .name = "gpio-dwapb",
+ .name = DWAPB_DRIVER_NAME,
.pm = &dwapb_gpio_pm_ops,
- .of_match_table = of_match_ptr(dwapb_of_match),
- .acpi_match_table = ACPI_PTR(dwapb_acpi_match),
+ .of_match_table = dwapb_of_match,
+ .acpi_match_table = dwapb_acpi_match,
},
.probe = dwapb_gpio_probe,
- .remove = dwapb_gpio_remove,
};
module_platform_driver(dwapb_gpio_driver);
@@ -849,3 +845,4 @@ module_platform_driver(dwapb_gpio_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jamie Iles");
MODULE_DESCRIPTION("Synopsys DesignWare APB GPIO driver");
+MODULE_ALIAS("platform:" DWAPB_DRIVER_NAME);
diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c
index 7b9ac4a12c20..8d722e026e9c 100644
--- a/drivers/gpio/gpio-eic-sprd.c
+++ b/drivers/gpio/gpio-eic-sprd.c
@@ -530,11 +530,12 @@ static void sprd_eic_handle_one_type(struct gpio_chip *chip)
}
for_each_set_bit(n, &reg, SPRD_EIC_PER_BANK_NR) {
- girq = irq_find_mapping(chip->irq.domain,
- bank * SPRD_EIC_PER_BANK_NR + n);
+ u32 offset = bank * SPRD_EIC_PER_BANK_NR + n;
+
+ girq = irq_find_mapping(chip->irq.domain, offset);
generic_handle_irq(girq);
- sprd_eic_toggle_trigger(chip, girq, n);
+ sprd_eic_toggle_trigger(chip, girq, offset);
}
}
}
@@ -568,6 +569,7 @@ static int sprd_eic_probe(struct platform_device *pdev)
const struct sprd_eic_variant_data *pdata;
struct gpio_irq_chip *irq;
struct sprd_eic *sprd_eic;
+ struct resource *res;
int ret, i;
pdata = of_device_get_match_data(&pdev->dev);
@@ -584,10 +586,8 @@ static int sprd_eic_probe(struct platform_device *pdev)
sprd_eic->type = pdata->type;
sprd_eic->irq = platform_get_irq(pdev, 0);
- if (sprd_eic->irq < 0) {
- dev_err(&pdev->dev, "Failed to get EIC interrupt.\n");
+ if (sprd_eic->irq < 0)
return sprd_eic->irq;
- }
for (i = 0; i < SPRD_EIC_MAX_BANK; i++) {
/*
@@ -596,16 +596,19 @@ static int sprd_eic_probe(struct platform_device *pdev)
* have one bank EIC, thus base[1] and base[2] can be
* optional.
*/
- sprd_eic->base[i] = devm_platform_ioremap_resource(pdev, i);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res)
+ break;
+
+ sprd_eic->base[i] = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(sprd_eic->base[i]))
- continue;
+ return PTR_ERR(sprd_eic->base[i]);
}
sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type];
sprd_eic->chip.ngpio = pdata->num_eics;
sprd_eic->chip.base = -1;
sprd_eic->chip.parent = &pdev->dev;
- sprd_eic->chip.of_node = pdev->dev.of_node;
sprd_eic->chip.direction_input = sprd_eic_direction_input;
switch (sprd_eic->type) {
case SPRD_EIC_DEBOUNCE:
@@ -613,14 +616,12 @@ static int sprd_eic_probe(struct platform_device *pdev)
sprd_eic->chip.free = sprd_eic_free;
sprd_eic->chip.set_config = sprd_eic_set_config;
sprd_eic->chip.set = sprd_eic_set;
- /* fall-through */
+ fallthrough;
case SPRD_EIC_ASYNC:
- /* fall-through */
case SPRD_EIC_SYNC:
sprd_eic->chip.get = sprd_eic_get;
break;
case SPRD_EIC_LATCH:
- /* fall-through */
default:
break;
}
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
index a87951293aaa..858e6ebbb584 100644
--- a/drivers/gpio/gpio-em.c
+++ b/drivers/gpio/gpio-em.c
@@ -173,7 +173,7 @@ static irqreturn_t em_gio_irq_handler(int irq, void *dev_id)
while ((pending = em_gio_read(p, GIO_MST))) {
offset = __ffs(pending);
em_gio_write(p, GIO_IIR, BIT(offset));
- generic_handle_irq(irq_find_mapping(p->irq_domain, offset));
+ generic_handle_domain_irq(p->irq_domain, offset);
irqs_handled++;
}
@@ -269,14 +269,14 @@ static void em_gio_irq_domain_remove(void *data)
static int em_gio_probe(struct platform_device *pdev)
{
struct em_gio_priv *p;
- struct resource *io[2], *irq[2];
struct gpio_chip *gpio_chip;
struct irq_chip *irq_chip;
- const char *name = dev_name(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ const char *name = dev_name(dev);
unsigned int ngpios;
- int ret;
+ int irq[2], ret;
- p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -284,33 +284,28 @@ static int em_gio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, p);
spin_lock_init(&p->sense_lock);
- io[0] = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- io[1] = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ irq[0] = platform_get_irq(pdev, 0);
+ if (irq[0] < 0)
+ return irq[0];
- if (!io[0] || !io[1] || !irq[0] || !irq[1]) {
- dev_err(&pdev->dev, "missing IRQ or IOMEM\n");
- return -EINVAL;
- }
+ irq[1] = platform_get_irq(pdev, 1);
+ if (irq[1] < 0)
+ return irq[1];
- p->base0 = devm_ioremap_nocache(&pdev->dev, io[0]->start,
- resource_size(io[0]));
- if (!p->base0)
- return -ENOMEM;
+ p->base0 = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(p->base0))
+ return PTR_ERR(p->base0);
- p->base1 = devm_ioremap_nocache(&pdev->dev, io[1]->start,
- resource_size(io[1]));
- if (!p->base1)
- return -ENOMEM;
+ p->base1 = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(p->base1))
+ return PTR_ERR(p->base1);
- if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) {
- dev_err(&pdev->dev, "Missing ngpios OF property\n");
+ if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+ dev_err(dev, "Missing ngpios OF property\n");
return -EINVAL;
}
gpio_chip = &p->gpio_chip;
- gpio_chip->of_node = pdev->dev.of_node;
gpio_chip->direction_input = em_gio_direction_input;
gpio_chip->get = em_gio_get;
gpio_chip->direction_output = em_gio_direction_output;
@@ -319,13 +314,13 @@ static int em_gio_probe(struct platform_device *pdev)
gpio_chip->request = em_gio_request;
gpio_chip->free = em_gio_free;
gpio_chip->label = name;
- gpio_chip->parent = &pdev->dev;
+ gpio_chip->parent = dev;
gpio_chip->owner = THIS_MODULE;
gpio_chip->base = -1;
gpio_chip->ngpio = ngpios;
irq_chip = &p->irq_chip;
- irq_chip->name = name;
+ irq_chip->name = "gpio-em";
irq_chip->irq_mask = em_gio_irq_disable;
irq_chip->irq_unmask = em_gio_irq_enable;
irq_chip->irq_set_type = em_gio_irq_set_type;
@@ -333,33 +328,31 @@ static int em_gio_probe(struct platform_device *pdev)
irq_chip->irq_release_resources = em_gio_irq_relres;
irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND;
- p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, ngpios, 0,
+ p->irq_domain = irq_domain_add_simple(dev->of_node, ngpios, 0,
&em_gio_irq_domain_ops, p);
if (!p->irq_domain) {
- dev_err(&pdev->dev, "cannot initialize irq domain\n");
+ dev_err(dev, "cannot initialize irq domain\n");
return -ENXIO;
}
- ret = devm_add_action_or_reset(&pdev->dev, em_gio_irq_domain_remove,
+ ret = devm_add_action_or_reset(dev, em_gio_irq_domain_remove,
p->irq_domain);
if (ret)
return ret;
- if (devm_request_irq(&pdev->dev, irq[0]->start,
- em_gio_irq_handler, 0, name, p)) {
- dev_err(&pdev->dev, "failed to request low IRQ\n");
+ if (devm_request_irq(dev, irq[0], em_gio_irq_handler, 0, name, p)) {
+ dev_err(dev, "failed to request low IRQ\n");
return -ENOENT;
}
- if (devm_request_irq(&pdev->dev, irq[1]->start,
- em_gio_irq_handler, 0, name, p)) {
- dev_err(&pdev->dev, "failed to request high IRQ\n");
+ if (devm_request_irq(dev, irq[1], em_gio_irq_handler, 0, name, p)) {
+ dev_err(dev, "failed to request high IRQ\n");
return -ENOENT;
}
- ret = devm_gpiochip_add_data(&pdev->dev, gpio_chip, p);
+ ret = devm_gpiochip_add_data(dev, gpio_chip, p);
if (ret) {
- dev_err(&pdev->dev, "failed to add GPIO controller\n");
+ dev_err(dev, "failed to add GPIO controller\n");
return ret;
}
diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c
new file mode 100644
index 000000000000..f836a8db4c1d
--- /dev/null
+++ b/drivers/gpio/gpio-en7523.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/bits.h>
+#include <linux/gpio/driver.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#define AIROHA_GPIO_MAX 32
+
+/**
+ * airoha_gpio_ctrl - Airoha GPIO driver data
+ * @gc: Associated gpio_chip instance.
+ * @data: The data register.
+ * @dir0: The direction register for the lower 16 pins.
+ * @dir1: The direction register for the higher 16 pins.
+ * @output: The output enable register.
+ */
+struct airoha_gpio_ctrl {
+ struct gpio_chip gc;
+ void __iomem *data;
+ void __iomem *dir[2];
+ void __iomem *output;
+};
+
+static struct airoha_gpio_ctrl *gc_to_ctrl(struct gpio_chip *gc)
+{
+ return container_of(gc, struct airoha_gpio_ctrl, gc);
+}
+
+static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio,
+ int val, int out)
+{
+ struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc);
+ u32 dir = ioread32(ctrl->dir[gpio / 16]);
+ u32 output = ioread32(ctrl->output);
+ u32 mask = BIT((gpio % 16) * 2);
+
+ if (out) {
+ dir |= mask;
+ output |= BIT(gpio);
+ } else {
+ dir &= ~mask;
+ output &= ~BIT(gpio);
+ }
+
+ iowrite32(dir, ctrl->dir[gpio / 16]);
+
+ if (out)
+ gc->set(gc, gpio, val);
+
+ iowrite32(output, ctrl->output);
+
+ return 0;
+}
+
+static int airoha_dir_out(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ return airoha_dir_set(gc, gpio, val, 1);
+}
+
+static int airoha_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ return airoha_dir_set(gc, gpio, 0, 0);
+}
+
+static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc);
+ u32 dir = ioread32(ctrl->dir[gpio / 16]);
+ u32 mask = BIT((gpio % 16) * 2);
+
+ return (dir & mask) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int airoha_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct airoha_gpio_ctrl *ctrl;
+ int err;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ ctrl->data = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ctrl->data))
+ return PTR_ERR(ctrl->data);
+
+ ctrl->dir[0] = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(ctrl->dir[0]))
+ return PTR_ERR(ctrl->dir[0]);
+
+ ctrl->dir[1] = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(ctrl->dir[1]))
+ return PTR_ERR(ctrl->dir[1]);
+
+ ctrl->output = devm_platform_ioremap_resource(pdev, 3);
+ if (IS_ERR(ctrl->output))
+ return PTR_ERR(ctrl->output);
+
+ err = bgpio_init(&ctrl->gc, dev, 4, ctrl->data, NULL,
+ NULL, NULL, NULL, 0);
+ if (err)
+ return dev_err_probe(dev, err, "unable to init generic GPIO");
+
+ ctrl->gc.ngpio = AIROHA_GPIO_MAX;
+ ctrl->gc.owner = THIS_MODULE;
+ ctrl->gc.direction_output = airoha_dir_out;
+ ctrl->gc.direction_input = airoha_dir_in;
+ ctrl->gc.get_direction = airoha_get_dir;
+
+ return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
+}
+
+static const struct of_device_id airoha_gpio_of_match[] = {
+ { .compatible = "airoha,en7523-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, airoha_gpio_of_match);
+
+static struct platform_driver airoha_gpio_driver = {
+ .driver = {
+ .name = "airoha-gpio",
+ .of_match_table = airoha_gpio_of_match,
+ },
+ .probe = airoha_gpio_probe,
+};
+module_platform_driver(airoha_gpio_driver);
+
+MODULE_DESCRIPTION("Airoha GPIO support");
+MODULE_AUTHOR("John Crispin <john@phrozen.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c
index a90870a60c15..2e1779709113 100644
--- a/drivers/gpio/gpio-ep93xx.c
+++ b/drivers/gpio/gpio-ep93xx.c
@@ -25,83 +25,89 @@
/* Maximum value for gpio line identifiers */
#define EP93XX_GPIO_LINE_MAX 63
+/* Number of GPIO chips in EP93XX */
+#define EP93XX_GPIO_CHIP_NUM 8
+
/* Maximum value for irq capable line identifiers */
#define EP93XX_GPIO_LINE_MAX_IRQ 23
+#define EP93XX_GPIO_A_IRQ_BASE 64
+#define EP93XX_GPIO_B_IRQ_BASE 72
/*
* Static mapping of GPIO bank F IRQS:
* F0..F7 (16..24) to irq 80..87.
*/
#define EP93XX_GPIO_F_IRQ_BASE 80
-struct ep93xx_gpio {
- void __iomem *base;
- struct gpio_chip gc[8];
+struct ep93xx_gpio_irq_chip {
+ struct irq_chip ic;
+ u8 irq_offset;
+ u8 int_unmasked;
+ u8 int_enabled;
+ u8 int_type1;
+ u8 int_type2;
+ u8 int_debounce;
};
-/*************************************************************************
- * Interrupt handling for EP93xx on-chip GPIOs
- *************************************************************************/
-static unsigned char gpio_int_unmasked[3];
-static unsigned char gpio_int_enabled[3];
-static unsigned char gpio_int_type1[3];
-static unsigned char gpio_int_type2[3];
-static unsigned char gpio_int_debounce[3];
-
-/* Port ordering is: A B F */
-static const u8 int_type1_register_offset[3] = { 0x90, 0xac, 0x4c };
-static const u8 int_type2_register_offset[3] = { 0x94, 0xb0, 0x50 };
-static const u8 eoi_register_offset[3] = { 0x98, 0xb4, 0x54 };
-static const u8 int_en_register_offset[3] = { 0x9c, 0xb8, 0x58 };
-static const u8 int_debounce_register_offset[3] = { 0xa8, 0xc4, 0x64 };
-
-static void ep93xx_gpio_update_int_params(struct ep93xx_gpio *epg, unsigned port)
-{
- BUG_ON(port > 2);
+struct ep93xx_gpio_chip {
+ struct gpio_chip gc;
+ struct ep93xx_gpio_irq_chip *eic;
+};
- writeb_relaxed(0, epg->base + int_en_register_offset[port]);
+struct ep93xx_gpio {
+ void __iomem *base;
+ struct ep93xx_gpio_chip gc[EP93XX_GPIO_CHIP_NUM];
+};
- writeb_relaxed(gpio_int_type2[port],
- epg->base + int_type2_register_offset[port]);
+#define to_ep93xx_gpio_chip(x) container_of(x, struct ep93xx_gpio_chip, gc)
- writeb_relaxed(gpio_int_type1[port],
- epg->base + int_type1_register_offset[port]);
+static struct ep93xx_gpio_irq_chip *to_ep93xx_gpio_irq_chip(struct gpio_chip *gc)
+{
+ struct ep93xx_gpio_chip *egc = to_ep93xx_gpio_chip(gc);
- writeb(gpio_int_unmasked[port] & gpio_int_enabled[port],
- epg->base + int_en_register_offset[port]);
+ return egc->eic;
}
-static int ep93xx_gpio_port(struct gpio_chip *gc)
+/*************************************************************************
+ * Interrupt handling for EP93xx on-chip GPIOs
+ *************************************************************************/
+#define EP93XX_INT_TYPE1_OFFSET 0x00
+#define EP93XX_INT_TYPE2_OFFSET 0x04
+#define EP93XX_INT_EOI_OFFSET 0x08
+#define EP93XX_INT_EN_OFFSET 0x0c
+#define EP93XX_INT_STATUS_OFFSET 0x10
+#define EP93XX_INT_RAW_STATUS_OFFSET 0x14
+#define EP93XX_INT_DEBOUNCE_OFFSET 0x18
+
+static void ep93xx_gpio_update_int_params(struct ep93xx_gpio *epg,
+ struct ep93xx_gpio_irq_chip *eic)
{
- struct ep93xx_gpio *epg = gpiochip_get_data(gc);
- int port = 0;
+ writeb_relaxed(0, epg->base + eic->irq_offset + EP93XX_INT_EN_OFFSET);
- while (port < ARRAY_SIZE(epg->gc) && gc != &epg->gc[port])
- port++;
+ writeb_relaxed(eic->int_type2,
+ epg->base + eic->irq_offset + EP93XX_INT_TYPE2_OFFSET);
- /* This should not happen but is there as a last safeguard */
- if (port == ARRAY_SIZE(epg->gc)) {
- pr_crit("can't find the GPIO port\n");
- return 0;
- }
+ writeb_relaxed(eic->int_type1,
+ epg->base + eic->irq_offset + EP93XX_INT_TYPE1_OFFSET);
- return port;
+ writeb_relaxed(eic->int_unmasked & eic->int_enabled,
+ epg->base + eic->irq_offset + EP93XX_INT_EN_OFFSET);
}
static void ep93xx_gpio_int_debounce(struct gpio_chip *gc,
unsigned int offset, bool enable)
{
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
- int port = ep93xx_gpio_port(gc);
+ struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc);
int port_mask = BIT(offset);
if (enable)
- gpio_int_debounce[port] |= port_mask;
+ eic->int_debounce |= port_mask;
else
- gpio_int_debounce[port] &= ~port_mask;
+ eic->int_debounce &= ~port_mask;
- writeb(gpio_int_debounce[port],
- epg->base + int_debounce_register_offset[port]);
+ writeb(eic->int_debounce,
+ epg->base + eic->irq_offset + EP93XX_INT_DEBOUNCE_OFFSET);
}
static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc)
@@ -122,13 +128,13 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc)
*/
stat = readb(epg->base + EP93XX_GPIO_A_INT_STATUS);
for_each_set_bit(offset, &stat, 8)
- generic_handle_irq(irq_find_mapping(epg->gc[0].irq.domain,
- offset));
+ generic_handle_domain_irq(epg->gc[0].gc.irq.domain,
+ offset);
stat = readb(epg->base + EP93XX_GPIO_B_INT_STATUS);
for_each_set_bit(offset, &stat, 8)
- generic_handle_irq(irq_find_mapping(epg->gc[1].irq.domain,
- offset));
+ generic_handle_domain_irq(epg->gc[1].gc.irq.domain,
+ offset);
chained_irq_exit(irqchip, desc);
}
@@ -153,52 +159,52 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc)
static void ep93xx_gpio_irq_ack(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
- int port = ep93xx_gpio_port(gc);
int port_mask = BIT(d->irq & 7);
if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
- gpio_int_type2[port] ^= port_mask; /* switch edge direction */
- ep93xx_gpio_update_int_params(epg, port);
+ eic->int_type2 ^= port_mask; /* switch edge direction */
+ ep93xx_gpio_update_int_params(epg, eic);
}
- writeb(port_mask, epg->base + eoi_register_offset[port]);
+ writeb(port_mask, epg->base + eic->irq_offset + EP93XX_INT_EOI_OFFSET);
}
static void ep93xx_gpio_irq_mask_ack(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
- int port = ep93xx_gpio_port(gc);
int port_mask = BIT(d->irq & 7);
if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH)
- gpio_int_type2[port] ^= port_mask; /* switch edge direction */
+ eic->int_type2 ^= port_mask; /* switch edge direction */
- gpio_int_unmasked[port] &= ~port_mask;
- ep93xx_gpio_update_int_params(epg, port);
+ eic->int_unmasked &= ~port_mask;
+ ep93xx_gpio_update_int_params(epg, eic);
- writeb(port_mask, epg->base + eoi_register_offset[port]);
+ writeb(port_mask, epg->base + eic->irq_offset + EP93XX_INT_EOI_OFFSET);
}
static void ep93xx_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
- int port = ep93xx_gpio_port(gc);
- gpio_int_unmasked[port] &= ~BIT(d->irq & 7);
- ep93xx_gpio_update_int_params(epg, port);
+ eic->int_unmasked &= ~BIT(d->irq & 7);
+ ep93xx_gpio_update_int_params(epg, eic);
}
static void ep93xx_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
- int port = ep93xx_gpio_port(gc);
- gpio_int_unmasked[port] |= BIT(d->irq & 7);
- ep93xx_gpio_update_int_params(epg, port);
+ eic->int_unmasked |= BIT(d->irq & 7);
+ ep93xx_gpio_update_int_params(epg, eic);
}
/*
@@ -209,8 +215,8 @@ static void ep93xx_gpio_irq_unmask(struct irq_data *d)
static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
- int port = ep93xx_gpio_port(gc);
int offset = d->irq & 7;
int port_mask = BIT(offset);
irq_flow_handler_t handler;
@@ -219,32 +225,32 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
switch (type) {
case IRQ_TYPE_EDGE_RISING:
- gpio_int_type1[port] |= port_mask;
- gpio_int_type2[port] |= port_mask;
+ eic->int_type1 |= port_mask;
+ eic->int_type2 |= port_mask;
handler = handle_edge_irq;
break;
case IRQ_TYPE_EDGE_FALLING:
- gpio_int_type1[port] |= port_mask;
- gpio_int_type2[port] &= ~port_mask;
+ eic->int_type1 |= port_mask;
+ eic->int_type2 &= ~port_mask;
handler = handle_edge_irq;
break;
case IRQ_TYPE_LEVEL_HIGH:
- gpio_int_type1[port] &= ~port_mask;
- gpio_int_type2[port] |= port_mask;
+ eic->int_type1 &= ~port_mask;
+ eic->int_type2 |= port_mask;
handler = handle_level_irq;
break;
case IRQ_TYPE_LEVEL_LOW:
- gpio_int_type1[port] &= ~port_mask;
- gpio_int_type2[port] &= ~port_mask;
+ eic->int_type1 &= ~port_mask;
+ eic->int_type2 &= ~port_mask;
handler = handle_level_irq;
break;
case IRQ_TYPE_EDGE_BOTH:
- gpio_int_type1[port] |= port_mask;
+ eic->int_type1 |= port_mask;
/* set initial polarity based on current input level */
if (gc->get(gc, offset))
- gpio_int_type2[port] &= ~port_mask; /* falling */
+ eic->int_type2 &= ~port_mask; /* falling */
else
- gpio_int_type2[port] |= port_mask; /* rising */
+ eic->int_type2 |= port_mask; /* rising */
handler = handle_edge_irq;
break;
default:
@@ -253,72 +259,13 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
irq_set_handler_locked(d, handler);
- gpio_int_enabled[port] |= port_mask;
+ eic->int_enabled |= port_mask;
- ep93xx_gpio_update_int_params(epg, port);
+ ep93xx_gpio_update_int_params(epg, eic);
return 0;
}
-static struct irq_chip ep93xx_gpio_irq_chip = {
- .name = "GPIO",
- .irq_ack = ep93xx_gpio_irq_ack,
- .irq_mask_ack = ep93xx_gpio_irq_mask_ack,
- .irq_mask = ep93xx_gpio_irq_mask,
- .irq_unmask = ep93xx_gpio_irq_unmask,
- .irq_set_type = ep93xx_gpio_irq_type,
-};
-
-static int ep93xx_gpio_init_irq(struct platform_device *pdev,
- struct ep93xx_gpio *epg)
-{
- int ab_parent_irq = platform_get_irq(pdev, 0);
- struct device *dev = &pdev->dev;
- int gpio_irq;
- int ret;
- int i;
-
- /* The A bank */
- ret = gpiochip_irqchip_add(&epg->gc[0], &ep93xx_gpio_irq_chip,
- 64, handle_level_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "Could not add irqchip 0\n");
- return ret;
- }
- gpiochip_set_chained_irqchip(&epg->gc[0], &ep93xx_gpio_irq_chip,
- ab_parent_irq,
- ep93xx_gpio_ab_irq_handler);
-
- /* The B bank */
- ret = gpiochip_irqchip_add(&epg->gc[1], &ep93xx_gpio_irq_chip,
- 72, handle_level_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "Could not add irqchip 1\n");
- return ret;
- }
- gpiochip_set_chained_irqchip(&epg->gc[1], &ep93xx_gpio_irq_chip,
- ab_parent_irq,
- ep93xx_gpio_ab_irq_handler);
-
- /* The F bank */
- for (i = 0; i < 8; i++) {
- gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i;
- irq_set_chip_data(gpio_irq, &epg->gc[5]);
- irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip,
- handle_level_irq);
- irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST);
- }
-
- for (i = 1; i <= 8; i++)
- irq_set_chained_handler_and_data(platform_get_irq(pdev, i),
- ep93xx_gpio_f_irq_handler,
- &epg->gc[i]);
- return 0;
-}
-
-
/*************************************************************************
* gpiolib interface for EP93xx on-chip GPIOs
*************************************************************************/
@@ -326,28 +273,37 @@ struct ep93xx_gpio_bank {
const char *label;
int data;
int dir;
+ int irq;
int base;
bool has_irq;
+ bool has_hierarchical_irq;
+ unsigned int irq_base;
};
-#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _has_irq) \
+#define EP93XX_GPIO_BANK(_label, _data, _dir, _irq, _base, _has_irq, _has_hier, _irq_base) \
{ \
.label = _label, \
.data = _data, \
.dir = _dir, \
+ .irq = _irq, \
.base = _base, \
.has_irq = _has_irq, \
+ .has_hierarchical_irq = _has_hier, \
+ .irq_base = _irq_base, \
}
static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = {
- EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true), /* Bank A has 8 IRQs */
- EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true), /* Bank B has 8 IRQs */
- EP93XX_GPIO_BANK("C", 0x08, 0x18, 40, false),
- EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24, false),
- EP93XX_GPIO_BANK("E", 0x20, 0x24, 32, false),
- EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true), /* Bank F has 8 IRQs */
- EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false),
- EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false),
+ /* Bank A has 8 IRQs */
+ EP93XX_GPIO_BANK("A", 0x00, 0x10, 0x90, 0, true, false, EP93XX_GPIO_A_IRQ_BASE),
+ /* Bank B has 8 IRQs */
+ EP93XX_GPIO_BANK("B", 0x04, 0x14, 0xac, 8, true, false, EP93XX_GPIO_B_IRQ_BASE),
+ EP93XX_GPIO_BANK("C", 0x08, 0x18, 0x00, 40, false, false, 0),
+ EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 0x00, 24, false, false, 0),
+ EP93XX_GPIO_BANK("E", 0x20, 0x24, 0x00, 32, false, false, 0),
+ /* Bank F has 8 IRQs */
+ EP93XX_GPIO_BANK("F", 0x30, 0x34, 0x4c, 16, false, true, EP93XX_GPIO_F_IRQ_BASE),
+ EP93XX_GPIO_BANK("G", 0x38, 0x3c, 0x00, 48, false, false, 0),
+ EP93XX_GPIO_BANK("H", 0x40, 0x44, 0x00, 56, false, false, 0),
};
static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset,
@@ -364,17 +320,25 @@ static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset,
return 0;
}
-static int ep93xx_gpio_f_to_irq(struct gpio_chip *gc, unsigned offset)
+static void ep93xx_init_irq_chip(struct device *dev, struct irq_chip *ic)
{
- return EP93XX_GPIO_F_IRQ_BASE + offset;
+ ic->irq_ack = ep93xx_gpio_irq_ack;
+ ic->irq_mask_ack = ep93xx_gpio_irq_mask_ack;
+ ic->irq_mask = ep93xx_gpio_irq_mask;
+ ic->irq_unmask = ep93xx_gpio_irq_unmask;
+ ic->irq_set_type = ep93xx_gpio_irq_type;
}
-static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev,
+static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
+ struct platform_device *pdev,
struct ep93xx_gpio *epg,
struct ep93xx_gpio_bank *bank)
{
void __iomem *data = epg->base + bank->data;
void __iomem *dir = epg->base + bank->dir;
+ struct gpio_chip *gc = &egc->gc;
+ struct device *dev = &pdev->dev;
+ struct gpio_irq_chip *girq;
int err;
err = bgpio_init(gc, dev, 1, data, NULL, NULL, dir, NULL, 0);
@@ -384,8 +348,71 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev,
gc->label = bank->label;
gc->base = bank->base;
- if (bank->has_irq)
+ girq = &gc->irq;
+ if (bank->has_irq || bank->has_hierarchical_irq) {
+ struct irq_chip *ic;
+
gc->set_config = ep93xx_gpio_set_config;
+ egc->eic = devm_kcalloc(dev, 1,
+ sizeof(*egc->eic),
+ GFP_KERNEL);
+ if (!egc->eic)
+ return -ENOMEM;
+ egc->eic->irq_offset = bank->irq;
+ ic = &egc->eic->ic;
+ ic->name = devm_kasprintf(dev, GFP_KERNEL, "gpio-irq-%s", bank->label);
+ if (!ic->name)
+ return -ENOMEM;
+ ep93xx_init_irq_chip(dev, ic);
+ girq->chip = ic;
+ }
+
+ if (bank->has_irq) {
+ int ab_parent_irq = platform_get_irq(pdev, 0);
+
+ girq->parent_handler = ep93xx_gpio_ab_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(dev, girq->num_parents,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
+ girq->parents[0] = ab_parent_irq;
+ girq->first = bank->irq_base;
+ }
+
+ /* Only bank F has especially funky IRQ handling */
+ if (bank->has_hierarchical_irq) {
+ int gpio_irq;
+ int i;
+
+ /*
+ * FIXME: convert this to use hierarchical IRQ support!
+ * this requires fixing the root irqchip to be hierarchical.
+ */
+ girq->parent_handler = ep93xx_gpio_f_irq_handler;
+ girq->num_parents = 8;
+ girq->parents = devm_kcalloc(dev, girq->num_parents,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ /* Pick resources 1..8 for these IRQs */
+ for (i = 0; i < girq->num_parents; i++) {
+ girq->parents[i] = platform_get_irq(pdev, i + 1);
+ gpio_irq = bank->irq_base + i;
+ irq_set_chip_data(gpio_irq, &epg->gc[5]);
+ irq_set_chip_and_handler(gpio_irq,
+ girq->chip,
+ handle_level_irq);
+ irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST);
+ }
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
+ girq->first = bank->irq_base;
+ }
return devm_gpiochip_add_data(dev, gc, epg);
}
@@ -404,19 +431,14 @@ static int ep93xx_gpio_probe(struct platform_device *pdev)
return PTR_ERR(epg->base);
for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) {
- struct gpio_chip *gc = &epg->gc[i];
+ struct ep93xx_gpio_chip *gc = &epg->gc[i];
struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i];
- if (ep93xx_gpio_add_bank(gc, &pdev->dev, epg, bank))
+ if (ep93xx_gpio_add_bank(gc, pdev, epg, bank))
dev_warn(&pdev->dev, "Unable to add gpio bank %s\n",
bank->label);
- /* Only bank F has especially funky IRQ handling */
- if (i == 5)
- gc->to_irq = ep93xx_gpio_f_to_irq;
}
- ep93xx_gpio_init_irq(pdev, epg);
-
return 0;
}
diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c
index fae327d5b06e..d37de78247a6 100644
--- a/drivers/gpio/gpio-exar.c
+++ b/drivers/gpio/gpio-exar.c
@@ -4,14 +4,17 @@
*
* Copyright (C) 2015 Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
*/
+
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
+#include <linux/idr.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#define EXAR_OFFSET_MPIOLVL_LO 0x90
#define EXAR_OFFSET_MPIOSEL_LO 0x93
@@ -24,98 +27,107 @@ static DEFINE_IDA(ida_index);
struct exar_gpio_chip {
struct gpio_chip gpio_chip;
- struct mutex lock;
+ struct regmap *regmap;
int index;
- void __iomem *regs;
char name[20];
unsigned int first_pin;
};
-static void exar_update(struct gpio_chip *chip, unsigned int reg, int val,
- unsigned int offset)
+static unsigned int
+exar_offset_to_sel_addr(struct exar_gpio_chip *exar_gpio, unsigned int offset)
{
- struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
- int temp;
-
- mutex_lock(&exar_gpio->lock);
- temp = readb(exar_gpio->regs + reg);
- temp &= ~BIT(offset);
- if (val)
- temp |= BIT(offset);
- writeb(temp, exar_gpio->regs + reg);
- mutex_unlock(&exar_gpio->lock);
+ return (offset + exar_gpio->first_pin) / 8 ? EXAR_OFFSET_MPIOSEL_HI
+ : EXAR_OFFSET_MPIOSEL_LO;
}
-static int exar_set_direction(struct gpio_chip *chip, int direction,
- unsigned int offset)
+static unsigned int
+exar_offset_to_lvl_addr(struct exar_gpio_chip *exar_gpio, unsigned int offset)
{
- struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
- unsigned int addr = (offset + exar_gpio->first_pin) / 8 ?
- EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO;
- unsigned int bit = (offset + exar_gpio->first_pin) % 8;
-
- exar_update(chip, addr, direction, bit);
- return 0;
+ return (offset + exar_gpio->first_pin) / 8 ? EXAR_OFFSET_MPIOLVL_HI
+ : EXAR_OFFSET_MPIOLVL_LO;
}
-static int exar_get(struct gpio_chip *chip, unsigned int reg)
+static unsigned int
+exar_offset_to_bit(struct exar_gpio_chip *exar_gpio, unsigned int offset)
{
- struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
- int value;
-
- mutex_lock(&exar_gpio->lock);
- value = readb(exar_gpio->regs + reg);
- mutex_unlock(&exar_gpio->lock);
-
- return value;
+ return (offset + exar_gpio->first_pin) % 8;
}
static int exar_get_direction(struct gpio_chip *chip, unsigned int offset)
{
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
- unsigned int addr = (offset + exar_gpio->first_pin) / 8 ?
- EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO;
- unsigned int bit = (offset + exar_gpio->first_pin) % 8;
+ unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset);
+ unsigned int bit = exar_offset_to_bit(exar_gpio, offset);
+
+ if (regmap_test_bits(exar_gpio->regmap, addr, BIT(bit)))
+ return GPIO_LINE_DIRECTION_IN;
- return !!(exar_get(chip, addr) & BIT(bit));
+ return GPIO_LINE_DIRECTION_OUT;
}
static int exar_get_value(struct gpio_chip *chip, unsigned int offset)
{
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
- unsigned int addr = (offset + exar_gpio->first_pin) / 8 ?
- EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO;
- unsigned int bit = (offset + exar_gpio->first_pin) % 8;
+ unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset);
+ unsigned int bit = exar_offset_to_bit(exar_gpio, offset);
- return !!(exar_get(chip, addr) & BIT(bit));
+ return !!(regmap_test_bits(exar_gpio->regmap, addr, BIT(bit)));
}
static void exar_set_value(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
- unsigned int addr = (offset + exar_gpio->first_pin) / 8 ?
- EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO;
- unsigned int bit = (offset + exar_gpio->first_pin) % 8;
+ unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset);
+ unsigned int bit = exar_offset_to_bit(exar_gpio, offset);
- exar_update(chip, addr, value, bit);
+ if (value)
+ regmap_set_bits(exar_gpio->regmap, addr, BIT(bit));
+ else
+ regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit));
}
static int exar_direction_output(struct gpio_chip *chip, unsigned int offset,
int value)
{
+ struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
+ unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset);
+ unsigned int bit = exar_offset_to_bit(exar_gpio, offset);
+
exar_set_value(chip, offset, value);
- return exar_set_direction(chip, 0, offset);
+ regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit));
+
+ return 0;
}
static int exar_direction_input(struct gpio_chip *chip, unsigned int offset)
{
- return exar_set_direction(chip, 1, offset);
+ struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
+ unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset);
+ unsigned int bit = exar_offset_to_bit(exar_gpio, offset);
+
+ regmap_set_bits(exar_gpio->regmap, addr, BIT(bit));
+
+ return 0;
+}
+
+static void exar_devm_ida_free(void *data)
+{
+ struct exar_gpio_chip *exar_gpio = data;
+
+ ida_free(&ida_index, exar_gpio->index);
}
+static const struct regmap_config exar_regmap_config = {
+ .name = "exar-gpio",
+ .reg_bits = 16,
+ .val_bits = 8,
+};
+
static int gpio_exar_probe(struct platform_device *pdev)
{
- struct pci_dev *pcidev = to_pci_dev(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct pci_dev *pcidev = to_pci_dev(dev->parent);
struct exar_gpio_chip *exar_gpio;
u32 first_pin, ngpios;
void __iomem *p;
@@ -129,28 +141,37 @@ static int gpio_exar_probe(struct platform_device *pdev)
if (!p)
return -ENOMEM;
- ret = device_property_read_u32(&pdev->dev, "exar,first-pin",
- &first_pin);
+ ret = device_property_read_u32(dev, "exar,first-pin", &first_pin);
if (ret)
return ret;
- ret = device_property_read_u32(&pdev->dev, "ngpios", &ngpios);
+ ret = device_property_read_u32(dev, "ngpios", &ngpios);
if (ret)
return ret;
- exar_gpio = devm_kzalloc(&pdev->dev, sizeof(*exar_gpio), GFP_KERNEL);
+ exar_gpio = devm_kzalloc(dev, sizeof(*exar_gpio), GFP_KERNEL);
if (!exar_gpio)
return -ENOMEM;
- mutex_init(&exar_gpio->lock);
+ /*
+ * We don't need to check the return values of mmio regmap operations (unless
+ * the regmap has a clock attached which is not the case here).
+ */
+ exar_gpio->regmap = devm_regmap_init_mmio(dev, p, &exar_regmap_config);
+ if (IS_ERR(exar_gpio->regmap))
+ return PTR_ERR(exar_gpio->regmap);
- index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL);
+ index = ida_alloc(&ida_index, GFP_KERNEL);
if (index < 0)
- goto err_destroy;
+ return index;
+
+ ret = devm_add_action_or_reset(dev, exar_devm_ida_free, exar_gpio);
+ if (ret)
+ return ret;
sprintf(exar_gpio->name, "exar_gpio%d", index);
exar_gpio->gpio_chip.label = exar_gpio->name;
- exar_gpio->gpio_chip.parent = &pdev->dev;
+ exar_gpio->gpio_chip.parent = dev;
exar_gpio->gpio_chip.direction_output = exar_direction_output;
exar_gpio->gpio_chip.direction_input = exar_direction_input;
exar_gpio->gpio_chip.get_direction = exar_get_direction;
@@ -158,38 +179,20 @@ static int gpio_exar_probe(struct platform_device *pdev)
exar_gpio->gpio_chip.set = exar_set_value;
exar_gpio->gpio_chip.base = -1;
exar_gpio->gpio_chip.ngpio = ngpios;
- exar_gpio->regs = p;
exar_gpio->index = index;
exar_gpio->first_pin = first_pin;
- ret = devm_gpiochip_add_data(&pdev->dev,
- &exar_gpio->gpio_chip, exar_gpio);
+ ret = devm_gpiochip_add_data(dev, &exar_gpio->gpio_chip, exar_gpio);
if (ret)
- goto err_destroy;
+ return ret;
platform_set_drvdata(pdev, exar_gpio);
return 0;
-
-err_destroy:
- ida_simple_remove(&ida_index, index);
- mutex_destroy(&exar_gpio->lock);
- return ret;
-}
-
-static int gpio_exar_remove(struct platform_device *pdev)
-{
- struct exar_gpio_chip *exar_gpio = platform_get_drvdata(pdev);
-
- ida_simple_remove(&ida_index, exar_gpio->index);
- mutex_destroy(&exar_gpio->lock);
-
- return 0;
}
static struct platform_driver gpio_exar_driver = {
.probe = gpio_exar_probe,
- .remove = gpio_exar_remove,
.driver = {
.name = DRIVER_NAME,
},
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
index fdc639f856f1..18a3147f5a42 100644
--- a/drivers/gpio/gpio-f7188x.c
+++ b/drivers/gpio/gpio-f7188x.c
@@ -36,9 +36,19 @@
#define SIO_F71889A_ID 0x1005 /* F71889A chipset ID */
#define SIO_F81866_ID 0x1010 /* F81866 chipset ID */
#define SIO_F81804_ID 0x1502 /* F81804 chipset ID, same for f81966 */
-
-
-enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866, f81804 };
+#define SIO_F81865_ID 0x0704 /* F81865 chipset ID */
+
+
+enum chips {
+ f71869,
+ f71869a,
+ f71882fg,
+ f71889a,
+ f71889f,
+ f81866,
+ f81804,
+ f81865,
+};
static const char * const f7188x_names[] = {
"f71869",
@@ -48,6 +58,7 @@ static const char * const f7188x_names[] = {
"f71889f",
"f81866",
"f81804",
+ "f81865",
};
struct f7188x_sio {
@@ -233,6 +244,15 @@ static struct f7188x_gpio_bank f81804_gpio_bank[] = {
F7188X_GPIO_BANK(90, 8, 0x98),
};
+static struct f7188x_gpio_bank f81865_gpio_bank[] = {
+ F7188X_GPIO_BANK(0, 8, 0xF0),
+ F7188X_GPIO_BANK(10, 8, 0xE0),
+ F7188X_GPIO_BANK(20, 8, 0xD0),
+ F7188X_GPIO_BANK(30, 8, 0xC0),
+ F7188X_GPIO_BANK(40, 8, 0xB0),
+ F7188X_GPIO_BANK(50, 8, 0xA0),
+ F7188X_GPIO_BANK(60, 5, 0x90),
+};
static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
@@ -250,7 +270,10 @@ static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
superio_exit(sio->addr);
- return !(dir & 1 << offset);
+ if (dir & 1 << offset)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
@@ -422,6 +445,10 @@ static int f7188x_gpio_probe(struct platform_device *pdev)
data->nr_bank = ARRAY_SIZE(f81804_gpio_bank);
data->bank = f81804_gpio_bank;
break;
+ case f81865:
+ data->nr_bank = ARRAY_SIZE(f81865_gpio_bank);
+ data->bank = f81865_gpio_bank;
+ break;
default:
return -ENODEV;
}
@@ -487,6 +514,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio)
case SIO_F81804_ID:
sio->type = f81804;
break;
+ case SIO_F81865_ID:
+ sio->type = f81865;
+ break;
default:
pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
goto err;
diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c
index 250e71f3e688..f422c3e129a0 100644
--- a/drivers/gpio/gpio-ftgpio010.c
+++ b/drivers/gpio/gpio-ftgpio010.c
@@ -149,8 +149,7 @@ static void ftgpio_gpio_irq_handler(struct irq_desc *desc)
stat = readl(g->base + GPIO_INT_STAT_RAW);
if (stat)
for_each_set_bit(offset, &stat, gc->ngpio)
- generic_handle_irq(irq_find_mapping(gc->irq.domain,
- offset));
+ generic_handle_domain_irq(gc->irq.domain, offset);
chained_irq_exit(irqchip, desc);
}
@@ -193,7 +192,7 @@ static int ftgpio_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
if (val == deb_div) {
/*
* The debounce timer happens to already be set to the
- * desireable value, what a coincidence! We can just enable
+ * desirable value, what a coincidence! We can just enable
* debounce on this GPIO line and return. This happens more
* often than you think, for example when all GPIO keys
* on a system are requesting the same debounce interval.
@@ -290,16 +289,14 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
GFP_KERNEL);
- if (!girq->parents)
- return -ENOMEM;
+ if (!girq->parents) {
+ ret = -ENOMEM;
+ goto dis_clk;
+ }
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
girq->parents[0] = irq;
- ret = devm_gpiochip_add_data(dev, &g->gc, g);
- if (ret)
- goto dis_clk;
-
/* Disable, unmask and clear all interrupts */
writel(0x0, g->base + GPIO_INT_EN);
writel(0x0, g->base + GPIO_INT_MASK);
@@ -308,14 +305,18 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
/* Clear any use of debounce */
writel(0x0, g->base + GPIO_DEBOUNCE_EN);
+ ret = devm_gpiochip_add_data(dev, &g->gc, g);
+ if (ret)
+ goto dis_clk;
+
platform_set_drvdata(pdev, g);
dev_info(dev, "FTGPIO010 @%p registered\n", g->base);
return 0;
dis_clk:
- if (!IS_ERR(g->clk))
- clk_disable_unprepare(g->clk);
+ clk_disable_unprepare(g->clk);
+
return ret;
}
@@ -323,8 +324,8 @@ static int ftgpio_gpio_remove(struct platform_device *pdev)
{
struct ftgpio_gpio *g = platform_get_drvdata(pdev);
- if (!IS_ERR(g->clk))
- clk_disable_unprepare(g->clk);
+ clk_disable_unprepare(g->clk);
+
return 0;
}
diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c
index 636952769bc8..f6a3de99f7db 100644
--- a/drivers/gpio/gpio-ge.c
+++ b/drivers/gpio/gpio-ge.c
@@ -82,7 +82,6 @@ static int __init gef_gpio_probe(struct platform_device *pdev)
gc->base = -1;
gc->ngpio = (u16)(uintptr_t)of_device_get_match_data(&pdev->dev);
gc->of_gpio_n_cells = 2;
- gc->of_node = pdev->dev.of_node;
/* This function adds a memory mapped GPIO chip */
ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL);
diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c
index 78a1db24e931..097a06463d01 100644
--- a/drivers/gpio/gpio-gpio-mm.c
+++ b/drivers/gpio/gpio-gpio-mm.c
@@ -42,7 +42,7 @@ struct gpiomm_gpio {
unsigned char out_state[6];
unsigned char control[2];
spinlock_t lock;
- unsigned int base;
+ void __iomem *base;
};
static int gpiomm_gpio_get_direction(struct gpio_chip *chip,
@@ -52,7 +52,10 @@ static int gpiomm_gpio_get_direction(struct gpio_chip *chip,
const unsigned int port = offset / 8;
const unsigned int mask = BIT(offset % 8);
- return !!(gpiommgpio->io_state[port] & mask);
+ if (gpiommgpio->io_state[port] & mask)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int gpiomm_gpio_direction_input(struct gpio_chip *chip,
@@ -61,7 +64,6 @@ static int gpiomm_gpio_direction_input(struct gpio_chip *chip,
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
const unsigned int io_port = offset / 8;
const unsigned int control_port = io_port / 3;
- const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4;
unsigned long flags;
unsigned int control;
@@ -86,7 +88,7 @@ static int gpiomm_gpio_direction_input(struct gpio_chip *chip,
}
control = BIT(7) | gpiommgpio->control[control_port];
- outb(control, control_addr);
+ iowrite8(control, gpiommgpio->base + 3 + control_port*4);
spin_unlock_irqrestore(&gpiommgpio->lock, flags);
@@ -100,7 +102,6 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip,
const unsigned int io_port = offset / 8;
const unsigned int control_port = io_port / 3;
const unsigned int mask = BIT(offset % 8);
- const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4;
const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port;
unsigned long flags;
unsigned int control;
@@ -131,9 +132,9 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip,
gpiommgpio->out_state[io_port] &= ~mask;
control = BIT(7) | gpiommgpio->control[control_port];
- outb(control, control_addr);
+ iowrite8(control, gpiommgpio->base + 3 + control_port*4);
- outb(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port);
+ iowrite8(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port);
spin_unlock_irqrestore(&gpiommgpio->lock, flags);
@@ -157,53 +158,32 @@ static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
return -EINVAL;
}
- port_state = inb(gpiommgpio->base + in_port);
+ port_state = ioread8(gpiommgpio->base + in_port);
spin_unlock_irqrestore(&gpiommgpio->lock, flags);
return !!(port_state & mask);
}
+static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+
static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
- size_t i;
- static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
- const unsigned int gpio_reg_size = 8;
- unsigned int bits_offset;
- size_t word_index;
- unsigned int word_offset;
- unsigned long word_mask;
- const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long offset;
+ unsigned long gpio_mask;
+ void __iomem *port_addr;
unsigned long port_state;
/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);
- /* get bits are evaluated a gpio port register at a time */
- for (i = 0; i < ARRAY_SIZE(ports); i++) {
- /* gpio offset in bits array */
- bits_offset = i * gpio_reg_size;
-
- /* word index for bits array */
- word_index = BIT_WORD(bits_offset);
-
- /* gpio offset within current word of bits array */
- word_offset = bits_offset % BITS_PER_LONG;
-
- /* mask of get bits for current gpio within current word */
- word_mask = mask[word_index] & (port_mask << word_offset);
- if (!word_mask) {
- /* no get bits in this port so skip to next one */
- continue;
- }
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ port_addr = gpiommgpio->base + ports[offset / 8];
+ port_state = ioread8(port_addr) & gpio_mask;
- /* read bits from current gpio port */
- port_state = inb(gpiommgpio->base + ports[i]);
-
- /* store acquired bits at respective bits array offset */
- bits[word_index] |= (port_state << word_offset) & word_mask;
+ bitmap_set_value8(bits, port_state, offset);
}
return 0;
@@ -225,7 +205,7 @@ static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
else
gpiommgpio->out_state[port] &= ~mask;
- outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
+ iowrite8(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
spin_unlock_irqrestore(&gpiommgpio->lock, flags);
}
@@ -234,37 +214,27 @@ static void gpiomm_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
- unsigned int i;
- const unsigned int gpio_reg_size = 8;
- unsigned int port;
- unsigned int out_port;
- unsigned int bitmask;
+ unsigned long offset;
+ unsigned long gpio_mask;
+ size_t index;
+ void __iomem *port_addr;
+ unsigned long bitmask;
unsigned long flags;
- /* set bits are evaluated a gpio register size at a time */
- for (i = 0; i < chip->ngpio; i += gpio_reg_size) {
- /* no more set bits in this mask word; skip to the next word */
- if (!mask[BIT_WORD(i)]) {
- i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size;
- continue;
- }
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ index = offset / 8;
+ port_addr = gpiommgpio->base + ports[index];
- port = i / gpio_reg_size;
- out_port = (port > 2) ? port + 1 : port;
- bitmask = mask[BIT_WORD(i)] & bits[BIT_WORD(i)];
+ bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
spin_lock_irqsave(&gpiommgpio->lock, flags);
/* update output state data and set device gpio register */
- gpiommgpio->out_state[port] &= ~mask[BIT_WORD(i)];
- gpiommgpio->out_state[port] |= bitmask;
- outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
+ gpiommgpio->out_state[index] &= ~gpio_mask;
+ gpiommgpio->out_state[index] |= bitmask;
+ iowrite8(gpiommgpio->out_state[index], port_addr);
spin_unlock_irqrestore(&gpiommgpio->lock, flags);
-
- /* prepare for next gpio register set */
- mask[BIT_WORD(i)] >>= gpio_reg_size;
- bits[BIT_WORD(i)] >>= gpio_reg_size;
}
}
@@ -296,6 +266,10 @@ static int gpiomm_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
+ gpiommgpio->base = devm_ioport_map(dev, base[id], GPIOMM_EXTENT);
+ if (!gpiommgpio->base)
+ return -ENOMEM;
+
gpiommgpio->chip.label = name;
gpiommgpio->chip.parent = dev;
gpiommgpio->chip.owner = THIS_MODULE;
@@ -309,7 +283,6 @@ static int gpiomm_probe(struct device *dev, unsigned int id)
gpiommgpio->chip.get_multiple = gpiomm_gpio_get_multiple;
gpiommgpio->chip.set = gpiomm_gpio_set;
gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
- gpiommgpio->base = base[id];
spin_lock_init(&gpiommgpio->lock);
@@ -320,14 +293,14 @@ static int gpiomm_probe(struct device *dev, unsigned int id)
}
/* initialize all GPIO as output */
- outb(0x80, base[id] + 3);
- outb(0x00, base[id]);
- outb(0x00, base[id] + 1);
- outb(0x00, base[id] + 2);
- outb(0x80, base[id] + 7);
- outb(0x00, base[id] + 4);
- outb(0x00, base[id] + 5);
- outb(0x00, base[id] + 6);
+ iowrite8(0x80, gpiommgpio->base + 3);
+ iowrite8(0x00, gpiommgpio->base);
+ iowrite8(0x00, gpiommgpio->base + 1);
+ iowrite8(0x00, gpiommgpio->base + 2);
+ iowrite8(0x80, gpiommgpio->base + 7);
+ iowrite8(0x00, gpiommgpio->base + 4);
+ iowrite8(0x00, gpiommgpio->base + 5);
+ iowrite8(0x00, gpiommgpio->base + 6);
return 0;
}
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c
index 0937b605e134..df563616f943 100644
--- a/drivers/gpio/gpio-grgpio.c
+++ b/drivers/gpio/gpio-grgpio.c
@@ -145,7 +145,7 @@ static int grgpio_irq_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
ipol = priv->gc.read_reg(priv->regs + GRGPIO_IPOL) & ~mask;
iedge = priv->gc.read_reg(priv->regs + GRGPIO_IEDGE) & ~mask;
@@ -153,7 +153,7 @@ static int grgpio_irq_set_type(struct irq_data *d, unsigned int type)
priv->gc.write_reg(priv->regs + GRGPIO_IPOL, ipol | pol);
priv->gc.write_reg(priv->regs + GRGPIO_IEDGE, iedge | edge);
- spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
return 0;
}
@@ -164,11 +164,11 @@ static void grgpio_irq_mask(struct irq_data *d)
int offset = d->hwirq;
unsigned long flags;
- spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
grgpio_set_imask(priv, offset, 0);
- spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
}
static void grgpio_irq_unmask(struct irq_data *d)
@@ -177,11 +177,11 @@ static void grgpio_irq_unmask(struct irq_data *d)
int offset = d->hwirq;
unsigned long flags;
- spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
grgpio_set_imask(priv, offset, 1);
- spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
}
static struct irq_chip grgpio_irq_chip = {
@@ -199,7 +199,7 @@ static irqreturn_t grgpio_irq_handler(int irq, void *dev)
int i;
int match = 0;
- spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
/*
* For each gpio line, call its interrupt handler if it its underlying
@@ -215,7 +215,7 @@ static irqreturn_t grgpio_irq_handler(int irq, void *dev)
}
}
- spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
if (!match)
dev_warn(priv->dev, "No gpio line matched irq %d\n", irq);
@@ -247,27 +247,26 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
dev_dbg(priv->dev, "Mapping irq %d for gpio line %d\n",
irq, offset);
- spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
/* Request underlying irq if not already requested */
lirq->irq = irq;
uirq = &priv->uirqs[lirq->index];
if (uirq->refcnt == 0) {
+ raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
ret = request_irq(uirq->uirq, grgpio_irq_handler, 0,
dev_name(priv->dev), priv);
if (ret) {
dev_err(priv->dev,
"Could not request underlying irq %d\n",
uirq->uirq);
-
- spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
-
return ret;
}
+ raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
}
uirq->refcnt++;
- spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
/* Setup irq */
irq_set_chip_data(irq, priv);
@@ -291,7 +290,7 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
- spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
/* Free underlying irq if last user unmapped */
index = -1;
@@ -309,11 +308,14 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
if (index >= 0) {
uirq = &priv->uirqs[lirq->index];
uirq->refcnt--;
- if (uirq->refcnt == 0)
+ if (uirq->refcnt == 0) {
+ raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
free_irq(uirq->uirq, priv);
+ return;
+ }
}
- spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
}
static const struct irq_domain_ops grgpio_irq_domain_ops = {
@@ -356,7 +358,6 @@ static int grgpio_probe(struct platform_device *ofdev)
priv->imask = gc->read_reg(regs + GRGPIO_IMASK);
priv->dev = &ofdev->dev;
- gc->of_node = np;
gc->owner = THIS_MODULE;
gc->to_irq = grgpio_to_irq;
gc->label = devm_kasprintf(&ofdev->dev, GFP_KERNEL, "%pOF", np);
@@ -408,8 +409,6 @@ static int grgpio_probe(struct platform_device *ofdev)
* Continue without irq functionality for that
* gpio line
*/
- dev_err(priv->dev,
- "Failed to get irq for offset %d\n", i);
continue;
}
priv->uirqs[lirq->index].uirq = ret;
@@ -435,12 +434,9 @@ static int grgpio_probe(struct platform_device *ofdev)
static int grgpio_remove(struct platform_device *ofdev)
{
struct grgpio_priv *priv = platform_get_drvdata(ofdev);
- unsigned long flags;
int i;
int ret = 0;
- spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
-
if (priv->domain) {
for (i = 0; i < GRGPIO_MAX_NGPIO; i++) {
if (priv->uirqs[i].refcnt != 0) {
@@ -456,8 +452,6 @@ static int grgpio_remove(struct platform_device *ofdev)
irq_domain_remove(priv->domain);
out:
- spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
-
return ret;
}
diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c
index 242112ff60ee..2109803ffb38 100644
--- a/drivers/gpio/gpio-gw-pld.c
+++ b/drivers/gpio/gpio-gw-pld.c
@@ -71,7 +71,6 @@ static int gw_pld_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
- struct device_node *np = dev->of_node;
struct gw_pld *gw;
int ret;
@@ -82,7 +81,6 @@ static int gw_pld_probe(struct i2c_client *client,
gw->chip.base = -1;
gw->chip.can_sleep = true;
gw->chip.parent = dev;
- gw->chip.of_node = np;
gw->chip.owner = THIS_MODULE;
gw->chip.label = dev_name(dev);
gw->chip.ngpio = 8;
diff --git a/drivers/gpio/gpio-hisi.c b/drivers/gpio/gpio-hisi.c
new file mode 100644
index 000000000000..3caabef5c7a2
--- /dev/null
+++ b/drivers/gpio/gpio-hisi.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2020 HiSilicon Limited. */
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#define HISI_GPIO_SWPORT_DR_SET_WX 0x000
+#define HISI_GPIO_SWPORT_DR_CLR_WX 0x004
+#define HISI_GPIO_SWPORT_DDR_SET_WX 0x010
+#define HISI_GPIO_SWPORT_DDR_CLR_WX 0x014
+#define HISI_GPIO_SWPORT_DDR_ST_WX 0x018
+#define HISI_GPIO_INTEN_SET_WX 0x020
+#define HISI_GPIO_INTEN_CLR_WX 0x024
+#define HISI_GPIO_INTMASK_SET_WX 0x030
+#define HISI_GPIO_INTMASK_CLR_WX 0x034
+#define HISI_GPIO_INTTYPE_EDGE_SET_WX 0x040
+#define HISI_GPIO_INTTYPE_EDGE_CLR_WX 0x044
+#define HISI_GPIO_INT_POLARITY_SET_WX 0x050
+#define HISI_GPIO_INT_POLARITY_CLR_WX 0x054
+#define HISI_GPIO_DEBOUNCE_SET_WX 0x060
+#define HISI_GPIO_DEBOUNCE_CLR_WX 0x064
+#define HISI_GPIO_INTSTATUS_WX 0x070
+#define HISI_GPIO_PORTA_EOI_WX 0x078
+#define HISI_GPIO_EXT_PORT_WX 0x080
+#define HISI_GPIO_INTCOMB_MASK_WX 0x0a0
+#define HISI_GPIO_INT_DEDGE_SET 0x0b0
+#define HISI_GPIO_INT_DEDGE_CLR 0x0b4
+#define HISI_GPIO_INT_DEDGE_ST 0x0b8
+
+#define HISI_GPIO_LINE_NUM_MAX 32
+#define HISI_GPIO_DRIVER_NAME "gpio-hisi"
+
+struct hisi_gpio {
+ struct gpio_chip chip;
+ struct device *dev;
+ void __iomem *reg_base;
+ unsigned int line_num;
+ struct irq_chip irq_chip;
+ int irq;
+};
+
+static inline u32 hisi_gpio_read_reg(struct gpio_chip *chip,
+ unsigned int off)
+{
+ struct hisi_gpio *hisi_gpio =
+ container_of(chip, struct hisi_gpio, chip);
+ void __iomem *reg = hisi_gpio->reg_base + off;
+
+ return readl(reg);
+}
+
+static inline void hisi_gpio_write_reg(struct gpio_chip *chip,
+ unsigned int off, u32 val)
+{
+ struct hisi_gpio *hisi_gpio =
+ container_of(chip, struct hisi_gpio, chip);
+ void __iomem *reg = hisi_gpio->reg_base + off;
+
+ writel(val, reg);
+}
+
+static void hisi_gpio_set_debounce(struct gpio_chip *chip, unsigned int off,
+ u32 debounce)
+{
+ if (debounce)
+ hisi_gpio_write_reg(chip, HISI_GPIO_DEBOUNCE_SET_WX, BIT(off));
+ else
+ hisi_gpio_write_reg(chip, HISI_GPIO_DEBOUNCE_CLR_WX, BIT(off));
+}
+
+static int hisi_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ u32 config_para = pinconf_to_config_param(config);
+ u32 config_arg;
+
+ switch (config_para) {
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ config_arg = pinconf_to_config_argument(config);
+ hisi_gpio_set_debounce(chip, offset, config_arg);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static void hisi_gpio_set_ack(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ hisi_gpio_write_reg(chip, HISI_GPIO_PORTA_EOI_WX, BIT(irqd_to_hwirq(d)));
+}
+
+static void hisi_gpio_irq_set_mask(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTMASK_SET_WX, BIT(irqd_to_hwirq(d)));
+}
+
+static void hisi_gpio_irq_clr_mask(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTMASK_CLR_WX, BIT(irqd_to_hwirq(d)));
+}
+
+static int hisi_gpio_irq_set_type(struct irq_data *d, u32 type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int mask = BIT(irqd_to_hwirq(d));
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ hisi_gpio_write_reg(chip, HISI_GPIO_INT_DEDGE_SET, mask);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTTYPE_EDGE_SET_WX, mask);
+ hisi_gpio_write_reg(chip, HISI_GPIO_INT_POLARITY_SET_WX, mask);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTTYPE_EDGE_SET_WX, mask);
+ hisi_gpio_write_reg(chip, HISI_GPIO_INT_POLARITY_CLR_WX, mask);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTTYPE_EDGE_CLR_WX, mask);
+ hisi_gpio_write_reg(chip, HISI_GPIO_INT_POLARITY_SET_WX, mask);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTTYPE_EDGE_CLR_WX, mask);
+ hisi_gpio_write_reg(chip, HISI_GPIO_INT_POLARITY_CLR_WX, mask);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * The dual-edge interrupt and other interrupt's registers do not
+ * take effect at the same time. The registers of the two-edge
+ * interrupts have higher priorities, the configuration of
+ * the dual-edge interrupts must be disabled before the configuration
+ * of other kind of interrupts.
+ */
+ if (type != IRQ_TYPE_EDGE_BOTH) {
+ unsigned int both = hisi_gpio_read_reg(chip, HISI_GPIO_INT_DEDGE_ST);
+
+ if (both & mask)
+ hisi_gpio_write_reg(chip, HISI_GPIO_INT_DEDGE_CLR, mask);
+ }
+
+ if (type & IRQ_TYPE_LEVEL_MASK)
+ irq_set_handler_locked(d, handle_level_irq);
+ else if (type & IRQ_TYPE_EDGE_BOTH)
+ irq_set_handler_locked(d, handle_edge_irq);
+
+ return 0;
+}
+
+static void hisi_gpio_irq_enable(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ hisi_gpio_irq_clr_mask(d);
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTEN_SET_WX, BIT(irqd_to_hwirq(d)));
+}
+
+static void hisi_gpio_irq_disable(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ hisi_gpio_irq_set_mask(d);
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTEN_CLR_WX, BIT(irqd_to_hwirq(d)));
+}
+
+static void hisi_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct hisi_gpio *hisi_gpio = irq_desc_get_handler_data(desc);
+ unsigned long irq_msk = hisi_gpio_read_reg(&hisi_gpio->chip,
+ HISI_GPIO_INTSTATUS_WX);
+ struct irq_chip *irq_c = irq_desc_get_chip(desc);
+ int hwirq;
+
+ chained_irq_enter(irq_c, desc);
+ for_each_set_bit(hwirq, &irq_msk, HISI_GPIO_LINE_NUM_MAX)
+ generic_handle_domain_irq(hisi_gpio->chip.irq.domain,
+ hwirq);
+ chained_irq_exit(irq_c, desc);
+}
+
+static void hisi_gpio_init_irq(struct hisi_gpio *hisi_gpio)
+{
+ struct gpio_chip *chip = &hisi_gpio->chip;
+ struct gpio_irq_chip *girq_chip = &chip->irq;
+
+ /* Set hooks for irq_chip */
+ hisi_gpio->irq_chip.irq_ack = hisi_gpio_set_ack;
+ hisi_gpio->irq_chip.irq_mask = hisi_gpio_irq_set_mask;
+ hisi_gpio->irq_chip.irq_unmask = hisi_gpio_irq_clr_mask;
+ hisi_gpio->irq_chip.irq_set_type = hisi_gpio_irq_set_type;
+ hisi_gpio->irq_chip.irq_enable = hisi_gpio_irq_enable;
+ hisi_gpio->irq_chip.irq_disable = hisi_gpio_irq_disable;
+
+ girq_chip->chip = &hisi_gpio->irq_chip;
+ girq_chip->default_type = IRQ_TYPE_NONE;
+ girq_chip->num_parents = 1;
+ girq_chip->parents = &hisi_gpio->irq;
+ girq_chip->parent_handler = hisi_gpio_irq_handler;
+ girq_chip->parent_handler_data = hisi_gpio;
+
+ /* Clear Mask of GPIO controller combine IRQ */
+ hisi_gpio_write_reg(chip, HISI_GPIO_INTCOMB_MASK_WX, 1);
+}
+
+static const struct acpi_device_id hisi_gpio_acpi_match[] = {
+ {"HISI0184", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, hisi_gpio_acpi_match);
+
+static void hisi_gpio_get_pdata(struct device *dev,
+ struct hisi_gpio *hisi_gpio)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fwnode_handle *fwnode;
+ int idx = 0;
+
+ device_for_each_child_node(dev, fwnode) {
+ /* Cycle for once, no need for an array to save line_num */
+ if (fwnode_property_read_u32(fwnode, "ngpios",
+ &hisi_gpio->line_num)) {
+ dev_err(dev,
+ "failed to get number of lines for port%d and use default value instead\n",
+ idx);
+ hisi_gpio->line_num = HISI_GPIO_LINE_NUM_MAX;
+ }
+
+ if (WARN_ON(hisi_gpio->line_num > HISI_GPIO_LINE_NUM_MAX))
+ hisi_gpio->line_num = HISI_GPIO_LINE_NUM_MAX;
+
+ hisi_gpio->irq = platform_get_irq(pdev, idx);
+
+ dev_info(dev,
+ "get hisi_gpio[%d] with %d lines\n", idx,
+ hisi_gpio->line_num);
+
+ idx++;
+ }
+}
+
+static int hisi_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hisi_gpio *hisi_gpio;
+ int port_num;
+ int ret;
+
+ /*
+ * One GPIO controller own one port currently,
+ * if we get more from ACPI table, return error.
+ */
+ port_num = device_get_child_node_count(dev);
+ if (WARN_ON(port_num != 1))
+ return -ENODEV;
+
+ hisi_gpio = devm_kzalloc(dev, sizeof(*hisi_gpio), GFP_KERNEL);
+ if (!hisi_gpio)
+ return -ENOMEM;
+
+ hisi_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hisi_gpio->reg_base))
+ return PTR_ERR(hisi_gpio->reg_base);
+
+ hisi_gpio_get_pdata(dev, hisi_gpio);
+
+ hisi_gpio->dev = dev;
+
+ ret = bgpio_init(&hisi_gpio->chip, hisi_gpio->dev, 0x4,
+ hisi_gpio->reg_base + HISI_GPIO_EXT_PORT_WX,
+ hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_SET_WX,
+ hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_CLR_WX,
+ hisi_gpio->reg_base + HISI_GPIO_SWPORT_DDR_SET_WX,
+ hisi_gpio->reg_base + HISI_GPIO_SWPORT_DDR_CLR_WX,
+ BGPIOF_NO_SET_ON_INPUT);
+ if (ret) {
+ dev_err(dev, "failed to init, ret = %d\n", ret);
+ return ret;
+ }
+
+ hisi_gpio->chip.set_config = hisi_gpio_set_config;
+ hisi_gpio->chip.ngpio = hisi_gpio->line_num;
+ hisi_gpio->chip.bgpio_dir_unreadable = 1;
+ hisi_gpio->chip.base = -1;
+
+ if (hisi_gpio->irq > 0)
+ hisi_gpio_init_irq(hisi_gpio);
+
+ ret = devm_gpiochip_add_data(dev, &hisi_gpio->chip, hisi_gpio);
+ if (ret) {
+ dev_err(dev, "failed to register gpiochip, ret = %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver hisi_gpio_driver = {
+ .driver = {
+ .name = HISI_GPIO_DRIVER_NAME,
+ .acpi_match_table = hisi_gpio_acpi_match,
+ },
+ .probe = hisi_gpio_probe,
+};
+
+module_platform_driver(hisi_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Luo Jiaxing <luojiaxing@huawei.com>");
+MODULE_DESCRIPTION("HiSilicon GPIO controller driver");
+MODULE_ALIAS("platform:" HISI_GPIO_DRIVER_NAME);
diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c
index e5fa00f8145f..4e13e937f832 100644
--- a/drivers/gpio/gpio-hlwd.c
+++ b/drivers/gpio/gpio-hlwd.c
@@ -65,7 +65,7 @@ static void hlwd_gpio_irqhandler(struct irq_desc *desc)
int hwirq;
u32 emulated_pending;
- spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG);
pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
@@ -93,15 +93,12 @@ static void hlwd_gpio_irqhandler(struct irq_desc *desc)
/* Mark emulated interrupts as pending */
pending |= rising | falling;
}
- spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
chained_irq_enter(chip, desc);
- for_each_set_bit(hwirq, &pending, 32) {
- int irq = irq_find_mapping(hlwd->gpioc.irq.domain, hwirq);
-
- generic_handle_irq(irq);
- }
+ for_each_set_bit(hwirq, &pending, 32)
+ generic_handle_domain_irq(hlwd->gpioc.irq.domain, hwirq);
chained_irq_exit(chip, desc);
}
@@ -121,11 +118,11 @@ static void hlwd_gpio_irq_mask(struct irq_data *data)
unsigned long flags;
u32 mask;
- spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
mask &= ~BIT(data->hwirq);
iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK);
- spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
}
static void hlwd_gpio_irq_unmask(struct irq_data *data)
@@ -135,11 +132,11 @@ static void hlwd_gpio_irq_unmask(struct irq_data *data)
unsigned long flags;
u32 mask;
- spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
mask |= BIT(data->hwirq);
iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK);
- spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
}
static void hlwd_gpio_irq_enable(struct irq_data *data)
@@ -176,7 +173,7 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
unsigned long flags;
u32 level;
- spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
hlwd->edge_emulation &= ~BIT(data->hwirq);
@@ -197,11 +194,11 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type);
break;
default:
- spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
return -EINVAL;
}
- spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
return 0;
}
@@ -244,43 +241,45 @@ static int hlwd_gpio_probe(struct platform_device *pdev)
ngpios = 32;
hlwd->gpioc.ngpio = ngpios;
- res = devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd);
- if (res)
- return res;
-
/* Mask and ack all interrupts */
iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK);
iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG);
/*
* If this GPIO controller is not marked as an interrupt controller in
- * the DT, return.
+ * the DT, skip interrupt support.
*/
- if (!of_property_read_bool(pdev->dev.of_node, "interrupt-controller"))
- return 0;
-
- hlwd->irq = platform_get_irq(pdev, 0);
- if (hlwd->irq < 0) {
- dev_info(&pdev->dev, "platform_get_irq returned %d\n",
- hlwd->irq);
- return hlwd->irq;
+ if (of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) {
+ struct gpio_irq_chip *girq;
+
+ hlwd->irq = platform_get_irq(pdev, 0);
+ if (hlwd->irq < 0) {
+ dev_info(&pdev->dev, "platform_get_irq returned %d\n",
+ hlwd->irq);
+ return hlwd->irq;
+ }
+
+ hlwd->irqc.name = dev_name(&pdev->dev);
+ hlwd->irqc.irq_mask = hlwd_gpio_irq_mask;
+ hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask;
+ hlwd->irqc.irq_enable = hlwd_gpio_irq_enable;
+ hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type;
+
+ girq = &hlwd->gpioc.irq;
+ girq->chip = &hlwd->irqc;
+ girq->parent_handler = hlwd_gpio_irqhandler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = hlwd->irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
}
- hlwd->irqc.name = dev_name(&pdev->dev);
- hlwd->irqc.irq_mask = hlwd_gpio_irq_mask;
- hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask;
- hlwd->irqc.irq_enable = hlwd_gpio_irq_enable;
- hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type;
-
- res = gpiochip_irqchip_add(&hlwd->gpioc, &hlwd->irqc, 0,
- handle_level_irq, IRQ_TYPE_NONE);
- if (res)
- return res;
-
- gpiochip_set_chained_irqchip(&hlwd->gpioc, &hlwd->irqc,
- hlwd->irq, hlwd_gpio_irqhandler);
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd);
}
static const struct of_device_id hlwd_gpio_match[] = {
diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c
index 9d3ac51a765c..a40bd56673fe 100644
--- a/drivers/gpio/gpio-htc-egpio.c
+++ b/drivers/gpio/gpio-htc-egpio.c
@@ -118,20 +118,6 @@ static void egpio_handler(struct irq_desc *desc)
}
}
-int htc_egpio_get_wakeup_irq(struct device *dev)
-{
- struct egpio_info *ei = dev_get_drvdata(dev);
-
- /* Read current pins. */
- u16 readval = egpio_readw(ei, ei->ack_register);
- /* Ack/unmask interrupts. */
- ack_irqs(ei);
- /* Return first set pin. */
- readval &= ei->irqs_enabled;
- return ei->irq_start + ffs(readval) - 1;
-}
-EXPORT_SYMBOL(htc_egpio_get_wakeup_irq);
-
static inline int egpio_pos(struct egpio_info *ei, int bit)
{
return bit >> ei->reg_shift;
@@ -234,7 +220,10 @@ static int egpio_get_direction(struct gpio_chip *chip, unsigned offset)
egpio = gpiochip_get_data(chip);
- return !test_bit(offset, &egpio->is_out);
+ if (test_bit(offset, &egpio->is_out))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static void egpio_write_cache(struct egpio_info *ei)
@@ -279,7 +268,6 @@ static int __init egpio_probe(struct platform_device *pdev)
struct gpio_chip *chip;
unsigned int irq, irq_end;
int i;
- int ret;
/* Initialize ei data structure. */
ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL);
@@ -289,28 +277,24 @@ static int __init egpio_probe(struct platform_device *pdev)
spin_lock_init(&ei->lock);
/* Find chained irq */
- ret = -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res)
ei->chained_irq = res->start;
/* Map egpio chip into virtual address space. */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- goto fail;
- ei->base_addr = devm_ioremap_nocache(&pdev->dev, res->start,
- resource_size(res));
- if (!ei->base_addr)
- goto fail;
- pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr);
+ ei->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ei->base_addr))
+ return PTR_ERR(ei->base_addr);
if ((pdata->bus_width != 16) && (pdata->bus_width != 32))
- goto fail;
+ return -EINVAL;
+
ei->bus_shift = fls(pdata->bus_width - 1) - 3;
pr_debug("bus_shift = %d\n", ei->bus_shift);
if ((pdata->reg_width != 8) && (pdata->reg_width != 16))
- goto fail;
+ return -EINVAL;
+
ei->reg_shift = fls(pdata->reg_width - 1);
pr_debug("reg_shift = %d\n", ei->reg_shift);
@@ -322,10 +306,9 @@ static int __init egpio_probe(struct platform_device *pdev)
ei->chip = devm_kcalloc(&pdev->dev,
ei->nchips, sizeof(struct egpio_chip),
GFP_KERNEL);
- if (!ei->chip) {
- ret = -ENOMEM;
- goto fail;
- }
+ if (!ei->chip)
+ return -ENOMEM;
+
for (i = 0; i < ei->nchips; i++) {
ei->chip[i].reg_start = pdata->chip[i].reg_start;
ei->chip[i].cached_values = pdata->chip[i].initial_values;
@@ -335,10 +318,9 @@ static int __init egpio_probe(struct platform_device *pdev)
chip->label = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"htc-egpio-%d",
i);
- if (!chip->label) {
- ret = -ENOMEM;
- goto fail;
- }
+ if (!chip->label)
+ return -ENOMEM;
+
chip->parent = &pdev->dev;
chip->owner = THIS_MODULE;
chip->get = egpio_get;
@@ -380,10 +362,6 @@ static int __init egpio_probe(struct platform_device *pdev)
}
return 0;
-
-fail:
- printk(KERN_ERR "EGPIO failed to setup\n");
- return ret;
}
#ifdef CONFIG_PM
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index 90bf7742f9b0..3b31f5e9bf40 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -5,13 +5,11 @@
* Copyright (C) 2010 Extreme Engineering Solutions.
*/
-
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/ioport.h>
#include <linux/mfd/lpc_ich.h>
#include <linux/module.h>
-#include <linux/pci.h>
#include <linux/platform_device.h>
#define DRV_NAME "gpio_ich"
@@ -74,8 +72,8 @@ struct ichx_desc {
u32 use_sel_ignore[3];
/* Some chipsets have quirks, let these use their own request/get */
- int (*request)(struct gpio_chip *chip, unsigned offset);
- int (*get)(struct gpio_chip *chip, unsigned offset);
+ int (*request)(struct gpio_chip *chip, unsigned int offset);
+ int (*get)(struct gpio_chip *chip, unsigned int offset);
/*
* Some chipsets don't let reading output values on GPIO_LVL register
@@ -89,7 +87,7 @@ static struct {
struct device *dev;
struct gpio_chip chip;
struct resource *gpio_base; /* GPIO IO base */
- struct resource *pm_base; /* Power Mangagment IO base */
+ struct resource *pm_base; /* Power Management IO base */
struct ichx_desc *desc; /* Pointer to chipset-specific description */
u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */
u8 use_gpio; /* Which GPIO groups are usable */
@@ -100,7 +98,7 @@ static int modparam_gpiobase = -1; /* dynamic */
module_param_named(gpiobase, modparam_gpiobase, int, 0444);
MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default.");
-static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
+static int ichx_write_bit(int reg, unsigned int nr, int val, int verify)
{
unsigned long flags;
u32 data, tmp;
@@ -132,7 +130,7 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
return (verify && data != tmp) ? -EPERM : 0;
}
-static int ichx_read_bit(int reg, unsigned nr)
+static int ichx_read_bit(int reg, unsigned int nr)
{
unsigned long flags;
u32 data;
@@ -152,17 +150,20 @@ static int ichx_read_bit(int reg, unsigned nr)
return !!(data & BIT(bit));
}
-static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
+static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned int nr)
{
return !!(ichx_priv.use_gpio & BIT(nr / 32));
}
-static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr)
+static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned int nr)
{
- return ichx_read_bit(GPIO_IO_SEL, nr);
+ if (ichx_read_bit(GPIO_IO_SEL, nr))
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
-static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr)
{
/*
* Try setting pin as an input and verify it worked since many pins
@@ -171,7 +172,7 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
return ichx_write_bit(GPIO_IO_SEL, nr, 1, 1);
}
-static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr,
int val)
{
/* Disable blink hardware which is available for GPIOs from 0 to 31. */
@@ -188,12 +189,12 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
return ichx_write_bit(GPIO_IO_SEL, nr, 0, 1);
}
-static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
+static int ichx_gpio_get(struct gpio_chip *chip, unsigned int nr)
{
return ichx_read_bit(GPIO_LVL, nr);
}
-static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
+static int ich6_gpio_get(struct gpio_chip *chip, unsigned int nr)
{
unsigned long flags;
u32 data;
@@ -220,7 +221,7 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
}
}
-static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)
+static int ichx_gpio_request(struct gpio_chip *chip, unsigned int nr)
{
if (!ichx_gpio_check_available(chip, nr))
return -ENXIO;
@@ -237,7 +238,7 @@ static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)
return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV;
}
-static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr)
+static int ich6_gpio_request(struct gpio_chip *chip, unsigned int nr)
{
/*
* Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100
@@ -251,7 +252,7 @@ static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr)
return ichx_gpio_request(chip, nr);
}
-static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
+static void ichx_gpio_set(struct gpio_chip *chip, unsigned int nr, int val)
{
ichx_write_bit(GPIO_LVL, nr, val, 0);
}
diff --git a/drivers/gpio/gpio-idt3243x.c b/drivers/gpio/gpio-idt3243x.c
new file mode 100644
index 000000000000..1cafdf46f875
--- /dev/null
+++ b/drivers/gpio/gpio-idt3243x.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Driver for IDT/Renesas 79RC3243x Interrupt Controller */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#define IDT_PIC_IRQ_PEND 0x00
+#define IDT_PIC_IRQ_MASK 0x08
+
+#define IDT_GPIO_DIR 0x00
+#define IDT_GPIO_DATA 0x04
+#define IDT_GPIO_ILEVEL 0x08
+#define IDT_GPIO_ISTAT 0x0C
+
+struct idt_gpio_ctrl {
+ struct gpio_chip gc;
+ void __iomem *pic;
+ void __iomem *gpio;
+ u32 mask_cache;
+};
+
+static void idt_gpio_dispatch(struct irq_desc *desc)
+{
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+ struct irq_chip *host_chip = irq_desc_get_chip(desc);
+ unsigned int bit, virq;
+ unsigned long pending;
+
+ chained_irq_enter(host_chip, desc);
+
+ pending = readl(ctrl->pic + IDT_PIC_IRQ_PEND);
+ pending &= ~ctrl->mask_cache;
+ for_each_set_bit(bit, &pending, gc->ngpio) {
+ virq = irq_linear_revmap(gc->irq.domain, bit);
+ if (virq)
+ generic_handle_irq(virq);
+ }
+
+ chained_irq_exit(host_chip, desc);
+}
+
+static int idt_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+ unsigned int sense = flow_type & IRQ_TYPE_SENSE_MASK;
+ unsigned long flags;
+ u32 ilevel;
+
+ /* hardware only supports level triggered */
+ if (sense == IRQ_TYPE_NONE || (sense & IRQ_TYPE_EDGE_BOTH))
+ return -EINVAL;
+
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+
+ ilevel = readl(ctrl->gpio + IDT_GPIO_ILEVEL);
+ if (sense & IRQ_TYPE_LEVEL_HIGH)
+ ilevel |= BIT(d->hwirq);
+ else if (sense & IRQ_TYPE_LEVEL_LOW)
+ ilevel &= ~BIT(d->hwirq);
+
+ writel(ilevel, ctrl->gpio + IDT_GPIO_ILEVEL);
+ irq_set_handler_locked(d, handle_level_irq);
+
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ return 0;
+}
+
+static void idt_gpio_ack(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+
+ writel(~BIT(d->hwirq), ctrl->gpio + IDT_GPIO_ISTAT);
+}
+
+static void idt_gpio_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+
+ ctrl->mask_cache |= BIT(d->hwirq);
+ writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
+
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+}
+
+static void idt_gpio_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+
+ ctrl->mask_cache &= ~BIT(d->hwirq);
+ writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
+
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+}
+
+static int idt_gpio_irq_init_hw(struct gpio_chip *gc)
+{
+ struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+
+ /* Mask interrupts. */
+ ctrl->mask_cache = 0xffffffff;
+ writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
+
+ return 0;
+}
+
+static struct irq_chip idt_gpio_irqchip = {
+ .name = "IDTGPIO",
+ .irq_mask = idt_gpio_mask,
+ .irq_ack = idt_gpio_ack,
+ .irq_unmask = idt_gpio_unmask,
+ .irq_set_type = idt_gpio_irq_set_type
+};
+
+static int idt_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_irq_chip *girq;
+ struct idt_gpio_ctrl *ctrl;
+ int parent_irq;
+ int ngpios;
+ int ret;
+
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ ctrl->gpio = devm_platform_ioremap_resource_byname(pdev, "gpio");
+ if (IS_ERR(ctrl->gpio))
+ return PTR_ERR(ctrl->gpio);
+
+ ctrl->gc.parent = dev;
+
+ ret = bgpio_init(&ctrl->gc, &pdev->dev, 4, ctrl->gpio + IDT_GPIO_DATA,
+ NULL, NULL, ctrl->gpio + IDT_GPIO_DIR, NULL, 0);
+ if (ret) {
+ dev_err(dev, "bgpio_init failed\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "ngpios", &ngpios);
+ if (!ret)
+ ctrl->gc.ngpio = ngpios;
+
+ if (device_property_read_bool(dev, "interrupt-controller")) {
+ ctrl->pic = devm_platform_ioremap_resource_byname(pdev, "pic");
+ if (IS_ERR(ctrl->pic))
+ return PTR_ERR(ctrl->pic);
+
+ parent_irq = platform_get_irq(pdev, 0);
+ if (parent_irq < 0)
+ return parent_irq;
+
+ girq = &ctrl->gc.irq;
+ girq->chip = &idt_gpio_irqchip;
+ girq->init_hw = idt_gpio_irq_init_hw;
+ girq->parent_handler = idt_gpio_dispatch;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(dev, girq->num_parents,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+
+ girq->parents[0] = parent_irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
+ }
+
+ return devm_gpiochip_add_data(&pdev->dev, &ctrl->gc, ctrl);
+}
+
+static const struct of_device_id idt_gpio_of_match[] = {
+ { .compatible = "idt,32434-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, idt_gpio_of_match);
+
+static struct platform_driver idt_gpio_driver = {
+ .probe = idt_gpio_probe,
+ .driver = {
+ .name = "idt3243x-gpio",
+ .of_match_table = idt_gpio_of_match,
+ },
+};
+module_platform_driver(idt_gpio_driver);
+
+MODULE_DESCRIPTION("IDT 79RC3243x GPIO/PIC Driver");
+MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c
deleted file mode 100644
index 4e803baf980e..000000000000
--- a/drivers/gpio/gpio-intel-mid.c
+++ /dev/null
@@ -1,412 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Intel MID GPIO driver
- *
- * Copyright (c) 2008-2014,2016 Intel Corporation.
- */
-
-/* Supports:
- * Moorestown platform Langwell chip.
- * Medfield platform Penwell chip.
- * Clovertrail platform Cloverview chip.
- */
-
-#include <linux/delay.h>
-#include <linux/gpio/driver.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-#include <linux/stddef.h>
-
-#define INTEL_MID_IRQ_TYPE_EDGE (1 << 0)
-#define INTEL_MID_IRQ_TYPE_LEVEL (1 << 1)
-
-/*
- * Langwell chip has 64 pins and thus there are 2 32bit registers to control
- * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit
- * registers to control them, so we only define the order here instead of a
- * structure, to get a bit offset for a pin (use GPDR as an example):
- *
- * nreg = ngpio / 32;
- * reg = offset / 32;
- * bit = offset % 32;
- * reg_addr = reg_base + GPDR * nreg * 4 + reg * 4;
- *
- * so the bit of reg_addr is to control pin offset's GPDR feature
-*/
-
-enum GPIO_REG {
- GPLR = 0, /* pin level read-only */
- GPDR, /* pin direction */
- GPSR, /* pin set */
- GPCR, /* pin clear */
- GRER, /* rising edge detect */
- GFER, /* falling edge detect */
- GEDR, /* edge detect result */
- GAFR, /* alt function */
-};
-
-/* intel_mid gpio driver data */
-struct intel_mid_gpio_ddata {
- u16 ngpio; /* number of gpio pins */
- u32 chip_irq_type; /* chip interrupt type */
-};
-
-struct intel_mid_gpio {
- struct gpio_chip chip;
- void __iomem *reg_base;
- spinlock_t lock;
- struct pci_dev *pdev;
-};
-
-static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
- enum GPIO_REG reg_type)
-{
- struct intel_mid_gpio *priv = gpiochip_get_data(chip);
- unsigned nreg = chip->ngpio / 32;
- u8 reg = offset / 32;
-
- return priv->reg_base + reg_type * nreg * 4 + reg * 4;
-}
-
-static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset,
- enum GPIO_REG reg_type)
-{
- struct intel_mid_gpio *priv = gpiochip_get_data(chip);
- unsigned nreg = chip->ngpio / 32;
- u8 reg = offset / 16;
-
- return priv->reg_base + reg_type * nreg * 4 + reg * 4;
-}
-
-static int intel_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
- void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR);
- u32 value = readl(gafr);
- int shift = (offset % 16) << 1, af = (value >> shift) & 3;
-
- if (af) {
- value &= ~(3 << shift);
- writel(value, gafr);
- }
- return 0;
-}
-
-static int intel_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- void __iomem *gplr = gpio_reg(chip, offset, GPLR);
-
- return !!(readl(gplr) & BIT(offset % 32));
-}
-
-static void intel_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- void __iomem *gpsr, *gpcr;
-
- if (value) {
- gpsr = gpio_reg(chip, offset, GPSR);
- writel(BIT(offset % 32), gpsr);
- } else {
- gpcr = gpio_reg(chip, offset, GPCR);
- writel(BIT(offset % 32), gpcr);
- }
-}
-
-static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- struct intel_mid_gpio *priv = gpiochip_get_data(chip);
- void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
- u32 value;
- unsigned long flags;
-
- if (priv->pdev)
- pm_runtime_get(&priv->pdev->dev);
-
- spin_lock_irqsave(&priv->lock, flags);
- value = readl(gpdr);
- value &= ~BIT(offset % 32);
- writel(value, gpdr);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- if (priv->pdev)
- pm_runtime_put(&priv->pdev->dev);
-
- return 0;
-}
-
-static int intel_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct intel_mid_gpio *priv = gpiochip_get_data(chip);
- void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
- unsigned long flags;
-
- intel_gpio_set(chip, offset, value);
-
- if (priv->pdev)
- pm_runtime_get(&priv->pdev->dev);
-
- spin_lock_irqsave(&priv->lock, flags);
- value = readl(gpdr);
- value |= BIT(offset % 32);
- writel(value, gpdr);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- if (priv->pdev)
- pm_runtime_put(&priv->pdev->dev);
-
- return 0;
-}
-
-static int intel_mid_irq_type(struct irq_data *d, unsigned type)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct intel_mid_gpio *priv = gpiochip_get_data(gc);
- u32 gpio = irqd_to_hwirq(d);
- unsigned long flags;
- u32 value;
- void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER);
- void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER);
-
- if (gpio >= priv->chip.ngpio)
- return -EINVAL;
-
- if (priv->pdev)
- pm_runtime_get(&priv->pdev->dev);
-
- spin_lock_irqsave(&priv->lock, flags);
- if (type & IRQ_TYPE_EDGE_RISING)
- value = readl(grer) | BIT(gpio % 32);
- else
- value = readl(grer) & (~BIT(gpio % 32));
- writel(value, grer);
-
- if (type & IRQ_TYPE_EDGE_FALLING)
- value = readl(gfer) | BIT(gpio % 32);
- else
- value = readl(gfer) & (~BIT(gpio % 32));
- writel(value, gfer);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- if (priv->pdev)
- pm_runtime_put(&priv->pdev->dev);
-
- return 0;
-}
-
-static void intel_mid_irq_unmask(struct irq_data *d)
-{
-}
-
-static void intel_mid_irq_mask(struct irq_data *d)
-{
-}
-
-static struct irq_chip intel_mid_irqchip = {
- .name = "INTEL_MID-GPIO",
- .irq_mask = intel_mid_irq_mask,
- .irq_unmask = intel_mid_irq_unmask,
- .irq_set_type = intel_mid_irq_type,
-};
-
-static const struct intel_mid_gpio_ddata gpio_lincroft = {
- .ngpio = 64,
-};
-
-static const struct intel_mid_gpio_ddata gpio_penwell_aon = {
- .ngpio = 96,
- .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
-};
-
-static const struct intel_mid_gpio_ddata gpio_penwell_core = {
- .ngpio = 96,
- .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
-};
-
-static const struct intel_mid_gpio_ddata gpio_cloverview_aon = {
- .ngpio = 96,
- .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE | INTEL_MID_IRQ_TYPE_LEVEL,
-};
-
-static const struct intel_mid_gpio_ddata gpio_cloverview_core = {
- .ngpio = 96,
- .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
-};
-
-static const struct pci_device_id intel_gpio_ids[] = {
- {
- /* Lincroft */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f),
- .driver_data = (kernel_ulong_t)&gpio_lincroft,
- },
- {
- /* Penwell AON */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f),
- .driver_data = (kernel_ulong_t)&gpio_penwell_aon,
- },
- {
- /* Penwell Core */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a),
- .driver_data = (kernel_ulong_t)&gpio_penwell_core,
- },
- {
- /* Cloverview Aon */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08eb),
- .driver_data = (kernel_ulong_t)&gpio_cloverview_aon,
- },
- {
- /* Cloverview Core */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
- .driver_data = (kernel_ulong_t)&gpio_cloverview_core,
- },
- { }
-};
-
-static void intel_mid_irq_handler(struct irq_desc *desc)
-{
- struct gpio_chip *gc = irq_desc_get_handler_data(desc);
- struct intel_mid_gpio *priv = gpiochip_get_data(gc);
- struct irq_data *data = irq_desc_get_irq_data(desc);
- struct irq_chip *chip = irq_data_get_irq_chip(data);
- u32 base, gpio, mask;
- unsigned long pending;
- void __iomem *gedr;
-
- /* check GPIO controller to check which pin triggered the interrupt */
- for (base = 0; base < priv->chip.ngpio; base += 32) {
- gedr = gpio_reg(&priv->chip, base, GEDR);
- while ((pending = readl(gedr))) {
- gpio = __ffs(pending);
- mask = BIT(gpio);
- /* Clear before handling so we can't lose an edge */
- writel(mask, gedr);
- generic_handle_irq(irq_find_mapping(gc->irq.domain,
- base + gpio));
- }
- }
-
- chip->irq_eoi(data);
-}
-
-static void intel_mid_irq_init_hw(struct intel_mid_gpio *priv)
-{
- void __iomem *reg;
- unsigned base;
-
- for (base = 0; base < priv->chip.ngpio; base += 32) {
- /* Clear the rising-edge detect register */
- reg = gpio_reg(&priv->chip, base, GRER);
- writel(0, reg);
- /* Clear the falling-edge detect register */
- reg = gpio_reg(&priv->chip, base, GFER);
- writel(0, reg);
- /* Clear the edge detect status register */
- reg = gpio_reg(&priv->chip, base, GEDR);
- writel(~0, reg);
- }
-}
-
-static int __maybe_unused intel_gpio_runtime_idle(struct device *dev)
-{
- int err = pm_schedule_suspend(dev, 500);
- return err ?: -EBUSY;
-}
-
-static const struct dev_pm_ops intel_gpio_pm_ops = {
- SET_RUNTIME_PM_OPS(NULL, NULL, intel_gpio_runtime_idle)
-};
-
-static int intel_gpio_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- void __iomem *base;
- struct intel_mid_gpio *priv;
- u32 gpio_base;
- u32 irq_base;
- int retval;
- struct intel_mid_gpio_ddata *ddata =
- (struct intel_mid_gpio_ddata *)id->driver_data;
-
- retval = pcim_enable_device(pdev);
- if (retval)
- return retval;
-
- retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev));
- if (retval) {
- dev_err(&pdev->dev, "I/O memory mapping error\n");
- return retval;
- }
-
- base = pcim_iomap_table(pdev)[1];
-
- irq_base = readl(base);
- gpio_base = readl(sizeof(u32) + base);
-
- /* release the IO mapping, since we already get the info from bar1 */
- pcim_iounmap_regions(pdev, 1 << 1);
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->reg_base = pcim_iomap_table(pdev)[0];
- priv->chip.label = dev_name(&pdev->dev);
- priv->chip.parent = &pdev->dev;
- priv->chip.request = intel_gpio_request;
- priv->chip.direction_input = intel_gpio_direction_input;
- priv->chip.direction_output = intel_gpio_direction_output;
- priv->chip.get = intel_gpio_get;
- priv->chip.set = intel_gpio_set;
- priv->chip.base = gpio_base;
- priv->chip.ngpio = ddata->ngpio;
- priv->chip.can_sleep = false;
- priv->pdev = pdev;
-
- spin_lock_init(&priv->lock);
-
- pci_set_drvdata(pdev, priv);
- retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
- if (retval) {
- dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
- return retval;
- }
-
- retval = gpiochip_irqchip_add(&priv->chip,
- &intel_mid_irqchip,
- irq_base,
- handle_simple_irq,
- IRQ_TYPE_NONE);
- if (retval) {
- dev_err(&pdev->dev,
- "could not connect irqchip to gpiochip\n");
- return retval;
- }
-
- intel_mid_irq_init_hw(priv);
-
- gpiochip_set_chained_irqchip(&priv->chip,
- &intel_mid_irqchip,
- pdev->irq,
- intel_mid_irq_handler);
-
- pm_runtime_put_noidle(&pdev->dev);
- pm_runtime_allow(&pdev->dev);
-
- return 0;
-}
-
-static struct pci_driver intel_gpio_driver = {
- .name = "intel_mid_gpio",
- .id_table = intel_gpio_ids,
- .probe = intel_gpio_probe,
- .driver = {
- .pm = &intel_gpio_pm_ops,
- },
-};
-
-builtin_pci_driver(intel_gpio_driver);
diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c
index b497a1d18ca9..f332341fd4c8 100644
--- a/drivers/gpio/gpio-it87.c
+++ b/drivers/gpio/gpio-it87.c
@@ -47,13 +47,13 @@
/**
* struct it87_gpio - it87-specific GPIO chip
- * @chip the underlying gpio_chip structure
- * @lock a lock to avoid races between operations
- * @io_base base address for gpio ports
- * @io_size size of the port rage starting from io_base.
- * @output_base Super I/O register address for Output Enable register
- * @simple_base Super I/O 'Simple I/O' Enable register
- * @simple_size Super IO 'Simple I/O' Enable register size; this is
+ * @chip: the underlying gpio_chip structure
+ * @lock: a lock to avoid races between operations
+ * @io_base: base address for gpio ports
+ * @io_size: size of the port rage starting from io_base.
+ * @output_base: Super I/O register address for Output Enable register
+ * @simple_base: Super I/O 'Simple I/O' Enable register
+ * @simple_size: Super IO 'Simple I/O' Enable register size; this is
* required because IT87xx chips might only provide Simple I/O
* switches on a subset of lines, whereas the others keep the
* same status all time.
@@ -125,14 +125,6 @@ static inline int superio_inw(int reg)
return val;
}
-static inline void superio_outw(int val, int reg)
-{
- outb(reg++, REG);
- outb(val >> 8, VAL);
- outb(reg, REG);
- outb(val, VAL);
-}
-
static inline void superio_set_mask(int mask, int reg)
{
u8 curr_val = superio_inb(reg);
diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c
index 670c2a85a35b..312309be0287 100644
--- a/drivers/gpio/gpio-ixp4xx.c
+++ b/drivers/gpio/gpio-ixp4xx.c
@@ -14,10 +14,6 @@
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
-/* Include that go away with DT transition */
-#include <linux/irqchip/irq-ixp4xx.h>
-
-#include <asm/mach-types.h>
#define IXP4XX_REG_GPOUT 0x00
#define IXP4XX_REG_GPOE 0x04
@@ -47,7 +43,6 @@
* @dev: containing device for this instance
* @fwnode: the fwnode for this GPIO chip
* @gc: gpiochip for this instance
- * @domain: irqdomain for this chip instance
* @base: remapped I/O-memory base
* @irq_edge: Each bit represents an IRQ: 1: edge-triggered,
* 0: level triggered
@@ -56,48 +51,22 @@ struct ixp4xx_gpio {
struct device *dev;
struct fwnode_handle *fwnode;
struct gpio_chip gc;
- struct irq_domain *domain;
void __iomem *base;
unsigned long long irq_edge;
};
-/**
- * struct ixp4xx_gpio_map - IXP4 GPIO to parent IRQ map
- * @gpio_offset: offset of the IXP4 GPIO line
- * @parent_hwirq: hwirq on the parent IRQ controller
- */
-struct ixp4xx_gpio_map {
- int gpio_offset;
- int parent_hwirq;
-};
-
-/* GPIO lines 0..12 have corresponding IRQs, GPIOs 13..15 have no IRQs */
-const struct ixp4xx_gpio_map ixp4xx_gpiomap[] = {
- { .gpio_offset = 0, .parent_hwirq = 6 },
- { .gpio_offset = 1, .parent_hwirq = 7 },
- { .gpio_offset = 2, .parent_hwirq = 19 },
- { .gpio_offset = 3, .parent_hwirq = 20 },
- { .gpio_offset = 4, .parent_hwirq = 21 },
- { .gpio_offset = 5, .parent_hwirq = 22 },
- { .gpio_offset = 6, .parent_hwirq = 23 },
- { .gpio_offset = 7, .parent_hwirq = 24 },
- { .gpio_offset = 8, .parent_hwirq = 25 },
- { .gpio_offset = 9, .parent_hwirq = 26 },
- { .gpio_offset = 10, .parent_hwirq = 27 },
- { .gpio_offset = 11, .parent_hwirq = 28 },
- { .gpio_offset = 12, .parent_hwirq = 29 },
-};
-
static void ixp4xx_gpio_irq_ack(struct irq_data *d)
{
- struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct ixp4xx_gpio *g = gpiochip_get_data(gc);
__raw_writel(BIT(d->hwirq), g->base + IXP4XX_REG_GPIS);
}
static void ixp4xx_gpio_irq_unmask(struct irq_data *d)
{
- struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct ixp4xx_gpio *g = gpiochip_get_data(gc);
/* ACK when unmasking if not edge-triggered */
if (!(g->irq_edge & BIT(d->hwirq)))
@@ -108,7 +77,8 @@ static void ixp4xx_gpio_irq_unmask(struct irq_data *d)
static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
- struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct ixp4xx_gpio *g = gpiochip_get_data(gc);
int line = d->hwirq;
unsigned long flags;
u32 int_style;
@@ -154,7 +124,7 @@ static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type)
int_reg = IXP4XX_REG_GPIT1;
}
- spin_lock_irqsave(&g->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&g->gc.bgpio_lock, flags);
/* Clear the style for the appropriate pin */
val = __raw_readl(g->base + int_reg);
@@ -173,7 +143,7 @@ static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type)
val |= BIT(d->hwirq);
__raw_writel(val, g->base + IXP4XX_REG_GPOE);
- spin_unlock_irqrestore(&g->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&g->gc.bgpio_lock, flags);
/* This parent only accept level high (asserted) */
return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
@@ -187,122 +157,31 @@ static struct irq_chip ixp4xx_gpio_irqchip = {
.irq_set_type = ixp4xx_gpio_irq_set_type,
};
-static int ixp4xx_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
-{
- struct ixp4xx_gpio *g = gpiochip_get_data(gc);
- struct irq_fwspec fwspec;
-
- fwspec.fwnode = g->fwnode;
- fwspec.param_count = 2;
- fwspec.param[0] = offset;
- fwspec.param[1] = IRQ_TYPE_NONE;
-
- return irq_create_fwspec_mapping(&fwspec);
-}
-
-static int ixp4xx_gpio_irq_domain_translate(struct irq_domain *domain,
- struct irq_fwspec *fwspec,
- unsigned long *hwirq,
- unsigned int *type)
+static int ixp4xx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+ unsigned int child,
+ unsigned int child_type,
+ unsigned int *parent,
+ unsigned int *parent_type)
{
- int ret;
+ /* All these interrupts are level high in the CPU */
+ *parent_type = IRQ_TYPE_LEVEL_HIGH;
- /* We support standard DT translation */
- if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) {
- return irq_domain_translate_twocell(domain, fwspec,
- hwirq, type);
+ /* GPIO lines 0..12 have dedicated IRQs */
+ if (child == 0) {
+ *parent = 6;
+ return 0;
}
-
- /* This goes away when we transition to DT */
- if (is_fwnode_irqchip(fwspec->fwnode)) {
- ret = irq_domain_translate_twocell(domain, fwspec,
- hwirq, type);
- if (ret)
- return ret;
- WARN_ON(*type == IRQ_TYPE_NONE);
+ if (child == 1) {
+ *parent = 7;
return 0;
}
- return -EINVAL;
-}
-
-static int ixp4xx_gpio_irq_domain_alloc(struct irq_domain *d,
- unsigned int irq, unsigned int nr_irqs,
- void *data)
-{
- struct ixp4xx_gpio *g = d->host_data;
- irq_hw_number_t hwirq;
- unsigned int type = IRQ_TYPE_NONE;
- struct irq_fwspec *fwspec = data;
- int ret;
- int i;
-
- ret = ixp4xx_gpio_irq_domain_translate(d, fwspec, &hwirq, &type);
- if (ret)
- return ret;
-
- dev_dbg(g->dev, "allocate IRQ %d..%d, hwirq %lu..%lu\n",
- irq, irq + nr_irqs - 1,
- hwirq, hwirq + nr_irqs - 1);
-
- for (i = 0; i < nr_irqs; i++) {
- struct irq_fwspec parent_fwspec;
- const struct ixp4xx_gpio_map *map;
- int j;
-
- /* Not all lines support IRQs */
- for (j = 0; j < ARRAY_SIZE(ixp4xx_gpiomap); j++) {
- map = &ixp4xx_gpiomap[j];
- if (map->gpio_offset == hwirq)
- break;
- }
- if (j == ARRAY_SIZE(ixp4xx_gpiomap)) {
- dev_err(g->dev, "can't look up hwirq %lu\n", hwirq);
- return -EINVAL;
- }
- dev_dbg(g->dev, "found parent hwirq %u\n", map->parent_hwirq);
-
- /*
- * We set handle_bad_irq because the .set_type() should
- * always be invoked and set the right type of handler.
- */
- irq_domain_set_info(d,
- irq + i,
- hwirq + i,
- &ixp4xx_gpio_irqchip,
- g,
- handle_bad_irq,
- NULL, NULL);
- irq_set_probe(irq + i);
-
- /*
- * Create a IRQ fwspec to send up to the parent irqdomain:
- * specify the hwirq we address on the parent and tie it
- * all together up the chain.
- */
- parent_fwspec.fwnode = d->parent->fwnode;
- parent_fwspec.param_count = 2;
- parent_fwspec.param[0] = map->parent_hwirq;
- /* This parent only handles asserted level IRQs */
- parent_fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
- dev_dbg(g->dev, "alloc_irqs_parent for %d parent hwirq %d\n",
- irq + i, map->parent_hwirq);
- ret = irq_domain_alloc_irqs_parent(d, irq + i, 1,
- &parent_fwspec);
- if (ret)
- dev_err(g->dev,
- "failed to allocate parent hwirq %d for hwirq %lu\n",
- map->parent_hwirq, hwirq);
+ if (child >= 2 && child <= 12) {
+ *parent = child + 17;
+ return 0;
}
-
- return 0;
+ return -EINVAL;
}
-static const struct irq_domain_ops ixp4xx_gpio_irqdomain_ops = {
- .translate = ixp4xx_gpio_irq_domain_translate,
- .alloc = ixp4xx_gpio_irq_domain_alloc,
- .free = irq_domain_free_irqs_common,
-};
-
static int ixp4xx_gpio_probe(struct platform_device *pdev)
{
unsigned long flags;
@@ -311,8 +190,9 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
struct irq_domain *parent;
struct resource *res;
struct ixp4xx_gpio *g;
+ struct gpio_irq_chip *girq;
+ struct device_node *irq_parent;
int ret;
- int i;
g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
if (!g)
@@ -321,16 +201,27 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
g->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(g->base)) {
- dev_err(dev, "ioremap error\n");
+ if (IS_ERR(g->base))
return PTR_ERR(g->base);
+
+ irq_parent = of_irq_find_parent(np);
+ if (!irq_parent) {
+ dev_err(dev, "no IRQ parent node\n");
+ return -ENODEV;
+ }
+ parent = irq_find_host(irq_parent);
+ if (!parent) {
+ dev_err(dev, "no IRQ parent domain\n");
+ return -ENODEV;
}
+ g->fwnode = of_node_to_fwnode(np);
/*
* Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on
* specific machines.
*/
- if (machine_is_dsmg600() || machine_is_nas100d())
+ if (of_machine_is_compatible("dlink,dsm-g600-a") ||
+ of_machine_is_compatible("iom,nas-100d"))
__raw_writel(0x0, g->base + IXP4XX_REG_GPCLK);
/*
@@ -360,7 +251,6 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
dev_err(dev, "unable to init generic GPIO\n");
return ret;
}
- g->gc.to_irq = ixp4xx_gpio_to_irq;
g->gc.ngpio = 16;
g->gc.label = "IXP4XX_GPIO_CHIP";
/*
@@ -372,86 +262,22 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
g->gc.parent = &pdev->dev;
g->gc.owner = THIS_MODULE;
+ girq = &g->gc.irq;
+ girq->chip = &ixp4xx_gpio_irqchip;
+ girq->fwnode = g->fwnode;
+ girq->parent_domain = parent;
+ girq->child_to_parent_hwirq = ixp4xx_gpio_child_to_parent_hwirq;
+ girq->handler = handle_bad_irq;
+ girq->default_type = IRQ_TYPE_NONE;
+
ret = devm_gpiochip_add_data(dev, &g->gc, g);
if (ret) {
dev_err(dev, "failed to add SoC gpiochip\n");
return ret;
}
- /*
- * When we convert to device tree we will simply look up the
- * parent irqdomain using irq_find_host(parent) as parent comes
- * from IRQCHIP_DECLARE(), then use of_node_to_fwnode() to get
- * the fwnode. For now we need this boardfile style code.
- */
- if (np) {
- struct device_node *irq_parent;
-
- irq_parent = of_irq_find_parent(np);
- if (!irq_parent) {
- dev_err(dev, "no IRQ parent node\n");
- return -ENODEV;
- }
- parent = irq_find_host(irq_parent);
- if (!parent) {
- dev_err(dev, "no IRQ parent domain\n");
- return -ENODEV;
- }
- g->fwnode = of_node_to_fwnode(np);
- } else {
- parent = ixp4xx_get_irq_domain();
- g->fwnode = irq_domain_alloc_fwnode(g->base);
- if (!g->fwnode) {
- dev_err(dev, "no domain base\n");
- return -ENODEV;
- }
- }
- g->domain = irq_domain_create_hierarchy(parent,
- IRQ_DOMAIN_FLAG_HIERARCHY,
- ARRAY_SIZE(ixp4xx_gpiomap),
- g->fwnode,
- &ixp4xx_gpio_irqdomain_ops,
- g);
- if (!g->domain) {
- irq_domain_free_fwnode(g->fwnode);
- dev_err(dev, "no hierarchical irq domain\n");
- return ret;
- }
-
- /*
- * After adding OF support, this is no longer needed: irqs
- * will be allocated for the respective fwnodes.
- */
- if (!np) {
- for (i = 0; i < ARRAY_SIZE(ixp4xx_gpiomap); i++) {
- const struct ixp4xx_gpio_map *map = &ixp4xx_gpiomap[i];
- struct irq_fwspec fwspec;
-
- fwspec.fwnode = g->fwnode;
- /* This is the hwirq for the GPIO line side of things */
- fwspec.param[0] = map->gpio_offset;
- fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
- fwspec.param_count = 2;
- ret = __irq_domain_alloc_irqs(g->domain,
- -1, /* just pick something */
- 1,
- NUMA_NO_NODE,
- &fwspec,
- false,
- NULL);
- if (ret < 0) {
- irq_domain_free_fwnode(g->fwnode);
- dev_err(dev,
- "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n",
- map->gpio_offset, map->parent_hwirq,
- ret);
- return ret;
- }
- }
- }
-
platform_set_drvdata(pdev, g);
- dev_info(dev, "IXP4 GPIO @%p registered\n", g->base);
+ dev_info(dev, "IXP4 GPIO registered\n");
return 0;
}
diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c
index ef51638f3f75..4ea15f08e0f4 100644
--- a/drivers/gpio/gpio-kempld.c
+++ b/drivers/gpio/gpio-kempld.c
@@ -104,7 +104,10 @@ static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
struct kempld_device_data *pld = gpio->pld;
- return !kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset);
+ if (kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int kempld_gpio_pincount(struct kempld_device_data *pld)
diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c
deleted file mode 100644
index a0f87c124894..000000000000
--- a/drivers/gpio/gpio-ks8695.c
+++ /dev/null
@@ -1,284 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * arch/arm/mach-ks8695/gpio.c
- *
- * Copyright (C) 2006 Andrew Victor
- * Updated to GPIOLIB, Copyright 2008 Simtec Electronics
- * Daniel Silverstone <dsilvers@simtec.co.uk>
- */
-#include <linux/gpio/driver.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/module.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <asm/mach/irq.h>
-
-#include <mach/regs-gpio.h>
-#include <mach/gpio-ks8695.h>
-
-/*
- * Configure a GPIO line for either GPIO function, or its internal
- * function (Interrupt, Timer, etc).
- */
-static void ks8695_gpio_mode(unsigned int pin, short gpio)
-{
- unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN };
- unsigned long x, flags;
-
- if (pin > KS8695_GPIO_5) /* only GPIO 0..5 have internal functions */
- return;
-
- local_irq_save(flags);
-
- x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC);
- if (gpio) /* GPIO: set bit to 0 */
- x &= ~enable[pin];
- else /* Internal function: set bit to 1 */
- x |= enable[pin];
- __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPC);
-
- local_irq_restore(flags);
-}
-
-
-static unsigned short gpio_irq[] = { KS8695_IRQ_EXTERN0, KS8695_IRQ_EXTERN1, KS8695_IRQ_EXTERN2, KS8695_IRQ_EXTERN3 };
-
-/*
- * Configure GPIO pin as external interrupt source.
- */
-int ks8695_gpio_interrupt(unsigned int pin, unsigned int type)
-{
- unsigned long x, flags;
-
- if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */
- return -EINVAL;
-
- local_irq_save(flags);
-
- /* set pin as input */
- x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM);
- x &= ~IOPM(pin);
- __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM);
-
- local_irq_restore(flags);
-
- /* Set IRQ triggering type */
- irq_set_irq_type(gpio_irq[pin], type);
-
- /* enable interrupt mode */
- ks8695_gpio_mode(pin, 0);
-
- return 0;
-}
-EXPORT_SYMBOL(ks8695_gpio_interrupt);
-
-
-
-/* .... Generic GPIO interface .............................................. */
-
-/*
- * Configure the GPIO line as an input.
- */
-static int ks8695_gpio_direction_input(struct gpio_chip *gc, unsigned int pin)
-{
- unsigned long x, flags;
-
- if (pin > KS8695_GPIO_15)
- return -EINVAL;
-
- /* set pin to GPIO mode */
- ks8695_gpio_mode(pin, 1);
-
- local_irq_save(flags);
-
- /* set pin as input */
- x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM);
- x &= ~IOPM(pin);
- __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM);
-
- local_irq_restore(flags);
-
- return 0;
-}
-
-
-/*
- * Configure the GPIO line as an output, with default state.
- */
-static int ks8695_gpio_direction_output(struct gpio_chip *gc,
- unsigned int pin, int state)
-{
- unsigned long x, flags;
-
- if (pin > KS8695_GPIO_15)
- return -EINVAL;
-
- /* set pin to GPIO mode */
- ks8695_gpio_mode(pin, 1);
-
- local_irq_save(flags);
-
- /* set line state */
- x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
- if (state)
- x |= IOPD(pin);
- else
- x &= ~IOPD(pin);
- __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD);
-
- /* set pin as output */
- x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM);
- x |= IOPM(pin);
- __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM);
-
- local_irq_restore(flags);
-
- return 0;
-}
-
-
-/*
- * Set the state of an output GPIO line.
- */
-static void ks8695_gpio_set_value(struct gpio_chip *gc,
- unsigned int pin, int state)
-{
- unsigned long x, flags;
-
- if (pin > KS8695_GPIO_15)
- return;
-
- local_irq_save(flags);
-
- /* set output line state */
- x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
- if (state)
- x |= IOPD(pin);
- else
- x &= ~IOPD(pin);
- __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD);
-
- local_irq_restore(flags);
-}
-
-
-/*
- * Read the state of a GPIO line.
- */
-static int ks8695_gpio_get_value(struct gpio_chip *gc, unsigned int pin)
-{
- unsigned long x;
-
- if (pin > KS8695_GPIO_15)
- return -EINVAL;
-
- x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
- return (x & IOPD(pin)) != 0;
-}
-
-
-/*
- * Map GPIO line to IRQ number.
- */
-static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin)
-{
- if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */
- return -EINVAL;
-
- return gpio_irq[pin];
-}
-
-/* GPIOLIB interface */
-
-static struct gpio_chip ks8695_gpio_chip = {
- .label = "KS8695",
- .direction_input = ks8695_gpio_direction_input,
- .direction_output = ks8695_gpio_direction_output,
- .get = ks8695_gpio_get_value,
- .set = ks8695_gpio_set_value,
- .to_irq = ks8695_gpio_to_irq,
- .base = 0,
- .ngpio = 16,
- .can_sleep = false,
-};
-
-/* Register the GPIOs */
-void ks8695_register_gpios(void)
-{
- if (gpiochip_add_data(&ks8695_gpio_chip, NULL))
- printk(KERN_ERR "Unable to register core GPIOs\n");
-}
-
-/* .... Debug interface ..................................................... */
-
-#ifdef CONFIG_DEBUG_FS
-
-static int ks8695_gpio_show(struct seq_file *s, void *unused)
-{
- unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN };
- unsigned int intmask[] = { IOPC_IOEINT0TM, IOPC_IOEINT1TM, IOPC_IOEINT2TM, IOPC_IOEINT3TM };
- unsigned long mode, ctrl, data;
- int i;
-
- mode = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM);
- ctrl = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC);
- data = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
-
- seq_printf(s, "Pin\tI/O\tFunction\tState\n\n");
-
- for (i = KS8695_GPIO_0; i <= KS8695_GPIO_15 ; i++) {
- seq_printf(s, "%i:\t", i);
-
- seq_printf(s, "%s\t", (mode & IOPM(i)) ? "Output" : "Input");
-
- if (i <= KS8695_GPIO_3) {
- if (ctrl & enable[i]) {
- seq_printf(s, "EXT%i ", i);
-
- switch ((ctrl & intmask[i]) >> (4 * i)) {
- case IOPC_TM_LOW:
- seq_printf(s, "(Low)"); break;
- case IOPC_TM_HIGH:
- seq_printf(s, "(High)"); break;
- case IOPC_TM_RISING:
- seq_printf(s, "(Rising)"); break;
- case IOPC_TM_FALLING:
- seq_printf(s, "(Falling)"); break;
- case IOPC_TM_EDGE:
- seq_printf(s, "(Edges)"); break;
- }
- } else
- seq_printf(s, "GPIO\t");
- } else if (i <= KS8695_GPIO_5) {
- if (ctrl & enable[i])
- seq_printf(s, "TOUT%i\t", i - KS8695_GPIO_4);
- else
- seq_printf(s, "GPIO\t");
- } else {
- seq_printf(s, "GPIO\t");
- }
-
- seq_printf(s, "\t");
-
- seq_printf(s, "%i\n", (data & IOPD(i)) ? 1 : 0);
- }
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(ks8695_gpio);
-
-static int __init ks8695_gpio_debugfs_init(void)
-{
- /* /sys/kernel/debug/ks8695_gpio */
- debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL,
- &ks8695_gpio_fops);
- return 0;
-}
-postcore_initcall(ks8695_gpio_debugfs_init);
-
-#endif
diff --git a/drivers/gpio/gpio-logicvc.c b/drivers/gpio/gpio-logicvc.c
new file mode 100644
index 000000000000..992cc958a43f
--- /dev/null
+++ b/drivers/gpio/gpio-logicvc.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define LOGICVC_CTRL_REG 0x40
+#define LOGICVC_CTRL_GPIO_SHIFT 11
+#define LOGICVC_CTRL_GPIO_BITS 5
+
+#define LOGICVC_POWER_CTRL_REG 0x78
+#define LOGICVC_POWER_CTRL_GPIO_SHIFT 0
+#define LOGICVC_POWER_CTRL_GPIO_BITS 4
+
+struct logicvc_gpio {
+ struct gpio_chip chip;
+ struct regmap *regmap;
+};
+
+static void logicvc_gpio_offset(struct logicvc_gpio *logicvc, unsigned offset,
+ unsigned int *reg, unsigned int *bit)
+{
+ if (offset >= LOGICVC_CTRL_GPIO_BITS) {
+ *reg = LOGICVC_POWER_CTRL_REG;
+
+ /* To the (virtual) power ctrl offset. */
+ offset -= LOGICVC_CTRL_GPIO_BITS;
+ /* To the actual bit offset in reg. */
+ offset += LOGICVC_POWER_CTRL_GPIO_SHIFT;
+ } else {
+ *reg = LOGICVC_CTRL_REG;
+
+ /* To the actual bit offset in reg. */
+ offset += LOGICVC_CTRL_GPIO_SHIFT;
+ }
+
+ *bit = BIT(offset);
+}
+
+static int logicvc_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct logicvc_gpio *logicvc = gpiochip_get_data(chip);
+ unsigned int reg, bit, value;
+ int ret;
+
+ logicvc_gpio_offset(logicvc, offset, &reg, &bit);
+
+ ret = regmap_read(logicvc->regmap, reg, &value);
+ if (ret)
+ return ret;
+
+ return !!(value & bit);
+}
+
+static void logicvc_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct logicvc_gpio *logicvc = gpiochip_get_data(chip);
+ unsigned int reg, bit;
+
+ logicvc_gpio_offset(logicvc, offset, &reg, &bit);
+
+ regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0);
+}
+
+static int logicvc_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ /* Pins are always configured as output, so just set the value. */
+ logicvc_gpio_set(chip, offset, value);
+
+ return 0;
+}
+
+static struct regmap_config logicvc_gpio_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .name = "logicvc-gpio",
+};
+
+static int logicvc_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *of_node = dev->of_node;
+ struct logicvc_gpio *logicvc;
+ int ret;
+
+ logicvc = devm_kzalloc(dev, sizeof(*logicvc), GFP_KERNEL);
+ if (!logicvc)
+ return -ENOMEM;
+
+ /* Try to get regmap from parent first. */
+ logicvc->regmap = syscon_node_to_regmap(of_node->parent);
+
+ /* Grab our own regmap if that fails. */
+ if (IS_ERR(logicvc->regmap)) {
+ struct resource res;
+ void __iomem *base;
+
+ ret = of_address_to_resource(of_node, 0, &res);
+ if (ret) {
+ dev_err(dev, "Failed to get resource from address\n");
+ return ret;
+ }
+
+ base = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ logicvc_gpio_regmap_config.max_register = resource_size(&res) -
+ logicvc_gpio_regmap_config.reg_stride;
+
+ logicvc->regmap =
+ devm_regmap_init_mmio(dev, base,
+ &logicvc_gpio_regmap_config);
+ if (IS_ERR(logicvc->regmap)) {
+ dev_err(dev, "Failed to create regmap for I/O\n");
+ return PTR_ERR(logicvc->regmap);
+ }
+ }
+
+ logicvc->chip.parent = dev;
+ logicvc->chip.owner = THIS_MODULE;
+ logicvc->chip.label = dev_name(dev);
+ logicvc->chip.base = -1;
+ logicvc->chip.ngpio = LOGICVC_CTRL_GPIO_BITS +
+ LOGICVC_POWER_CTRL_GPIO_BITS;
+ logicvc->chip.get = logicvc_gpio_get;
+ logicvc->chip.set = logicvc_gpio_set;
+ logicvc->chip.direction_output = logicvc_gpio_direction_output;
+
+ platform_set_drvdata(pdev, logicvc);
+
+ return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc);
+}
+
+static const struct of_device_id logicivc_gpio_of_table[] = {
+ {
+ .compatible = "xylon,logicvc-3.02.a-gpio",
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, logicivc_gpio_of_table);
+
+static struct platform_driver logicvc_gpio_driver = {
+ .driver = {
+ .name = "gpio-logicvc",
+ .of_match_table = logicivc_gpio_of_table,
+ },
+ .probe = logicvc_gpio_probe,
+};
+
+module_platform_driver(logicvc_gpio_driver);
+
+MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
+MODULE_DESCRIPTION("Xylon LogiCVC GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c
index 00943170ce36..a42145873cc9 100644
--- a/drivers/gpio/gpio-loongson.c
+++ b/drivers/gpio/gpio-loongson.c
@@ -22,7 +22,7 @@
#define STLS2F_N_GPIO 4
#define STLS3A_N_GPIO 16
-#ifdef CONFIG_CPU_LOONGSON3
+#ifdef CONFIG_CPU_LOONGSON64
#define LOONGSON_N_GPIO STLS3A_N_GPIO
#else
#define LOONGSON_N_GPIO STLS2F_N_GPIO
diff --git a/drivers/gpio/gpio-loongson1.c b/drivers/gpio/gpio-loongson1.c
index 1b1ee94eeab4..5d90b3bc5a25 100644
--- a/drivers/gpio/gpio-loongson1.c
+++ b/drivers/gpio/gpio-loongson1.c
@@ -25,10 +25,10 @@ static int ls1x_gpio_request(struct gpio_chip *gc, unsigned int offset)
{
unsigned long flags;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
__raw_writel(__raw_readl(gpio_reg_base + GPIO_CFG) | BIT(offset),
gpio_reg_base + GPIO_CFG);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@@ -37,10 +37,10 @@ static void ls1x_gpio_free(struct gpio_chip *gc, unsigned int offset)
{
unsigned long flags;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
__raw_writel(__raw_readl(gpio_reg_base + GPIO_CFG) & ~BIT(offset),
gpio_reg_base + GPIO_CFG);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static int ls1x_gpio_probe(struct platform_device *pdev)
diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c
index 801995dd9b26..70fad87ff2db 100644
--- a/drivers/gpio/gpio-lp873x.c
+++ b/drivers/gpio/gpio-lp873x.c
@@ -33,7 +33,7 @@ static int lp873x_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
/* This device is output only */
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
}
static int lp873x_gpio_direction_input(struct gpio_chip *chip,
diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c
index a121c8f10610..fcde6708b5df 100644
--- a/drivers/gpio/gpio-lp87565.c
+++ b/drivers/gpio/gpio-lp87565.c
@@ -57,7 +57,10 @@ static int lp87565_gpio_get_direction(struct gpio_chip *chip,
if (ret < 0)
return ret;
- return !(val & BIT(offset));
+ if (val & BIT(offset))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int lp87565_gpio_direction_input(struct gpio_chip *chip,
@@ -120,14 +123,14 @@ static int lp87565_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
return regmap_update_bits(gpio->map,
LP87565_REG_GPIO_CONFIG,
BIT(offset +
- __ffs(LP87565_GOIO1_OD)),
+ __ffs(LP87565_GPIO1_OD)),
BIT(offset +
- __ffs(LP87565_GOIO1_OD)));
+ __ffs(LP87565_GPIO1_OD)));
case PIN_CONFIG_DRIVE_PUSH_PULL:
return regmap_update_bits(gpio->map,
LP87565_REG_GPIO_CONFIG,
BIT(offset +
- __ffs(LP87565_GOIO1_OD)), 0);
+ __ffs(LP87565_GPIO1_OD)), 0);
default:
return -ENOTSUPP;
}
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index 24885b3db3d5..d2b65cfb336e 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -16,36 +16,33 @@
#include <linux/platform_device.h>
#include <linux/module.h>
-#include <mach/hardware.h>
-#include <mach/platform.h>
-
-#define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000)
-#define LPC32XX_GPIO_P3_OUTP_SET _GPREG(0x004)
-#define LPC32XX_GPIO_P3_OUTP_CLR _GPREG(0x008)
-#define LPC32XX_GPIO_P3_OUTP_STATE _GPREG(0x00C)
-#define LPC32XX_GPIO_P2_DIR_SET _GPREG(0x010)
-#define LPC32XX_GPIO_P2_DIR_CLR _GPREG(0x014)
-#define LPC32XX_GPIO_P2_DIR_STATE _GPREG(0x018)
-#define LPC32XX_GPIO_P2_INP_STATE _GPREG(0x01C)
-#define LPC32XX_GPIO_P2_OUTP_SET _GPREG(0x020)
-#define LPC32XX_GPIO_P2_OUTP_CLR _GPREG(0x024)
-#define LPC32XX_GPIO_P2_MUX_SET _GPREG(0x028)
-#define LPC32XX_GPIO_P2_MUX_CLR _GPREG(0x02C)
-#define LPC32XX_GPIO_P2_MUX_STATE _GPREG(0x030)
-#define LPC32XX_GPIO_P0_INP_STATE _GPREG(0x040)
-#define LPC32XX_GPIO_P0_OUTP_SET _GPREG(0x044)
-#define LPC32XX_GPIO_P0_OUTP_CLR _GPREG(0x048)
-#define LPC32XX_GPIO_P0_OUTP_STATE _GPREG(0x04C)
-#define LPC32XX_GPIO_P0_DIR_SET _GPREG(0x050)
-#define LPC32XX_GPIO_P0_DIR_CLR _GPREG(0x054)
-#define LPC32XX_GPIO_P0_DIR_STATE _GPREG(0x058)
-#define LPC32XX_GPIO_P1_INP_STATE _GPREG(0x060)
-#define LPC32XX_GPIO_P1_OUTP_SET _GPREG(0x064)
-#define LPC32XX_GPIO_P1_OUTP_CLR _GPREG(0x068)
-#define LPC32XX_GPIO_P1_OUTP_STATE _GPREG(0x06C)
-#define LPC32XX_GPIO_P1_DIR_SET _GPREG(0x070)
-#define LPC32XX_GPIO_P1_DIR_CLR _GPREG(0x074)
-#define LPC32XX_GPIO_P1_DIR_STATE _GPREG(0x078)
+#define LPC32XX_GPIO_P3_INP_STATE (0x000)
+#define LPC32XX_GPIO_P3_OUTP_SET (0x004)
+#define LPC32XX_GPIO_P3_OUTP_CLR (0x008)
+#define LPC32XX_GPIO_P3_OUTP_STATE (0x00C)
+#define LPC32XX_GPIO_P2_DIR_SET (0x010)
+#define LPC32XX_GPIO_P2_DIR_CLR (0x014)
+#define LPC32XX_GPIO_P2_DIR_STATE (0x018)
+#define LPC32XX_GPIO_P2_INP_STATE (0x01C)
+#define LPC32XX_GPIO_P2_OUTP_SET (0x020)
+#define LPC32XX_GPIO_P2_OUTP_CLR (0x024)
+#define LPC32XX_GPIO_P2_MUX_SET (0x028)
+#define LPC32XX_GPIO_P2_MUX_CLR (0x02C)
+#define LPC32XX_GPIO_P2_MUX_STATE (0x030)
+#define LPC32XX_GPIO_P0_INP_STATE (0x040)
+#define LPC32XX_GPIO_P0_OUTP_SET (0x044)
+#define LPC32XX_GPIO_P0_OUTP_CLR (0x048)
+#define LPC32XX_GPIO_P0_OUTP_STATE (0x04C)
+#define LPC32XX_GPIO_P0_DIR_SET (0x050)
+#define LPC32XX_GPIO_P0_DIR_CLR (0x054)
+#define LPC32XX_GPIO_P0_DIR_STATE (0x058)
+#define LPC32XX_GPIO_P1_INP_STATE (0x060)
+#define LPC32XX_GPIO_P1_OUTP_SET (0x064)
+#define LPC32XX_GPIO_P1_OUTP_CLR (0x068)
+#define LPC32XX_GPIO_P1_OUTP_STATE (0x06C)
+#define LPC32XX_GPIO_P1_DIR_SET (0x070)
+#define LPC32XX_GPIO_P1_DIR_CLR (0x074)
+#define LPC32XX_GPIO_P1_DIR_STATE (0x078)
#define GPIO012_PIN_TO_BIT(x) (1 << (x))
#define GPIO3_PIN_TO_BIT(x) (1 << ((x) + 25))
@@ -72,12 +69,12 @@
#define LPC32XX_GPO_P3_GRP (LPC32XX_GPI_P3_GRP + LPC32XX_GPI_P3_MAX)
struct gpio_regs {
- void __iomem *inp_state;
- void __iomem *outp_state;
- void __iomem *outp_set;
- void __iomem *outp_clr;
- void __iomem *dir_set;
- void __iomem *dir_clr;
+ unsigned long inp_state;
+ unsigned long outp_state;
+ unsigned long outp_set;
+ unsigned long outp_clr;
+ unsigned long dir_set;
+ unsigned long dir_clr;
};
/*
@@ -165,16 +162,27 @@ static struct gpio_regs gpio_grp_regs_p3 = {
struct lpc32xx_gpio_chip {
struct gpio_chip chip;
struct gpio_regs *gpio_grp;
+ void __iomem *reg_base;
};
+static inline u32 gpreg_read(struct lpc32xx_gpio_chip *group, unsigned long offset)
+{
+ return __raw_readl(group->reg_base + offset);
+}
+
+static inline void gpreg_write(struct lpc32xx_gpio_chip *group, u32 val, unsigned long offset)
+{
+ __raw_writel(val, group->reg_base + offset);
+}
+
static void __set_gpio_dir_p012(struct lpc32xx_gpio_chip *group,
unsigned pin, int input)
{
if (input)
- __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ gpreg_write(group, GPIO012_PIN_TO_BIT(pin),
group->gpio_grp->dir_clr);
else
- __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ gpreg_write(group, GPIO012_PIN_TO_BIT(pin),
group->gpio_grp->dir_set);
}
@@ -184,19 +192,19 @@ static void __set_gpio_dir_p3(struct lpc32xx_gpio_chip *group,
u32 u = GPIO3_PIN_TO_BIT(pin);
if (input)
- __raw_writel(u, group->gpio_grp->dir_clr);
+ gpreg_write(group, u, group->gpio_grp->dir_clr);
else
- __raw_writel(u, group->gpio_grp->dir_set);
+ gpreg_write(group, u, group->gpio_grp->dir_set);
}
static void __set_gpio_level_p012(struct lpc32xx_gpio_chip *group,
unsigned pin, int high)
{
if (high)
- __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ gpreg_write(group, GPIO012_PIN_TO_BIT(pin),
group->gpio_grp->outp_set);
else
- __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ gpreg_write(group, GPIO012_PIN_TO_BIT(pin),
group->gpio_grp->outp_clr);
}
@@ -206,31 +214,31 @@ static void __set_gpio_level_p3(struct lpc32xx_gpio_chip *group,
u32 u = GPIO3_PIN_TO_BIT(pin);
if (high)
- __raw_writel(u, group->gpio_grp->outp_set);
+ gpreg_write(group, u, group->gpio_grp->outp_set);
else
- __raw_writel(u, group->gpio_grp->outp_clr);
+ gpreg_write(group, u, group->gpio_grp->outp_clr);
}
static void __set_gpo_level_p3(struct lpc32xx_gpio_chip *group,
unsigned pin, int high)
{
if (high)
- __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_set);
+ gpreg_write(group, GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_set);
else
- __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_clr);
+ gpreg_write(group, GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_clr);
}
static int __get_gpio_state_p012(struct lpc32xx_gpio_chip *group,
unsigned pin)
{
- return GPIO012_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state),
+ return GPIO012_PIN_IN_SEL(gpreg_read(group, group->gpio_grp->inp_state),
pin);
}
static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group,
unsigned pin)
{
- int state = __raw_readl(group->gpio_grp->inp_state);
+ int state = gpreg_read(group, group->gpio_grp->inp_state);
/*
* P3 GPIO pin input mapping is not contiguous, GPIOP3-0..4 is mapped
@@ -242,13 +250,13 @@ static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group,
static int __get_gpi_state_p3(struct lpc32xx_gpio_chip *group,
unsigned pin)
{
- return GPI3_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), pin);
+ return GPI3_PIN_IN_SEL(gpreg_read(group, group->gpio_grp->inp_state), pin);
}
static int __get_gpo_state_p3(struct lpc32xx_gpio_chip *group,
unsigned pin)
{
- return GPO3_PIN_IN_SEL(__raw_readl(group->gpio_grp->outp_state), pin);
+ return GPO3_PIN_IN_SEL(gpreg_read(group, group->gpio_grp->outp_state), pin);
}
/*
@@ -497,12 +505,18 @@ static int lpc32xx_of_xlate(struct gpio_chip *gc,
static int lpc32xx_gpio_probe(struct platform_device *pdev)
{
int i;
+ void __iomem *reg_base;
+
+ reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(reg_base))
+ return PTR_ERR(reg_base);
for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++) {
+ lpc32xx_gpiochip[i].chip.parent = &pdev->dev;
if (pdev->dev.of_node) {
lpc32xx_gpiochip[i].chip.of_xlate = lpc32xx_of_xlate;
lpc32xx_gpiochip[i].chip.of_gpio_n_cells = 3;
- lpc32xx_gpiochip[i].chip.of_node = pdev->dev.of_node;
+ lpc32xx_gpiochip[i].reg_base = reg_base;
}
devm_gpiochip_add_data(&pdev->dev, &lpc32xx_gpiochip[i].chip,
&lpc32xx_gpiochip[i]);
@@ -527,3 +541,7 @@ static struct platform_driver lpc32xx_gpio_driver = {
};
module_platform_driver(lpc32xx_gpio_driver);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIO driver for LPC32xx SoC");
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
deleted file mode 100644
index 31b4a091ab60..000000000000
--- a/drivers/gpio/gpio-lynxpoint.c
+++ /dev/null
@@ -1,458 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * GPIO controller driver for Intel Lynxpoint PCH chipset>
- * Copyright (c) 2012, Intel Corporation.
- *
- * Author: Mathias Nyman <mathias.nyman@linux.intel.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/bitops.h>
-#include <linux/gpio/driver.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-/* LynxPoint chipset has support for 94 gpio pins */
-
-#define LP_NUM_GPIO 94
-
-/* Bitmapped register offsets */
-#define LP_ACPI_OWNED 0x00 /* Bitmap, set by bios, 0: pin reserved for ACPI */
-#define LP_GC 0x7C /* set APIC IRQ to IRQ14 or IRQ15 for all pins */
-#define LP_INT_STAT 0x80
-#define LP_INT_ENABLE 0x90
-
-/* Each pin has two 32 bit config registers, starting at 0x100 */
-#define LP_CONFIG1 0x100
-#define LP_CONFIG2 0x104
-
-/* LP_CONFIG1 reg bits */
-#define OUT_LVL_BIT BIT(31)
-#define IN_LVL_BIT BIT(30)
-#define TRIG_SEL_BIT BIT(4) /* 0: Edge, 1: Level */
-#define INT_INV_BIT BIT(3) /* Invert interrupt triggering */
-#define DIR_BIT BIT(2) /* 0: Output, 1: Input */
-#define USE_SEL_BIT BIT(0) /* 0: Native, 1: GPIO */
-
-/* LP_CONFIG2 reg bits */
-#define GPINDIS_BIT BIT(2) /* disable input sensing */
-#define GPIWP_BIT (BIT(0) | BIT(1)) /* weak pull options */
-
-struct lp_gpio {
- struct gpio_chip chip;
- struct platform_device *pdev;
- spinlock_t lock;
- unsigned long reg_base;
-};
-
-/*
- * Lynxpoint gpios are controlled through both bitmapped registers and
- * per gpio specific registers. The bitmapped registers are in chunks of
- * 3 x 32bit registers to cover all 94 gpios
- *
- * per gpio specific registers consist of two 32bit registers per gpio
- * (LP_CONFIG1 and LP_CONFIG2), with 94 gpios there's a total of
- * 188 config registers.
- *
- * A simplified view of the register layout look like this:
- *
- * LP_ACPI_OWNED[31:0] gpio ownerships for gpios 0-31 (bitmapped registers)
- * LP_ACPI_OWNED[63:32] gpio ownerships for gpios 32-63
- * LP_ACPI_OWNED[94:64] gpio ownerships for gpios 63-94
- * ...
- * LP_INT_ENABLE[31:0] ...
- * LP_INT_ENABLE[63:31] ...
- * LP_INT_ENABLE[94:64] ...
- * LP0_CONFIG1 (gpio 0) config1 reg for gpio 0 (per gpio registers)
- * LP0_CONFIG2 (gpio 0) config2 reg for gpio 0
- * LP1_CONFIG1 (gpio 1) config1 reg for gpio 1
- * LP1_CONFIG2 (gpio 1) config2 reg for gpio 1
- * LP2_CONFIG1 (gpio 2) ...
- * LP2_CONFIG2 (gpio 2) ...
- * ...
- * LP94_CONFIG1 (gpio 94) ...
- * LP94_CONFIG2 (gpio 94) ...
- */
-
-static unsigned long lp_gpio_reg(struct gpio_chip *chip, unsigned offset,
- int reg)
-{
- struct lp_gpio *lg = gpiochip_get_data(chip);
- int reg_offset;
-
- if (reg == LP_CONFIG1 || reg == LP_CONFIG2)
- /* per gpio specific config registers */
- reg_offset = offset * 8;
- else
- /* bitmapped registers */
- reg_offset = (offset / 32) * 4;
-
- return lg->reg_base + reg + reg_offset;
-}
-
-static int lp_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
- struct lp_gpio *lg = gpiochip_get_data(chip);
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- unsigned long conf2 = lp_gpio_reg(chip, offset, LP_CONFIG2);
- unsigned long acpi_use = lp_gpio_reg(chip, offset, LP_ACPI_OWNED);
-
- pm_runtime_get(&lg->pdev->dev); /* should we put if failed */
-
- /* Fail if BIOS reserved pin for ACPI use */
- if (!(inl(acpi_use) & BIT(offset % 32))) {
- dev_err(&lg->pdev->dev, "gpio %d reserved for ACPI\n", offset);
- return -EBUSY;
- }
- /* Fail if pin is in alternate function mode (not GPIO mode) */
- if (!(inl(reg) & USE_SEL_BIT))
- return -ENODEV;
-
- /* enable input sensing */
- outl(inl(conf2) & ~GPINDIS_BIT, conf2);
-
-
- return 0;
-}
-
-static void lp_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
- struct lp_gpio *lg = gpiochip_get_data(chip);
- unsigned long conf2 = lp_gpio_reg(chip, offset, LP_CONFIG2);
-
- /* disable input sensing */
- outl(inl(conf2) | GPINDIS_BIT, conf2);
-
- pm_runtime_put(&lg->pdev->dev);
-}
-
-static int lp_irq_type(struct irq_data *d, unsigned type)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct lp_gpio *lg = gpiochip_get_data(gc);
- u32 hwirq = irqd_to_hwirq(d);
- unsigned long flags;
- u32 value;
- unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_CONFIG1);
-
- if (hwirq >= lg->chip.ngpio)
- return -EINVAL;
-
- spin_lock_irqsave(&lg->lock, flags);
- value = inl(reg);
-
- /* set both TRIG_SEL and INV bits to 0 for rising edge */
- if (type & IRQ_TYPE_EDGE_RISING)
- value &= ~(TRIG_SEL_BIT | INT_INV_BIT);
-
- /* TRIG_SEL bit 0, INV bit 1 for falling edge */
- if (type & IRQ_TYPE_EDGE_FALLING)
- value = (value | INT_INV_BIT) & ~TRIG_SEL_BIT;
-
- /* TRIG_SEL bit 1, INV bit 0 for level low */
- if (type & IRQ_TYPE_LEVEL_LOW)
- value = (value | TRIG_SEL_BIT) & ~INT_INV_BIT;
-
- /* TRIG_SEL bit 1, INV bit 1 for level high */
- if (type & IRQ_TYPE_LEVEL_HIGH)
- value |= TRIG_SEL_BIT | INT_INV_BIT;
-
- outl(value, reg);
- spin_unlock_irqrestore(&lg->lock, flags);
-
- return 0;
-}
-
-static int lp_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- return !!(inl(reg) & IN_LVL_BIT);
-}
-
-static void lp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- struct lp_gpio *lg = gpiochip_get_data(chip);
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- unsigned long flags;
-
- spin_lock_irqsave(&lg->lock, flags);
-
- if (value)
- outl(inl(reg) | OUT_LVL_BIT, reg);
- else
- outl(inl(reg) & ~OUT_LVL_BIT, reg);
-
- spin_unlock_irqrestore(&lg->lock, flags);
-}
-
-static int lp_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- struct lp_gpio *lg = gpiochip_get_data(chip);
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- unsigned long flags;
-
- spin_lock_irqsave(&lg->lock, flags);
- outl(inl(reg) | DIR_BIT, reg);
- spin_unlock_irqrestore(&lg->lock, flags);
-
- return 0;
-}
-
-static int lp_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct lp_gpio *lg = gpiochip_get_data(chip);
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- unsigned long flags;
-
- lp_gpio_set(chip, offset, value);
-
- spin_lock_irqsave(&lg->lock, flags);
- outl(inl(reg) & ~DIR_BIT, reg);
- spin_unlock_irqrestore(&lg->lock, flags);
-
- return 0;
-}
-
-static void lp_gpio_irq_handler(struct irq_desc *desc)
-{
- struct irq_data *data = irq_desc_get_irq_data(desc);
- struct gpio_chip *gc = irq_desc_get_handler_data(desc);
- struct lp_gpio *lg = gpiochip_get_data(gc);
- struct irq_chip *chip = irq_data_get_irq_chip(data);
- unsigned long reg, ena, pending;
- u32 base, pin;
-
- /* check from GPIO controller which pin triggered the interrupt */
- for (base = 0; base < lg->chip.ngpio; base += 32) {
- reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
- ena = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE);
-
- /* Only interrupts that are enabled */
- pending = inl(reg) & inl(ena);
-
- for_each_set_bit(pin, &pending, 32) {
- unsigned irq;
-
- /* Clear before handling so we don't lose an edge */
- outl(BIT(pin), reg);
-
- irq = irq_find_mapping(lg->chip.irq.domain, base + pin);
- generic_handle_irq(irq);
- }
- }
- chip->irq_eoi(data);
-}
-
-static void lp_irq_unmask(struct irq_data *d)
-{
-}
-
-static void lp_irq_mask(struct irq_data *d)
-{
-}
-
-static void lp_irq_enable(struct irq_data *d)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct lp_gpio *lg = gpiochip_get_data(gc);
- u32 hwirq = irqd_to_hwirq(d);
- unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE);
- unsigned long flags;
-
- spin_lock_irqsave(&lg->lock, flags);
- outl(inl(reg) | BIT(hwirq % 32), reg);
- spin_unlock_irqrestore(&lg->lock, flags);
-}
-
-static void lp_irq_disable(struct irq_data *d)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct lp_gpio *lg = gpiochip_get_data(gc);
- u32 hwirq = irqd_to_hwirq(d);
- unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE);
- unsigned long flags;
-
- spin_lock_irqsave(&lg->lock, flags);
- outl(inl(reg) & ~BIT(hwirq % 32), reg);
- spin_unlock_irqrestore(&lg->lock, flags);
-}
-
-static struct irq_chip lp_irqchip = {
- .name = "LP-GPIO",
- .irq_mask = lp_irq_mask,
- .irq_unmask = lp_irq_unmask,
- .irq_enable = lp_irq_enable,
- .irq_disable = lp_irq_disable,
- .irq_set_type = lp_irq_type,
- .flags = IRQCHIP_SKIP_SET_WAKE,
-};
-
-static void lp_gpio_irq_init_hw(struct lp_gpio *lg)
-{
- unsigned long reg;
- unsigned base;
-
- for (base = 0; base < lg->chip.ngpio; base += 32) {
- /* disable gpio pin interrupts */
- reg = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE);
- outl(0, reg);
- /* Clear interrupt status register */
- reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
- outl(0xffffffff, reg);
- }
-}
-
-static int lp_gpio_probe(struct platform_device *pdev)
-{
- struct lp_gpio *lg;
- struct gpio_chip *gc;
- struct resource *io_rc, *irq_rc;
- struct device *dev = &pdev->dev;
- unsigned long reg_len;
- int ret = -ENODEV;
-
- lg = devm_kzalloc(dev, sizeof(struct lp_gpio), GFP_KERNEL);
- if (!lg)
- return -ENOMEM;
-
- lg->pdev = pdev;
- platform_set_drvdata(pdev, lg);
-
- io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
- irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-
- if (!io_rc) {
- dev_err(dev, "missing IO resources\n");
- return -EINVAL;
- }
-
- lg->reg_base = io_rc->start;
- reg_len = resource_size(io_rc);
-
- if (!devm_request_region(dev, lg->reg_base, reg_len, "lp-gpio")) {
- dev_err(dev, "failed requesting IO region 0x%x\n",
- (unsigned int)lg->reg_base);
- return -EBUSY;
- }
-
- spin_lock_init(&lg->lock);
-
- gc = &lg->chip;
- gc->label = dev_name(dev);
- gc->owner = THIS_MODULE;
- gc->request = lp_gpio_request;
- gc->free = lp_gpio_free;
- gc->direction_input = lp_gpio_direction_input;
- gc->direction_output = lp_gpio_direction_output;
- gc->get = lp_gpio_get;
- gc->set = lp_gpio_set;
- gc->base = -1;
- gc->ngpio = LP_NUM_GPIO;
- gc->can_sleep = false;
- gc->parent = dev;
-
- ret = devm_gpiochip_add_data(dev, gc, lg);
- if (ret) {
- dev_err(dev, "failed adding lp-gpio chip\n");
- return ret;
- }
-
- /* set up interrupts */
- if (irq_rc && irq_rc->start) {
- lp_gpio_irq_init_hw(lg);
- ret = gpiochip_irqchip_add(gc, &lp_irqchip, 0,
- handle_simple_irq, IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "failed to add irqchip\n");
- return ret;
- }
-
- gpiochip_set_chained_irqchip(gc, &lp_irqchip,
- (unsigned)irq_rc->start,
- lp_gpio_irq_handler);
- }
-
- pm_runtime_enable(dev);
-
- return 0;
-}
-
-static int lp_gpio_runtime_suspend(struct device *dev)
-{
- return 0;
-}
-
-static int lp_gpio_runtime_resume(struct device *dev)
-{
- return 0;
-}
-
-static int lp_gpio_resume(struct device *dev)
-{
- struct lp_gpio *lg = dev_get_drvdata(dev);
- unsigned long reg;
- int i;
-
- /* on some hardware suspend clears input sensing, re-enable it here */
- for (i = 0; i < lg->chip.ngpio; i++) {
- if (gpiochip_is_requested(&lg->chip, i) != NULL) {
- reg = lp_gpio_reg(&lg->chip, i, LP_CONFIG2);
- outl(inl(reg) & ~GPINDIS_BIT, reg);
- }
- }
- return 0;
-}
-
-static const struct dev_pm_ops lp_gpio_pm_ops = {
- .runtime_suspend = lp_gpio_runtime_suspend,
- .runtime_resume = lp_gpio_runtime_resume,
- .resume = lp_gpio_resume,
-};
-
-static const struct acpi_device_id lynxpoint_gpio_acpi_match[] = {
- { "INT33C7", 0 },
- { "INT3437", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(acpi, lynxpoint_gpio_acpi_match);
-
-static int lp_gpio_remove(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver lp_gpio_driver = {
- .probe = lp_gpio_probe,
- .remove = lp_gpio_remove,
- .driver = {
- .name = "lp_gpio",
- .pm = &lp_gpio_pm_ops,
- .acpi_match_table = ACPI_PTR(lynxpoint_gpio_acpi_match),
- },
-};
-
-static int __init lp_gpio_init(void)
-{
- return platform_driver_register(&lp_gpio_driver);
-}
-
-static void __exit lp_gpio_exit(void)
-{
- platform_driver_unregister(&lp_gpio_driver);
-}
-
-subsys_initcall(lp_gpio_init);
-module_exit(lp_gpio_exit);
-
-MODULE_AUTHOR("Mathias Nyman (Intel)");
-MODULE_DESCRIPTION("GPIO interface for Intel Lynxpoint");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:lp_gpio");
diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c
index 4dbc837d1215..8f38303fcbc4 100644
--- a/drivers/gpio/gpio-madera.c
+++ b/drivers/gpio/gpio-madera.c
@@ -34,7 +34,10 @@ static int madera_gpio_get_direction(struct gpio_chip *chip,
if (ret < 0)
return ret;
- return !!(val & MADERA_GP1_DIR_MASK);
+ if (val & MADERA_GP1_DIR_MASK)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int madera_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
@@ -120,7 +123,7 @@ static const struct gpio_chip madera_gpio_chip = {
static int madera_gpio_probe(struct platform_device *pdev)
{
struct madera *madera = dev_get_drvdata(pdev->dev.parent);
- struct madera_pdata *pdata = dev_get_platdata(madera->dev);
+ struct madera_pdata *pdata = &madera->pdata;
struct madera_gpio *madera_gpio;
int ret;
@@ -136,6 +139,9 @@ static int madera_gpio_probe(struct platform_device *pdev)
madera_gpio->gpio_chip.parent = pdev->dev.parent;
switch (madera->type) {
+ case CS47L15:
+ madera_gpio->gpio_chip.ngpio = CS47L15_NUM_GPIOS;
+ break;
case CS47L35:
madera_gpio->gpio_chip.ngpio = CS47L35_NUM_GPIOS;
break;
@@ -147,13 +153,18 @@ static int madera_gpio_probe(struct platform_device *pdev)
case CS47L91:
madera_gpio->gpio_chip.ngpio = CS47L90_NUM_GPIOS;
break;
+ case CS42L92:
+ case CS47L92:
+ case CS47L93:
+ madera_gpio->gpio_chip.ngpio = CS47L92_NUM_GPIOS;
+ break;
default:
dev_err(&pdev->dev, "Unknown chip variant %d\n", madera->type);
return -EINVAL;
}
/* We want to be usable on systems that don't use devicetree or acpi */
- if (pdata && pdata->gpio_base)
+ if (pdata->gpio_base)
madera_gpio->gpio_chip.base = pdata->gpio_base;
else
madera_gpio->gpio_chip.base = -1;
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index 4b4b2ceb82fc..161c4751c5f7 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -31,6 +31,7 @@
*/
#include <linux/bitmap.h>
+#include <linux/bitops.h>
#include <linux/crc8.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
@@ -94,7 +95,7 @@ DECLARE_CRC8_TABLE(max3191x_crc8);
static int max3191x_get_direction(struct gpio_chip *gpio, unsigned int offset)
{
- return 1; /* always in */
+ return GPIO_LINE_DIRECTION_IN; /* always in */
}
static int max3191x_direction_input(struct gpio_chip *gpio, unsigned int offset)
@@ -232,16 +233,20 @@ static int max3191x_get_multiple(struct gpio_chip *gpio, unsigned long *mask,
unsigned long *bits)
{
struct max3191x_chip *max3191x = gpiochip_get_data(gpio);
- int ret, bit = 0, wordlen = max3191x_wordlen(max3191x);
+ const unsigned int wordlen = max3191x_wordlen(max3191x);
+ int ret;
+ unsigned long bit;
+ unsigned long gpio_mask;
+ unsigned long in;
mutex_lock(&max3191x->lock);
ret = max3191x_readout_locked(max3191x);
if (ret)
goto out_unlock;
- while ((bit = find_next_bit(mask, gpio->ngpio, bit)) != gpio->ngpio) {
+ bitmap_zero(bits, gpio->ngpio);
+ for_each_set_clump8(bit, gpio_mask, mask, gpio->ngpio) {
unsigned int chipnum = bit / MAX3191X_NGPIO;
- unsigned long in, shift, index;
if (max3191x_chip_is_faulting(max3191x, chipnum)) {
ret = -EIO;
@@ -249,12 +254,8 @@ static int max3191x_get_multiple(struct gpio_chip *gpio, unsigned long *mask,
}
in = ((u8 *)max3191x->xfer.rx_buf)[chipnum * wordlen];
- shift = round_down(bit % BITS_PER_LONG, MAX3191X_NGPIO);
- index = bit / BITS_PER_LONG;
- bits[index] &= ~(mask[index] & (0xff << shift));
- bits[index] |= mask[index] & (in << shift); /* copy bits */
-
- bit = (chipnum + 1) * MAX3191X_NGPIO; /* go to next chip */
+ in &= gpio_mask;
+ bitmap_set_value8(bits, in, bit);
}
out_unlock:
@@ -325,7 +326,7 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
bitmap_zero(values, ndescs);
gpiod_set_array_value_cansleep(ndescs, desc, info, values);
- kfree(values);
+ bitmap_free(values);
}
static struct gpio_descs *devm_gpiod_get_array_optional_count(
@@ -442,14 +443,12 @@ static int max3191x_probe(struct spi_device *spi)
return 0;
}
-static int max3191x_remove(struct spi_device *spi)
+static void max3191x_remove(struct spi_device *spi)
{
struct max3191x_chip *max3191x = spi_get_drvdata(spi);
gpiochip_remove(&max3191x->gpio);
mutex_destroy(&max3191x->lock);
-
- return 0;
}
static int __init max3191x_register_driver(struct spi_driver *sdrv)
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..11813f41d460 100644
--- a/drivers/gpio/gpio-max7301.c
+++ b/drivers/gpio/gpio-max7301.c
@@ -64,9 +64,9 @@ static int max7301_probe(struct spi_device *spi)
return ret;
}
-static int max7301_remove(struct spi_device *spi)
+static void max7301_remove(struct spi_device *spi)
{
- return __max730x_remove(&spi->dev);
+ __max730x_remove(&spi->dev);
}
static const struct spi_device_id max7301_id[] = {
diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c
index 1e1935c51096..bb5cf14ae4c8 100644
--- a/drivers/gpio/gpio-max730x.c
+++ b/drivers/gpio/gpio-max730x.c
@@ -47,7 +47,7 @@
static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
{
- struct max7301 *ts = gpiochip_get_data(chip);
+ struct max7301 *ts = container_of(chip, struct max7301, chip);
u8 *config;
u8 offset_bits, pin_config;
int ret;
@@ -89,7 +89,7 @@ static int __max7301_set(struct max7301 *ts, unsigned offset, int value)
static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,
int value)
{
- struct max7301 *ts = gpiochip_get_data(chip);
+ struct max7301 *ts = container_of(chip, struct max7301, chip);
u8 *config;
u8 offset_bits;
int ret;
@@ -189,10 +189,6 @@ int __max730x_probe(struct max7301 *ts)
ts->chip.parent = dev;
ts->chip.owner = THIS_MODULE;
- ret = gpiochip_add_data(&ts->chip, ts);
- if (ret)
- goto exit_destroy;
-
/*
* initialize pullups according to platform data and cache the
* register values for later use.
@@ -214,7 +210,9 @@ int __max730x_probe(struct max7301 *ts)
}
}
- return ret;
+ ret = gpiochip_add_data(&ts->chip, ts);
+ if (!ret)
+ return ret;
exit_destroy:
mutex_destroy(&ts->lock);
@@ -222,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-max732x.c b/drivers/gpio/gpio-max732x.c
index 5fb0bcf31142..da6972117030 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -496,15 +496,13 @@ static int max732x_irq_setup(struct max732x_chip *chip,
const struct i2c_device_id *id)
{
struct i2c_client *client = chip->client;
- struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
int has_irq = max732x_features[id->driver_data] >> 32;
int irq_base = 0;
int ret;
- if (((pdata && pdata->irq_base) || client->irq)
- && has_irq != INT_NONE) {
- if (pdata)
- irq_base = pdata->irq_base;
+ if (client->irq && has_irq != INT_NONE) {
+ struct gpio_irq_chip *girq;
+
chip->irq_features = has_irq;
mutex_init(&chip->irq_lock);
@@ -517,19 +515,17 @@ static int max732x_irq_setup(struct max732x_chip *chip,
client->irq);
return ret;
}
- ret = gpiochip_irqchip_add_nested(&chip->gpio_chip,
- &max732x_irq_chip,
- irq_base,
- handle_simple_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(&client->dev,
- "could not connect irqchip to gpiochip\n");
- return ret;
- }
- gpiochip_set_nested_irqchip(&chip->gpio_chip,
- &max732x_irq_chip,
- client->irq);
+
+ girq = &chip->gpio_chip.irq;
+ girq->chip = &max732x_irq_chip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->threaded = true;
+ girq->first = irq_base; /* FIXME: get rid of this */
}
return 0;
@@ -540,10 +536,9 @@ static int max732x_irq_setup(struct max732x_chip *chip,
const struct i2c_device_id *id)
{
struct i2c_client *client = chip->client;
- struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
int has_irq = max732x_features[id->driver_data] >> 32;
- if (((pdata && pdata->irq_base) || client->irq) && has_irq != INT_NONE)
+ if (client->irq && has_irq != INT_NONE)
dev_warn(&client->dev, "interrupt support not compiled in\n");
return 0;
@@ -695,52 +690,24 @@ static int max732x_probe(struct i2c_client *client,
return ret;
}
- ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
+ ret = max732x_irq_setup(chip, id);
if (ret)
return ret;
- ret = max732x_irq_setup(chip, id);
+ ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
if (ret)
return ret;
- if (pdata && pdata->setup) {
- ret = pdata->setup(client, chip->gpio_chip.base,
- chip->gpio_chip.ngpio, pdata->context);
- if (ret < 0)
- dev_warn(&client->dev, "setup failed, %d\n", ret);
- }
-
i2c_set_clientdata(client, chip);
return 0;
}
-static int max732x_remove(struct i2c_client *client)
-{
- struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
- struct max732x_chip *chip = i2c_get_clientdata(client);
-
- if (pdata && pdata->teardown) {
- int ret;
-
- ret = pdata->teardown(client, chip->gpio_chip.base,
- chip->gpio_chip.ngpio, pdata->context);
- if (ret < 0) {
- dev_err(&client->dev, "%s failed, %d\n",
- "teardown", ret);
- return ret;
- }
- }
-
- return 0;
-}
-
static struct i2c_driver max732x_driver = {
.driver = {
.name = "max732x",
.of_match_table = of_match_ptr(max732x_of_table),
},
.probe = max732x_probe,
- .remove = max732x_remove,
.id_table = max732x_id,
};
diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c
index b7d89e30131e..c18b60e39a94 100644
--- a/drivers/gpio/gpio-max77620.c
+++ b/drivers/gpio/gpio-max77620.c
@@ -18,109 +18,118 @@ struct max77620_gpio {
struct gpio_chip gpio_chip;
struct regmap *rmap;
struct device *dev;
+ struct mutex buslock; /* irq_bus_lock */
+ unsigned int irq_type[MAX77620_GPIO_NR];
+ bool irq_enabled[MAX77620_GPIO_NR];
};
-static const struct regmap_irq max77620_gpio_irqs[] = {
- [0] = {
- .reg_offset = 0,
- .mask = MAX77620_IRQ_LVL2_GPIO_EDGE0,
- .type = {
- .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING,
- .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING,
- .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK,
- .type_reg_offset = 0,
- .types_supported = IRQ_TYPE_EDGE_BOTH,
- },
- },
- [1] = {
- .reg_offset = 0,
- .mask = MAX77620_IRQ_LVL2_GPIO_EDGE1,
- .type = {
- .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING,
- .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING,
- .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK,
- .type_reg_offset = 1,
- .types_supported = IRQ_TYPE_EDGE_BOTH,
- },
- },
- [2] = {
- .reg_offset = 0,
- .mask = MAX77620_IRQ_LVL2_GPIO_EDGE2,
- .type = {
- .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING,
- .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING,
- .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK,
- .type_reg_offset = 2,
- .types_supported = IRQ_TYPE_EDGE_BOTH,
- },
- },
- [3] = {
- .reg_offset = 0,
- .mask = MAX77620_IRQ_LVL2_GPIO_EDGE3,
- .type = {
- .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING,
- .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING,
- .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK,
- .type_reg_offset = 3,
- .types_supported = IRQ_TYPE_EDGE_BOTH,
- },
- },
- [4] = {
- .reg_offset = 0,
- .mask = MAX77620_IRQ_LVL2_GPIO_EDGE4,
- .type = {
- .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING,
- .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING,
- .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK,
- .type_reg_offset = 4,
- .types_supported = IRQ_TYPE_EDGE_BOTH,
- },
- },
- [5] = {
- .reg_offset = 0,
- .mask = MAX77620_IRQ_LVL2_GPIO_EDGE5,
- .type = {
- .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING,
- .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING,
- .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK,
- .type_reg_offset = 5,
- .types_supported = IRQ_TYPE_EDGE_BOTH,
- },
- },
- [6] = {
- .reg_offset = 0,
- .mask = MAX77620_IRQ_LVL2_GPIO_EDGE6,
- .type = {
- .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING,
- .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING,
- .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK,
- .type_reg_offset = 6,
- .types_supported = IRQ_TYPE_EDGE_BOTH,
- },
- },
- [7] = {
- .reg_offset = 0,
- .mask = MAX77620_IRQ_LVL2_GPIO_EDGE7,
- .type = {
- .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING,
- .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING,
- .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK,
- .type_reg_offset = 7,
- .types_supported = IRQ_TYPE_EDGE_BOTH,
- },
- },
-};
+static irqreturn_t max77620_gpio_irqhandler(int irq, void *data)
+{
+ struct max77620_gpio *gpio = data;
+ unsigned int value, offset;
+ unsigned long pending;
+ int err;
+
+ err = regmap_read(gpio->rmap, MAX77620_REG_IRQ_LVL2_GPIO, &value);
+ if (err < 0) {
+ dev_err(gpio->dev, "REG_IRQ_LVL2_GPIO read failed: %d\n", err);
+ return IRQ_NONE;
+ }
+
+ pending = value;
+
+ for_each_set_bit(offset, &pending, MAX77620_GPIO_NR) {
+ unsigned int virq;
+
+ virq = irq_find_mapping(gpio->gpio_chip.irq.domain, offset);
+ handle_nested_irq(virq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void max77620_gpio_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct max77620_gpio *gpio = gpiochip_get_data(chip);
+
+ gpio->irq_enabled[data->hwirq] = false;
+ gpiochip_disable_irq(chip, data->hwirq);
+}
-static const struct regmap_irq_chip max77620_gpio_irq_chip = {
- .name = "max77620-gpio",
- .irqs = max77620_gpio_irqs,
- .num_irqs = ARRAY_SIZE(max77620_gpio_irqs),
- .num_regs = 1,
- .num_type_reg = 8,
- .irq_reg_stride = 1,
- .type_reg_stride = 1,
- .status_base = MAX77620_REG_IRQ_LVL2_GPIO,
- .type_base = MAX77620_REG_GPIO0,
+static void max77620_gpio_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct max77620_gpio *gpio = gpiochip_get_data(chip);
+
+ gpiochip_enable_irq(chip, data->hwirq);
+ gpio->irq_enabled[data->hwirq] = true;
+}
+
+static int max77620_gpio_set_irq_type(struct irq_data *data, unsigned int type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct max77620_gpio *gpio = gpiochip_get_data(chip);
+ unsigned int irq_type;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ irq_type = MAX77620_CNFG_GPIO_INT_RISING;
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ irq_type = MAX77620_CNFG_GPIO_INT_FALLING;
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ irq_type = MAX77620_CNFG_GPIO_INT_RISING |
+ MAX77620_CNFG_GPIO_INT_FALLING;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ gpio->irq_type[data->hwirq] = irq_type;
+
+ return 0;
+}
+
+static void max77620_gpio_bus_lock(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct max77620_gpio *gpio = gpiochip_get_data(chip);
+
+ mutex_lock(&gpio->buslock);
+}
+
+static void max77620_gpio_bus_sync_unlock(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct max77620_gpio *gpio = gpiochip_get_data(chip);
+ unsigned int value, offset = data->hwirq;
+ int err;
+
+ value = gpio->irq_enabled[offset] ? gpio->irq_type[offset] : 0;
+
+ err = regmap_update_bits(gpio->rmap, GPIO_REG_ADDR(offset),
+ MAX77620_CNFG_GPIO_INT_MASK, value);
+ if (err < 0)
+ dev_err(chip->parent, "failed to update interrupt mask: %d\n",
+ err);
+
+ mutex_unlock(&gpio->buslock);
+}
+
+static const struct irq_chip max77620_gpio_irqchip = {
+ .name = "max77620-gpio",
+ .irq_mask = max77620_gpio_irq_mask,
+ .irq_unmask = max77620_gpio_irq_unmask,
+ .irq_set_type = max77620_gpio_set_irq_type,
+ .irq_bus_lock = max77620_gpio_bus_lock,
+ .irq_bus_sync_unlock = max77620_gpio_bus_sync_unlock,
+ .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset)
@@ -192,13 +201,13 @@ static int max77620_gpio_set_debounce(struct max77620_gpio *mgpio,
case 0:
val = MAX77620_CNFG_GPIO_DBNC_None;
break;
- case 1 ... 8:
+ case 1 ... 8000:
val = MAX77620_CNFG_GPIO_DBNC_8ms;
break;
- case 9 ... 16:
+ case 8001 ... 16000:
val = MAX77620_CNFG_GPIO_DBNC_16ms;
break;
- case 17 ... 32:
+ case 16001 ... 32000:
val = MAX77620_CNFG_GPIO_DBNC_32ms;
break;
default:
@@ -254,48 +263,73 @@ static int max77620_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
return -ENOTSUPP;
}
-static int max77620_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
+static int max77620_gpio_irq_init_hw(struct gpio_chip *gc)
{
- struct max77620_gpio *mgpio = gpiochip_get_data(gc);
- struct max77620_chip *chip = dev_get_drvdata(mgpio->dev->parent);
+ struct max77620_gpio *gpio = gpiochip_get_data(gc);
+ unsigned int i;
+ int err;
+
+ /*
+ * GPIO interrupts may be left ON after bootloader, hence let's
+ * pre-initialize hardware to the expected state by disabling all
+ * the interrupts.
+ */
+ for (i = 0; i < MAX77620_GPIO_NR; i++) {
+ err = regmap_update_bits(gpio->rmap, GPIO_REG_ADDR(i),
+ MAX77620_CNFG_GPIO_INT_MASK, 0);
+ if (err < 0) {
+ dev_err(gpio->dev,
+ "failed to disable interrupt: %d\n", err);
+ return err;
+ }
+ }
- return regmap_irq_get_virq(chip->gpio_irq_data, offset);
+ return 0;
}
static int max77620_gpio_probe(struct platform_device *pdev)
{
struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct max77620_gpio *mgpio;
- int gpio_irq;
+ struct gpio_irq_chip *girq;
+ unsigned int gpio_irq;
int ret;
- gpio_irq = platform_get_irq(pdev, 0);
- if (gpio_irq <= 0) {
- dev_err(&pdev->dev, "GPIO irq not available %d\n", gpio_irq);
- return -ENODEV;
- }
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+
+ gpio_irq = ret;
mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL);
if (!mgpio)
return -ENOMEM;
+ mutex_init(&mgpio->buslock);
mgpio->rmap = chip->rmap;
mgpio->dev = &pdev->dev;
mgpio->gpio_chip.label = pdev->name;
- mgpio->gpio_chip.parent = &pdev->dev;
+ mgpio->gpio_chip.parent = pdev->dev.parent;
mgpio->gpio_chip.direction_input = max77620_gpio_dir_input;
mgpio->gpio_chip.get = max77620_gpio_get;
mgpio->gpio_chip.direction_output = max77620_gpio_dir_output;
mgpio->gpio_chip.set = max77620_gpio_set;
mgpio->gpio_chip.set_config = max77620_gpio_set_config;
- mgpio->gpio_chip.to_irq = max77620_gpio_to_irq;
mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR;
mgpio->gpio_chip.can_sleep = 1;
mgpio->gpio_chip.base = -1;
-#ifdef CONFIG_OF_GPIO
- mgpio->gpio_chip.of_node = pdev->dev.parent->of_node;
-#endif
+
+ girq = &mgpio->gpio_chip.irq;
+ gpio_irq_chip_set_chip(girq, &max77620_gpio_irqchip);
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_edge_irq;
+ girq->init_hw = max77620_gpio_irq_init_hw;
+ girq->threaded = true;
platform_set_drvdata(pdev, mgpio);
@@ -305,12 +339,11 @@ static int max77620_gpio_probe(struct platform_device *pdev)
return ret;
}
- ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, gpio_irq,
- IRQF_ONESHOT, -1,
- &max77620_gpio_irq_chip,
- &chip->gpio_irq_data);
+ ret = devm_request_threaded_irq(&pdev->dev, gpio_irq, NULL,
+ max77620_gpio_irqhandler, IRQF_ONESHOT,
+ "max77620-gpio", mgpio);
if (ret < 0) {
- dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret);
+ dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret);
return ret;
}
@@ -335,5 +368,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-max77650.c b/drivers/gpio/gpio-max77650.c
index 3f03f4e8956c..3075f2513c6f 100644
--- a/drivers/gpio/gpio-max77650.c
+++ b/drivers/gpio/gpio-max77650.c
@@ -188,3 +188,4 @@ module_platform_driver(max77650_gpio_driver);
MODULE_DESCRIPTION("MAXIM 77650/77651 GPIO driver");
MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:max77650-gpio");
diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c
index 8f466993cd24..37c5363e391e 100644
--- a/drivers/gpio/gpio-mb86s7x.c
+++ b/drivers/gpio/gpio-mb86s7x.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include "gpiolib.h"
+#include "gpiolib-acpi.h"
/*
* Only first 8bits of a register correspond to each pin,
@@ -144,7 +145,9 @@ static int mb86s70_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
for (index = 0;; index++) {
irq = platform_get_irq(to_platform_device(gc->parent), index);
- if (irq <= 0)
+ if (irq < 0)
+ return irq;
+ if (irq == 0)
break;
if (irq_get_irq_data(irq)->hwirq == offset)
return irq;
@@ -167,15 +170,13 @@ static int mb86s70_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gchip->base))
return PTR_ERR(gchip->base);
- if (!has_acpi_companion(&pdev->dev)) {
- gchip->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(gchip->clk))
- return PTR_ERR(gchip->clk);
+ gchip->clk = devm_clk_get_optional(&pdev->dev, NULL);
+ if (IS_ERR(gchip->clk))
+ return PTR_ERR(gchip->clk);
- ret = clk_prepare_enable(gchip->clk);
- if (ret)
- return ret;
- }
+ ret = clk_prepare_enable(gchip->clk);
+ if (ret)
+ return ret;
spin_lock_init(&gchip->lock);
@@ -185,15 +186,13 @@ static int mb86s70_gpio_probe(struct platform_device *pdev)
gchip->gc.free = mb86s70_gpio_free;
gchip->gc.get = mb86s70_gpio_get;
gchip->gc.set = mb86s70_gpio_set;
+ gchip->gc.to_irq = mb86s70_gpio_to_irq;
gchip->gc.label = dev_name(&pdev->dev);
gchip->gc.ngpio = 32;
gchip->gc.owner = THIS_MODULE;
gchip->gc.parent = &pdev->dev;
gchip->gc.base = -1;
- if (has_acpi_companion(&pdev->dev))
- gchip->gc.to_irq = mb86s70_gpio_to_irq;
-
ret = gpiochip_add_data(&gchip->gc, gchip);
if (ret) {
dev_err(&pdev->dev, "couldn't register gpio driver\n");
@@ -201,8 +200,7 @@ static int mb86s70_gpio_probe(struct platform_device *pdev)
return ret;
}
- if (has_acpi_companion(&pdev->dev))
- acpi_gpiochip_request_interrupts(&gchip->gc);
+ acpi_gpiochip_request_interrupts(&gchip->gc);
return 0;
}
@@ -211,8 +209,7 @@ static int mb86s70_gpio_remove(struct platform_device *pdev)
{
struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev);
- if (has_acpi_companion(&pdev->dev))
- acpi_gpiochip_free_interrupts(&gchip->gc);
+ acpi_gpiochip_free_interrupts(&gchip->gc);
gpiochip_remove(&gchip->gc);
clk_disable_unprepare(gchip->clk);
diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c
index f8194f7c6186..cd9b16dbe1a9 100644
--- a/drivers/gpio/gpio-mc33880.c
+++ b/drivers/gpio/gpio-mc33880.c
@@ -134,18 +134,14 @@ exit_destroy:
return ret;
}
-static int mc33880_remove(struct spi_device *spi)
+static void 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);
-
- return 0;
}
static struct spi_driver mc33880_driver = {
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
deleted file mode 100644
index a9f17cebd5ed..000000000000
--- a/drivers/gpio/gpio-mc9s08dz60.c
+++ /dev/null
@@ -1,112 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright 2009-2012 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * Author: Wu Guoxing <b39297@freescale.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/gpio/driver.h>
-
-#define GPIO_GROUP_NUM 2
-#define GPIO_NUM_PER_GROUP 8
-#define GPIO_NUM (GPIO_GROUP_NUM*GPIO_NUM_PER_GROUP)
-
-struct mc9s08dz60 {
- struct i2c_client *client;
- struct gpio_chip chip;
-};
-
-static void mc9s_gpio_to_reg_and_bit(int offset, u8 *reg, u8 *bit)
-{
- *reg = 0x20 + offset / GPIO_NUM_PER_GROUP;
- *bit = offset % GPIO_NUM_PER_GROUP;
-}
-
-static int mc9s08dz60_get_value(struct gpio_chip *gc, unsigned offset)
-{
- u8 reg, bit;
- s32 value;
- struct mc9s08dz60 *mc9s = gpiochip_get_data(gc);
-
- mc9s_gpio_to_reg_and_bit(offset, &reg, &bit);
- value = i2c_smbus_read_byte_data(mc9s->client, reg);
-
- return (value >= 0) ? (value >> bit) & 0x1 : 0;
-}
-
-static int mc9s08dz60_set(struct mc9s08dz60 *mc9s, unsigned offset, int val)
-{
- u8 reg, bit;
- s32 value;
-
- mc9s_gpio_to_reg_and_bit(offset, &reg, &bit);
- value = i2c_smbus_read_byte_data(mc9s->client, reg);
- if (value >= 0) {
- if (val)
- value |= 1 << bit;
- else
- value &= ~(1 << bit);
-
- return i2c_smbus_write_byte_data(mc9s->client, reg, value);
- } else
- return value;
-
-}
-
-
-static void mc9s08dz60_set_value(struct gpio_chip *gc, unsigned offset, int val)
-{
- struct mc9s08dz60 *mc9s = gpiochip_get_data(gc);
-
- mc9s08dz60_set(mc9s, offset, val);
-}
-
-static int mc9s08dz60_direction_output(struct gpio_chip *gc,
- unsigned offset, int val)
-{
- struct mc9s08dz60 *mc9s = gpiochip_get_data(gc);
-
- return mc9s08dz60_set(mc9s, offset, val);
-}
-
-static int mc9s08dz60_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct mc9s08dz60 *mc9s;
-
- mc9s = devm_kzalloc(&client->dev, sizeof(*mc9s), GFP_KERNEL);
- if (!mc9s)
- return -ENOMEM;
-
- mc9s->chip.label = client->name;
- mc9s->chip.base = -1;
- mc9s->chip.parent = &client->dev;
- mc9s->chip.owner = THIS_MODULE;
- mc9s->chip.ngpio = GPIO_NUM;
- mc9s->chip.can_sleep = true;
- mc9s->chip.get = mc9s08dz60_get_value;
- mc9s->chip.set = mc9s08dz60_set_value;
- mc9s->chip.direction_output = mc9s08dz60_direction_output;
- mc9s->client = client;
- i2c_set_clientdata(client, mc9s);
-
- return devm_gpiochip_add_data(&client->dev, &mc9s->chip, mc9s);
-}
-
-static const struct i2c_device_id mc9s08dz60_id[] = {
- {"mc9s08dz60", 0},
- {},
-};
-
-static struct i2c_driver mc9s08dz60_i2c_driver = {
- .driver = {
- .name = "mc9s08dz60",
- },
- .probe = mc9s08dz60_probe,
- .id_table = mc9s08dz60_id,
-};
-builtin_i2c_driver(mc9s08dz60_i2c_driver);
diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c
index 70fdb42a8e88..a035a9bcb57c 100644
--- a/drivers/gpio/gpio-menz127.c
+++ b/drivers/gpio/gpio-menz127.c
@@ -64,7 +64,7 @@ static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio,
debounce /= 50;
}
- spin_lock(&gc->bgpio_lock);
+ raw_spin_lock(&gc->bgpio_lock);
db_en = readl(priv->reg_base + MEN_Z127_DBER);
@@ -79,7 +79,7 @@ static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio,
writel(db_en, priv->reg_base + MEN_Z127_DBER);
writel(db_cnt, priv->reg_base + GPIO_TO_DBCNT_REG(gpio));
- spin_unlock(&gc->bgpio_lock);
+ raw_spin_unlock(&gc->bgpio_lock);
return 0;
}
@@ -91,7 +91,7 @@ static int men_z127_set_single_ended(struct gpio_chip *gc,
struct men_z127_gpio *priv = gpiochip_get_data(gc);
u32 od_en;
- spin_lock(&gc->bgpio_lock);
+ raw_spin_lock(&gc->bgpio_lock);
od_en = readl(priv->reg_base + MEN_Z127_ODER);
if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN)
@@ -101,7 +101,7 @@ static int men_z127_set_single_ended(struct gpio_chip *gc,
od_en &= ~BIT(offset);
writel(od_en, priv->reg_base + MEN_Z127_ODER);
- spin_unlock(&gc->bgpio_lock);
+ raw_spin_unlock(&gc->bgpio_lock);
return 0;
}
@@ -211,3 +211,4 @@ MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
MODULE_DESCRIPTION("MEN 16z127 GPIO Controller");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("mcb:16z127");
+MODULE_IMPORT_NS(MCB);
diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c
index 3302125e5265..72ac09a59702 100644
--- a/drivers/gpio/gpio-merrifield.c
+++ b/drivers/gpio/gpio-merrifield.c
@@ -162,7 +162,10 @@ static int mrfld_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
- return !(readl(gpdr) & BIT(offset % 32));
+ if (readl(gpdr) & BIT(offset % 32))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int mrfld_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
@@ -191,6 +194,11 @@ static int mrfld_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
{
u32 debounce;
+ if ((pinconf_to_config_param(config) == PIN_CONFIG_BIAS_DISABLE) ||
+ (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_UP) ||
+ (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_DOWN))
+ return gpiochip_generic_config(chip, offset, config);
+
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
return -ENOTSUPP;
@@ -212,10 +220,8 @@ static void mrfld_irq_ack(struct irq_data *d)
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
-static void mrfld_irq_unmask_mask(struct irq_data *d, bool unmask)
+static void mrfld_irq_unmask_mask(struct mrfld_gpio *priv, u32 gpio, bool unmask)
{
- struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
- u32 gpio = irqd_to_hwirq(d);
void __iomem *gimr = gpio_reg(&priv->chip, gpio, GIMR);
unsigned long flags;
u32 value;
@@ -233,12 +239,20 @@ static void mrfld_irq_unmask_mask(struct irq_data *d, bool unmask)
static void mrfld_irq_mask(struct irq_data *d)
{
- mrfld_irq_unmask_mask(d, false);
+ struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
+ u32 gpio = irqd_to_hwirq(d);
+
+ mrfld_irq_unmask_mask(priv, gpio, false);
+ gpiochip_disable_irq(&priv->chip, gpio);
}
static void mrfld_irq_unmask(struct irq_data *d)
{
- mrfld_irq_unmask_mask(d, true);
+ struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
+ u32 gpio = irqd_to_hwirq(d);
+
+ gpiochip_enable_irq(&priv->chip, gpio);
+ mrfld_irq_unmask_mask(priv, gpio, true);
}
static int mrfld_irq_set_type(struct irq_data *d, unsigned int type)
@@ -321,13 +335,15 @@ static int mrfld_irq_set_wake(struct irq_data *d, unsigned int on)
return 0;
}
-static struct irq_chip mrfld_irqchip = {
+static const struct irq_chip mrfld_irqchip = {
.name = "gpio-merrifield",
.irq_ack = mrfld_irq_ack,
.irq_mask = mrfld_irq_mask,
.irq_unmask = mrfld_irq_unmask,
.irq_set_type = mrfld_irq_set_type,
.irq_set_wake = mrfld_irq_set_wake,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void mrfld_irq_handler(struct irq_desc *desc)
@@ -351,19 +367,16 @@ static void mrfld_irq_handler(struct irq_desc *desc)
/* Only interrupts that are enabled */
pending &= enabled;
- for_each_set_bit(gpio, &pending, 32) {
- unsigned int irq;
-
- irq = irq_find_mapping(gc->irq.domain, base + gpio);
- generic_handle_irq(irq);
- }
+ for_each_set_bit(gpio, &pending, 32)
+ generic_handle_domain_irq(gc->irq.domain, base + gpio);
}
chained_irq_exit(irqchip, desc);
}
-static void mrfld_irq_init_hw(struct mrfld_gpio *priv)
+static int mrfld_irq_init_hw(struct gpio_chip *chip)
{
+ struct mrfld_gpio *priv = gpiochip_get_data(chip);
void __iomem *reg;
unsigned int base;
@@ -375,6 +388,8 @@ static void mrfld_irq_init_hw(struct mrfld_gpio *priv)
reg = gpio_reg(&priv->chip, base, GFER);
writel(0, reg);
}
+
+ return 0;
}
static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv)
@@ -393,14 +408,39 @@ static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv)
return name;
}
-static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int mrfld_gpio_add_pin_ranges(struct gpio_chip *chip)
{
+ struct mrfld_gpio *priv = gpiochip_get_data(chip);
const struct mrfld_gpio_pinrange *range;
const char *pinctrl_dev_name;
+ unsigned int i;
+ int retval;
+
+ pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name(priv);
+ if (!pinctrl_dev_name)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) {
+ range = &mrfld_gpio_ranges[i];
+ retval = gpiochip_add_pin_range(&priv->chip, pinctrl_dev_name,
+ range->gpio_base,
+ range->pin_base,
+ range->npins);
+ if (retval) {
+ dev_err(priv->dev, "failed to add GPIO pin range\n");
+ return retval;
+ }
+ }
+
+ return 0;
+}
+
+static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct gpio_irq_chip *girq;
struct mrfld_gpio *priv;
u32 gpio_base, irq_base;
void __iomem *base;
- unsigned int i;
int retval;
retval = pcim_enable_device(pdev);
@@ -415,8 +455,8 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
base = pcim_iomap_table(pdev)[1];
- irq_base = readl(base);
- gpio_base = readl(sizeof(u32) + base);
+ irq_base = readl(base + 0 * sizeof(u32));
+ gpio_base = readl(base + 1 * sizeof(u32));
/* Release the IO mapping, since we already get the info from BAR1 */
pcim_iounmap_regions(pdev, BIT(1));
@@ -441,42 +481,35 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
priv->chip.base = gpio_base;
priv->chip.ngpio = MRFLD_NGPIO;
priv->chip.can_sleep = false;
+ priv->chip.add_pin_ranges = mrfld_gpio_add_pin_ranges;
raw_spin_lock_init(&priv->lock);
- pci_set_drvdata(pdev, priv);
- retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
- if (retval) {
- dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
+ retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (retval < 0)
return retval;
- }
- pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name(priv);
- for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) {
- range = &mrfld_gpio_ranges[i];
- retval = gpiochip_add_pin_range(&priv->chip,
- pinctrl_dev_name,
- range->gpio_base,
- range->pin_base,
- range->npins);
- if (retval) {
- dev_err(&pdev->dev, "failed to add GPIO pin range\n");
- return retval;
- }
- }
+ girq = &priv->chip.irq;
+ gpio_irq_chip_set_chip(girq, &mrfld_irqchip);
+ girq->init_hw = mrfld_irq_init_hw;
+ girq->parent_handler = mrfld_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents,
+ sizeof(*girq->parents), GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = pci_irq_vector(pdev, 0);
+ girq->first = irq_base;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
- retval = gpiochip_irqchip_add(&priv->chip, &mrfld_irqchip, irq_base,
- handle_bad_irq, IRQ_TYPE_NONE);
+ retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
if (retval) {
- dev_err(&pdev->dev, "could not connect irqchip to gpiochip\n");
+ dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
return retval;
}
- mrfld_irq_init_hw(priv);
-
- gpiochip_set_chained_irqchip(&priv->chip, &mrfld_irqchip, pdev->irq,
- mrfld_irq_handler);
-
+ pci_set_drvdata(pdev, priv);
return 0;
}
diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c
index 92b6e958cfed..48e3768a830e 100644
--- a/drivers/gpio/gpio-ml-ioh.c
+++ b/drivers/gpio/gpio-ml-ioh.c
@@ -44,7 +44,7 @@ struct ioh_regs {
/**
* struct ioh_gpio_reg_data - The register store data.
- * @ien_reg To store contents of interrupt enable register.
+ * @ien_reg: To store contents of interrupt enable register.
* @imask_reg: To store contents of interrupt mask regist
* @po_reg: To store contents of PO register.
* @pm_reg: To store contents of PM register.
@@ -98,9 +98,9 @@ static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
spin_lock_irqsave(&chip->spinlock, flags);
reg_val = ioread32(&chip->reg->regs[chip->ch].po);
if (val)
- reg_val |= (1 << nr);
+ reg_val |= BIT(nr);
else
- reg_val &= ~(1 << nr);
+ reg_val &= ~BIT(nr);
iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
spin_unlock_irqrestore(&chip->spinlock, flags);
@@ -110,7 +110,7 @@ static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr)
{
struct ioh_gpio *chip = gpiochip_get_data(gpio);
- return !!(ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr));
+ return !!(ioread32(&chip->reg->regs[chip->ch].pi) & BIT(nr));
}
static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
@@ -122,16 +122,16 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
unsigned long flags;
spin_lock_irqsave(&chip->spinlock, flags);
- pm = ioread32(&chip->reg->regs[chip->ch].pm) &
- ((1 << num_ports[chip->ch]) - 1);
- pm |= (1 << nr);
+ pm = ioread32(&chip->reg->regs[chip->ch].pm);
+ pm &= BIT(num_ports[chip->ch]) - 1;
+ pm |= BIT(nr);
iowrite32(pm, &chip->reg->regs[chip->ch].pm);
reg_val = ioread32(&chip->reg->regs[chip->ch].po);
if (val)
- reg_val |= (1 << nr);
+ reg_val |= BIT(nr);
else
- reg_val &= ~(1 << nr);
+ reg_val &= ~BIT(nr);
iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
spin_unlock_irqrestore(&chip->spinlock, flags);
@@ -146,20 +146,19 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
unsigned long flags;
spin_lock_irqsave(&chip->spinlock, flags);
- pm = ioread32(&chip->reg->regs[chip->ch].pm) &
- ((1 << num_ports[chip->ch]) - 1);
- pm &= ~(1 << nr);
+ pm = ioread32(&chip->reg->regs[chip->ch].pm);
+ pm &= BIT(num_ports[chip->ch]) - 1;
+ pm &= ~BIT(nr);
iowrite32(pm, &chip->reg->regs[chip->ch].pm);
spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
-#ifdef CONFIG_PM
/*
* Save register configuration and disable interrupts.
*/
-static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
+static void __maybe_unused ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
{
int i;
@@ -185,7 +184,7 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
/*
* This function restores the register configuration of the GPIO device.
*/
-static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
+static void __maybe_unused ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
{
int i;
@@ -207,7 +206,6 @@ static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
&chip->reg->ioh_sel_reg[i]);
}
}
-#endif
static int ioh_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
{
@@ -306,7 +304,7 @@ static void ioh_irq_unmask(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct ioh_gpio *chip = gc->private;
- iowrite32(1 << (d->irq - chip->irq_base),
+ iowrite32(BIT(d->irq - chip->irq_base),
&chip->reg->regs[chip->ch].imaskclr);
}
@@ -315,7 +313,7 @@ static void ioh_irq_mask(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct ioh_gpio *chip = gc->private;
- iowrite32(1 << (d->irq - chip->irq_base),
+ iowrite32(BIT(d->irq - chip->irq_base),
&chip->reg->regs[chip->ch].imask);
}
@@ -328,7 +326,7 @@ static void ioh_irq_disable(struct irq_data *d)
spin_lock_irqsave(&chip->spinlock, flags);
ien = ioread32(&chip->reg->regs[chip->ch].ien);
- ien &= ~(1 << (d->irq - chip->irq_base));
+ ien &= ~BIT(d->irq - chip->irq_base);
iowrite32(ien, &chip->reg->regs[chip->ch].ien);
spin_unlock_irqrestore(&chip->spinlock, flags);
}
@@ -342,7 +340,7 @@ static void ioh_irq_enable(struct irq_data *d)
spin_lock_irqsave(&chip->spinlock, flags);
ien = ioread32(&chip->reg->regs[chip->ch].ien);
- ien |= 1 << (d->irq - chip->irq_base);
+ ien |= BIT(d->irq - chip->irq_base);
iowrite32(ien, &chip->reg->regs[chip->ch].ien);
spin_unlock_irqrestore(&chip->spinlock, flags);
}
@@ -403,6 +401,7 @@ static int ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
static int ioh_gpio_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
+ struct device *dev = &pdev->dev;
int ret;
int i, j;
struct ioh_gpio *chip;
@@ -410,159 +409,91 @@ static int ioh_gpio_probe(struct pci_dev *pdev,
void *chip_save;
int irq_base;
- ret = pci_enable_device(pdev);
+ ret = pcim_enable_device(pdev);
if (ret) {
- dev_err(&pdev->dev, "%s : pci_enable_device failed", __func__);
- goto err_pci_enable;
+ dev_err(dev, "%s : pcim_enable_device failed", __func__);
+ return ret;
}
- ret = pci_request_regions(pdev, KBUILD_MODNAME);
+ ret = pcim_iomap_regions(pdev, BIT(1), KBUILD_MODNAME);
if (ret) {
- dev_err(&pdev->dev, "pci_request_regions failed-%d", ret);
- goto err_request_regions;
+ dev_err(dev, "pcim_iomap_regions failed-%d", ret);
+ return ret;
}
- base = pci_iomap(pdev, 1, 0);
+ base = pcim_iomap_table(pdev)[1];
if (!base) {
- dev_err(&pdev->dev, "%s : pci_iomap failed", __func__);
- ret = -ENOMEM;
- goto err_iomap;
+ dev_err(dev, "%s : pcim_iomap_table failed", __func__);
+ return -ENOMEM;
}
- chip_save = kcalloc(8, sizeof(*chip), GFP_KERNEL);
+ chip_save = devm_kcalloc(dev, 8, sizeof(*chip), GFP_KERNEL);
if (chip_save == NULL) {
- ret = -ENOMEM;
- goto err_kzalloc;
+ return -ENOMEM;
}
chip = chip_save;
for (i = 0; i < 8; i++, chip++) {
- chip->dev = &pdev->dev;
+ chip->dev = dev;
chip->base = base;
chip->reg = chip->base;
chip->ch = i;
spin_lock_init(&chip->spinlock);
ioh_gpio_setup(chip, num_ports[i]);
- ret = gpiochip_add_data(&chip->gpio, chip);
+ ret = devm_gpiochip_add_data(dev, &chip->gpio, chip);
if (ret) {
- dev_err(&pdev->dev, "IOH gpio: Failed to register GPIO\n");
- goto err_gpiochip_add;
+ dev_err(dev, "IOH gpio: Failed to register GPIO\n");
+ return ret;
}
}
chip = chip_save;
for (j = 0; j < 8; j++, chip++) {
- irq_base = devm_irq_alloc_descs(&pdev->dev, -1, IOH_IRQ_BASE,
+ irq_base = devm_irq_alloc_descs(dev, -1, IOH_IRQ_BASE,
num_ports[j], NUMA_NO_NODE);
if (irq_base < 0) {
- dev_warn(&pdev->dev,
+ dev_warn(dev,
"ml_ioh_gpio: Failed to get IRQ base num\n");
- ret = irq_base;
- goto err_gpiochip_add;
+ return irq_base;
}
chip->irq_base = irq_base;
ret = ioh_gpio_alloc_generic_chip(chip,
irq_base, num_ports[j]);
if (ret)
- goto err_gpiochip_add;
+ return ret;
}
chip = chip_save;
- ret = devm_request_irq(&pdev->dev, pdev->irq, ioh_gpio_handler,
+ ret = devm_request_irq(dev, pdev->irq, ioh_gpio_handler,
IRQF_SHARED, KBUILD_MODNAME, chip);
if (ret != 0) {
- dev_err(&pdev->dev,
- "%s request_irq failed\n", __func__);
- goto err_gpiochip_add;
+ dev_err(dev, "%s request_irq failed\n", __func__);
+ return ret;
}
pci_set_drvdata(pdev, chip);
return 0;
-
-err_gpiochip_add:
- chip = chip_save;
- while (--i >= 0) {
- gpiochip_remove(&chip->gpio);
- chip++;
- }
- kfree(chip_save);
-
-err_kzalloc:
- pci_iounmap(pdev, base);
-
-err_iomap:
- pci_release_regions(pdev);
-
-err_request_regions:
- pci_disable_device(pdev);
-
-err_pci_enable:
-
- dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret);
- return ret;
}
-static void ioh_gpio_remove(struct pci_dev *pdev)
+static int __maybe_unused ioh_gpio_suspend(struct device *dev)
{
- int i;
- struct ioh_gpio *chip = pci_get_drvdata(pdev);
- void *chip_save;
-
- chip_save = chip;
-
- for (i = 0; i < 8; i++, chip++)
- gpiochip_remove(&chip->gpio);
-
- chip = chip_save;
- pci_iounmap(pdev, chip->base);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- kfree(chip);
-}
-
-#ifdef CONFIG_PM
-static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- s32 ret;
- struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ struct ioh_gpio *chip = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&chip->spinlock, flags);
ioh_gpio_save_reg_conf(chip);
spin_unlock_irqrestore(&chip->spinlock, flags);
- ret = pci_save_state(pdev);
- if (ret) {
- dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret);
- return ret;
- }
- pci_disable_device(pdev);
- pci_set_power_state(pdev, PCI_D0);
- ret = pci_enable_wake(pdev, PCI_D0, 1);
- if (ret)
- dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret);
-
return 0;
}
-static int ioh_gpio_resume(struct pci_dev *pdev)
+static int __maybe_unused ioh_gpio_resume(struct device *dev)
{
- s32 ret;
- struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ struct ioh_gpio *chip = dev_get_drvdata(dev);
unsigned long flags;
- ret = pci_enable_wake(pdev, PCI_D0, 0);
-
- pci_set_power_state(pdev, PCI_D0);
- ret = pci_enable_device(pdev);
- if (ret) {
- dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret);
- return ret;
- }
- pci_restore_state(pdev);
-
spin_lock_irqsave(&chip->spinlock, flags);
iowrite32(0x01, &chip->reg->srst);
iowrite32(0x00, &chip->reg->srst);
@@ -571,10 +502,8 @@ static int ioh_gpio_resume(struct pci_dev *pdev)
return 0;
}
-#else
-#define ioh_gpio_suspend NULL
-#define ioh_gpio_resume NULL
-#endif
+
+static SIMPLE_DEV_PM_OPS(ioh_gpio_pm_ops, ioh_gpio_suspend, ioh_gpio_resume);
static const struct pci_device_id ioh_gpio_pcidev_id[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) },
@@ -586,9 +515,9 @@ static struct pci_driver ioh_gpio_driver = {
.name = "ml_ioh_gpio",
.id_table = ioh_gpio_pcidev_id,
.probe = ioh_gpio_probe,
- .remove = ioh_gpio_remove,
- .suspend = ioh_gpio_suspend,
- .resume = ioh_gpio_resume
+ .driver = {
+ .pm = &ioh_gpio_pm_ops,
+ },
};
module_pci_driver(ioh_gpio_driver);
diff --git a/drivers/gpio/gpio-mlxbf.c b/drivers/gpio/gpio-mlxbf.c
index 894aaf55fc96..1fa9973f55b9 100644
--- a/drivers/gpio/gpio-mlxbf.c
+++ b/drivers/gpio/gpio-mlxbf.c
@@ -127,7 +127,7 @@ static int mlxbf_gpio_resume(struct platform_device *pdev)
}
#endif
-static const struct acpi_device_id mlxbf_gpio_acpi_match[] = {
+static const struct acpi_device_id __maybe_unused mlxbf_gpio_acpi_match[] = {
{ "MLNXBF02", 0 },
{}
};
diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c
new file mode 100644
index 000000000000..64cb060d9d75
--- /dev/null
+++ b/drivers/gpio/gpio-mlxbf2.c
@@ -0,0 +1,462 @@
+// 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>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/resource.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+/*
+ * There are 3 YU GPIO blocks:
+ * gpio[0]: HOST_GPIO0->HOST_GPIO31
+ * gpio[1]: HOST_GPIO32->HOST_GPIO63
+ * gpio[2]: HOST_GPIO64->HOST_GPIO69
+ */
+#define MLXBF2_GPIO_MAX_PINS_PER_BLOCK 32
+
+/*
+ * arm_gpio_lock register:
+ * bit[31] lock status: active if set
+ * bit[15:0] set lock
+ * The lock is enabled only if 0xd42f is written to this field
+ */
+#define YU_ARM_GPIO_LOCK_ADDR 0x2801088
+#define YU_ARM_GPIO_LOCK_SIZE 0x8
+#define YU_LOCK_ACTIVE_BIT(val) (val >> 31)
+#define YU_ARM_GPIO_LOCK_ACQUIRE 0xd42f
+#define YU_ARM_GPIO_LOCK_RELEASE 0x0
+
+/*
+ * gpio[x] block registers and their offset
+ */
+#define YU_GPIO_DATAIN 0x04
+#define YU_GPIO_MODE1 0x08
+#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;
+ u32 gpio_mode1;
+};
+
+/* 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;
+
+ struct mlxbf2_gpio_context_save_regs *csave_regs;
+};
+
+/* BlueField-2 gpio shared structure. */
+struct mlxbf2_gpio_param {
+ void __iomem *io;
+ struct resource *res;
+ struct mutex *lock;
+};
+
+static struct resource yu_arm_gpio_lock_res =
+ DEFINE_RES_MEM_NAMED(YU_ARM_GPIO_LOCK_ADDR, YU_ARM_GPIO_LOCK_SIZE, "YU_ARM_GPIO_LOCK");
+
+static DEFINE_MUTEX(yu_arm_gpio_lock_mutex);
+
+static struct mlxbf2_gpio_param yu_arm_gpio_lock_param = {
+ .res = &yu_arm_gpio_lock_res,
+ .lock = &yu_arm_gpio_lock_mutex,
+};
+
+/* Request memory region and map yu_arm_gpio_lock resource */
+static int mlxbf2_gpio_get_lock_res(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ resource_size_t size;
+ int ret = 0;
+
+ mutex_lock(yu_arm_gpio_lock_param.lock);
+
+ /* Check if the memory map already exists */
+ if (yu_arm_gpio_lock_param.io)
+ goto exit;
+
+ res = yu_arm_gpio_lock_param.res;
+ size = resource_size(res);
+
+ if (!devm_request_mem_region(dev, res->start, size, res->name)) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ yu_arm_gpio_lock_param.io = devm_ioremap(dev, res->start, size);
+ if (!yu_arm_gpio_lock_param.io)
+ ret = -ENOMEM;
+
+exit:
+ mutex_unlock(yu_arm_gpio_lock_param.lock);
+
+ return ret;
+}
+
+/*
+ * Acquire the YU arm_gpio_lock to be able to change the direction
+ * mode. If the lock_active bit is already set, return an error.
+ */
+static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
+{
+ u32 arm_gpio_lock_val;
+
+ mutex_lock(yu_arm_gpio_lock_param.lock);
+ raw_spin_lock(&gs->gc.bgpio_lock);
+
+ arm_gpio_lock_val = readl(yu_arm_gpio_lock_param.io);
+
+ /*
+ * When lock active bit[31] is set, ModeX is write enabled
+ */
+ if (YU_LOCK_ACTIVE_BIT(arm_gpio_lock_val)) {
+ raw_spin_unlock(&gs->gc.bgpio_lock);
+ mutex_unlock(yu_arm_gpio_lock_param.lock);
+ return -EINVAL;
+ }
+
+ writel(YU_ARM_GPIO_LOCK_ACQUIRE, yu_arm_gpio_lock_param.io);
+
+ return 0;
+}
+
+/*
+ * Release the YU arm_gpio_lock after changing the direction mode.
+ */
+static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs)
+ __releases(&gs->gc.bgpio_lock)
+ __releases(yu_arm_gpio_lock_param.lock)
+{
+ writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io);
+ raw_spin_unlock(&gs->gc.bgpio_lock);
+ mutex_unlock(yu_arm_gpio_lock_param.lock);
+}
+
+/*
+ * mode0 and mode1 are both locked by the gpio_lock field.
+ *
+ * Together, mode0 and mode1 define the gpio Mode dependeing also
+ * on Reg_DataOut.
+ *
+ * {mode1,mode0}:{Reg_DataOut=0,Reg_DataOut=1}->{DataOut=0,DataOut=1}
+ *
+ * {0,0}:Reg_DataOut{0,1}->{Z,Z} Input PAD
+ * {0,1}:Reg_DataOut{0,1}->{0,1} Full drive Output PAD
+ * {1,0}:Reg_DataOut{0,1}->{0,Z} 0-set PAD to low, 1-float
+ * {1,1}:Reg_DataOut{0,1}->{Z,1} 0-float, 1-set PAD to high
+ */
+
+/*
+ * Set input direction:
+ * {mode1,mode0} = {0,0}
+ */
+static int mlxbf2_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mlxbf2_gpio_context *gs = gpiochip_get_data(chip);
+ int ret;
+
+ /*
+ * Although the arm_gpio_lock was set in the probe function, check again
+ * if it is still enabled to be able to write to the ModeX registers.
+ */
+ ret = mlxbf2_gpio_lock_acquire(gs);
+ if (ret < 0)
+ return ret;
+
+ writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE0_CLEAR);
+ writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE1_CLEAR);
+
+ mlxbf2_gpio_lock_release(gs);
+
+ return ret;
+}
+
+/*
+ * Set output direction:
+ * {mode1,mode0} = {0,1}
+ */
+static int mlxbf2_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset,
+ int value)
+{
+ struct mlxbf2_gpio_context *gs = gpiochip_get_data(chip);
+ int ret = 0;
+
+ /*
+ * Although the arm_gpio_lock was set in the probe function,
+ * check again it is still enabled to be able to write to the
+ * ModeX registers.
+ */
+ ret = mlxbf2_gpio_lock_acquire(gs);
+ if (ret < 0)
+ return ret;
+
+ writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE1_CLEAR);
+ writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE0_SET);
+
+ mlxbf2_gpio_lock_release(gs);
+
+ 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;
+
+ raw_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);
+ raw_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;
+
+ raw_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);
+ raw_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;
+ }
+
+ raw_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);
+ }
+ raw_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;
+ const char *name;
+ int ret, irq;
+
+ name = dev_name(dev);
+
+ gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);
+ if (!gs)
+ return -ENOMEM;
+
+ /* YU GPIO block address */
+ gs->gpio_io = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(gs->gpio_io))
+ return PTR_ERR(gs->gpio_io);
+
+ ret = mlxbf2_gpio_get_lock_res(pdev);
+ if (ret) {
+ dev_err(dev, "Failed to get yu_arm_gpio_lock resource\n");
+ return ret;
+ }
+
+ if (device_property_read_u32(dev, "npins", &npins))
+ npins = MLXBF2_GPIO_MAX_PINS_PER_BLOCK;
+
+ gc = &gs->gc;
+
+ ret = bgpio_init(gc, dev, 4,
+ gs->gpio_io + YU_GPIO_DATAIN,
+ gs->gpio_io + YU_GPIO_DATASET,
+ gs->gpio_io + YU_GPIO_DATACLEAR,
+ NULL,
+ NULL,
+ 0);
+
+ if (ret) {
+ dev_err(dev, "bgpio_init failed\n");
+ return ret;
+ }
+
+ gc->direction_input = mlxbf2_gpio_direction_input;
+ gc->direction_output = mlxbf2_gpio_direction_output;
+ 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);
+ if (ret) {
+ dev_err(dev, "Failed adding memory mapped gpiochip\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev)
+{
+ struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev);
+
+ gs->csave_regs->gpio_mode0 = readl(gs->gpio_io +
+ YU_GPIO_MODE0);
+ gs->csave_regs->gpio_mode1 = readl(gs->gpio_io +
+ YU_GPIO_MODE1);
+
+ return 0;
+}
+
+static int __maybe_unused mlxbf2_gpio_resume(struct device *dev)
+{
+ struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev);
+
+ writel(gs->csave_regs->gpio_mode0, gs->gpio_io +
+ YU_GPIO_MODE0);
+ writel(gs->csave_regs->gpio_mode1, gs->gpio_io +
+ YU_GPIO_MODE1);
+
+ return 0;
+}
+static SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume);
+
+static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = {
+ { "MLNXBF22", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, mlxbf2_gpio_acpi_match);
+
+static struct platform_driver mlxbf2_gpio_driver = {
+ .driver = {
+ .name = "mlxbf2_gpio",
+ .acpi_match_table = mlxbf2_gpio_acpi_match,
+ .pm = &mlxbf2_pm_ops,
+ },
+ .probe = mlxbf2_gpio_probe,
+};
+
+module_platform_driver(mlxbf2_gpio_driver);
+
+MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver");
+MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c
index f460d71b0c92..538e31fe8903 100644
--- a/drivers/gpio/gpio-mm-lantiq.c
+++ b/drivers/gpio/gpio-mm-lantiq.c
@@ -36,7 +36,7 @@ struct ltq_mm {
* @chip: Pointer to our private data structure.
*
* Write the shadow value to the EBU to set the gpios. We need to set the
- * global EBU lock to make sure that PCI/MTD dont break.
+ * global EBU lock to make sure that PCI/MTD don't break.
*/
static void ltq_mm_apply(struct ltq_mm *chip)
{
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index 6f904c874678..d9dff3dc92ae 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -195,8 +195,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
*bits &= ~*mask;
/* Create a mirrored mask */
- bit = -1;
- while ((bit = find_next_bit(mask, gc->ngpio, bit + 1)) < gc->ngpio)
+ for_each_set_bit(bit, mask, gc->ngpio)
readmask |= bgpio_line2mask(gc, bit);
/* Read the register */
@@ -206,8 +205,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
* Mirror the result into the "bits" result, this will give line 0
* in bit 0 ... line 31 in bit 31 for a 32bit register.
*/
- bit = -1;
- while ((bit = find_next_bit(&val, gc->ngpio, bit + 1)) < gc->ngpio)
+ for_each_set_bit(bit, &val, gc->ngpio)
*bits |= bgpio_line2mask(gc, bit);
return 0;
@@ -222,7 +220,7 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
unsigned long mask = bgpio_line2mask(gc, gpio);
unsigned long flags;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
if (val)
gc->bgpio_data |= mask;
@@ -231,7 +229,7 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
gc->write_reg(gc->reg_dat, gc->bgpio_data);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
@@ -250,7 +248,7 @@ static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
unsigned long mask = bgpio_line2mask(gc, gpio);
unsigned long flags;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
if (val)
gc->bgpio_data |= mask;
@@ -259,7 +257,7 @@ static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
gc->write_reg(gc->reg_set, gc->bgpio_data);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static void bgpio_multiple_get_masks(struct gpio_chip *gc,
@@ -272,15 +270,11 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc,
*set_mask = 0;
*clear_mask = 0;
- for (i = 0; i < gc->bgpio_bits; i++) {
- if (*mask == 0)
- break;
- if (__test_and_clear_bit(i, mask)) {
- if (test_bit(i, bits))
- *set_mask |= bgpio_line2mask(gc, i);
- else
- *clear_mask |= bgpio_line2mask(gc, i);
- }
+ for_each_set_bit(i, mask, gc->bgpio_bits) {
+ if (test_bit(i, bits))
+ *set_mask |= bgpio_line2mask(gc, i);
+ else
+ *clear_mask |= bgpio_line2mask(gc, i);
}
}
@@ -292,7 +286,7 @@ static void bgpio_set_multiple_single_reg(struct gpio_chip *gc,
unsigned long flags;
unsigned long set_mask, clear_mask;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
@@ -301,7 +295,7 @@ static void bgpio_set_multiple_single_reg(struct gpio_chip *gc,
gc->write_reg(reg, gc->bgpio_data);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
@@ -353,7 +347,7 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
unsigned long flags;
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio);
@@ -362,7 +356,7 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
if (gc->reg_dir_out)
gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@@ -370,24 +364,30 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
{
/* Return 0 if output, 1 if input */
- if (gc->bgpio_dir_unreadable)
- return !(gc->bgpio_dir & bgpio_line2mask(gc, gpio));
- if (gc->reg_dir_out)
- return !(gc->read_reg(gc->reg_dir_out) & bgpio_line2mask(gc, gpio));
+ if (gc->bgpio_dir_unreadable) {
+ if (gc->bgpio_dir & bgpio_line2mask(gc, gpio))
+ return GPIO_LINE_DIRECTION_OUT;
+ return GPIO_LINE_DIRECTION_IN;
+ }
+
+ if (gc->reg_dir_out) {
+ if (gc->read_reg(gc->reg_dir_out) & bgpio_line2mask(gc, gpio))
+ return GPIO_LINE_DIRECTION_OUT;
+ return GPIO_LINE_DIRECTION_IN;
+ }
+
if (gc->reg_dir_in)
- return !!(gc->read_reg(gc->reg_dir_in) & bgpio_line2mask(gc, gpio));
+ if (!(gc->read_reg(gc->reg_dir_in) & bgpio_line2mask(gc, gpio)))
+ return GPIO_LINE_DIRECTION_OUT;
- /* This should not happen */
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
}
-static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{
unsigned long flags;
- gc->set(gc, gpio, val);
-
- spin_lock_irqsave(&gc->bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
gc->bgpio_dir |= bgpio_line2mask(gc, gpio);
@@ -396,8 +396,22 @@ static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
if (gc->reg_dir_out)
gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
- spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+}
+static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ bgpio_dir_out(gc, gpio, val);
+ gc->set(gc, gpio, val);
+ return 0;
+}
+
+static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ gc->set(gc, gpio, val);
+ bgpio_dir_out(gc, gpio, val);
return 0;
}
@@ -530,7 +544,10 @@ static int bgpio_setup_direction(struct gpio_chip *gc,
if (dirout || dirin) {
gc->reg_dir_out = dirout;
gc->reg_dir_in = dirin;
- gc->direction_output = bgpio_dir_out;
+ if (flags & BGPIOF_NO_SET_ON_INPUT)
+ gc->direction_output = bgpio_dir_out_dir_first;
+ else
+ gc->direction_output = bgpio_dir_out_val_first;
gc->direction_input = bgpio_dir_in;
gc->get_direction = bgpio_get_dir;
} else {
@@ -593,7 +610,7 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
if (gc->bgpio_bits > BITS_PER_LONG)
return -EINVAL;
- spin_lock_init(&gc->bgpio_lock);
+ raw_spin_lock_init(&gc->bgpio_lock);
gc->parent = dev;
gc->label = dev_name(dev);
gc->base = -1;
diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c
index f1a9c0544e3f..8943cea92764 100644
--- a/drivers/gpio/gpio-mockup.c
+++ b/drivers/gpio/gpio-mockup.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* GPIO Testing Device Driver
*
@@ -7,41 +7,36 @@
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
*/
-#include <linux/init.h>
-#include <linux/module.h>
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/debugfs.h>
#include <linux/gpio/driver.h>
-#include <linux/gpio/consumer.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irq_sim.h>
-#include <linux/debugfs.h>
-#include <linux/uaccess.h>
+#include <linux/irqdomain.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+#include <linux/uaccess.h>
#include "gpiolib.h"
-#define GPIO_MOCKUP_NAME "gpio-mockup"
#define GPIO_MOCKUP_MAX_GC 10
/*
* We're storing two values per chip: the GPIO base and the number
* of GPIO lines.
*/
#define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2)
-/* Maximum of three properties + the sentinel. */
-#define GPIO_MOCKUP_MAX_PROP 4
-
-#define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__)
-
-enum {
- GPIO_MOCKUP_DIR_IN = 0,
- GPIO_MOCKUP_DIR_OUT = 1,
-};
+/* Maximum of four properties + the sentinel. */
+#define GPIO_MOCKUP_MAX_PROP 5
/*
* struct gpio_pin_status - structure describing a GPIO status
- * @dir: Configures direction of gpio as "in" or "out", 0=in, 1=out
+ * @dir: Configures direction of gpio as "in" or "out"
* @value: Configures status of the gpio as 0(low) or 1(high)
*/
struct gpio_mockup_line_status {
@@ -53,7 +48,7 @@ struct gpio_mockup_line_status {
struct gpio_mockup_chip {
struct gpio_chip gc;
struct gpio_mockup_line_status *lines;
- struct irq_sim irqsim;
+ struct irq_domain *irq_sim_domain;
struct dentry *dbg_dir;
struct mutex lock;
};
@@ -146,13 +141,77 @@ static void gpio_mockup_set_multiple(struct gpio_chip *gc,
mutex_unlock(&chip->lock);
}
+static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,
+ unsigned int offset, int value)
+{
+ struct gpio_chip *gc = &chip->gc;
+ struct gpio_desc *desc = gpiochip_get_desc(gc, offset);
+ int curr, irq, irq_type, ret = 0;
+
+ mutex_lock(&chip->lock);
+
+ if (test_bit(FLAG_REQUESTED, &desc->flags) &&
+ !test_bit(FLAG_IS_OUT, &desc->flags)) {
+ curr = __gpio_mockup_get(chip, offset);
+ if (curr == value)
+ goto out;
+
+ irq = irq_find_mapping(chip->irq_sim_domain, offset);
+ if (!irq)
+ /*
+ * This is fine - it just means, nobody is listening
+ * for interrupts on this line, otherwise
+ * irq_create_mapping() would have been called from
+ * the to_irq() callback.
+ */
+ goto set_value;
+
+ irq_type = irq_get_trigger_type(irq);
+
+ if ((value == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
+ (value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) {
+ ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING,
+ true);
+ if (ret)
+ goto out;
+ }
+ }
+
+set_value:
+ /* Change the value unless we're actively driving the line. */
+ if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+ !test_bit(FLAG_IS_OUT, &desc->flags))
+ __gpio_mockup_set(chip, offset, value);
+
+out:
+ chip->lines[offset].pull = value;
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static int gpio_mockup_set_config(struct gpio_chip *gc,
+ unsigned int offset, unsigned long config)
+{
+ struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
+
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_BIAS_PULL_UP:
+ return gpio_mockup_apply_pull(chip, offset, 1);
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ return gpio_mockup_apply_pull(chip, offset, 0);
+ default:
+ break;
+ }
+ return -ENOTSUPP;
+}
+
static int gpio_mockup_dirout(struct gpio_chip *gc,
unsigned int offset, int value)
{
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock);
- chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT;
+ chip->lines[offset].dir = GPIO_LINE_DIRECTION_OUT;
__gpio_mockup_set(chip, offset, value);
mutex_unlock(&chip->lock);
@@ -164,7 +223,7 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock);
- chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN;
+ chip->lines[offset].dir = GPIO_LINE_DIRECTION_IN;
mutex_unlock(&chip->lock);
return 0;
@@ -176,7 +235,7 @@ static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)
int direction;
mutex_lock(&chip->lock);
- direction = !chip->lines[offset].dir;
+ direction = chip->lines[offset].dir;
mutex_unlock(&chip->lock);
return direction;
@@ -186,7 +245,7 @@ static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
- return irq_sim_irqnum(&chip->irqsim, offset);
+ return irq_create_mapping(chip->irq_sim_domain, offset);
}
static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)
@@ -226,12 +285,8 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
size_t size, loff_t *ppos)
{
struct gpio_mockup_dbgfs_private *priv;
- int rv, val, curr, irq, irq_type;
- struct gpio_mockup_chip *chip;
+ int rv, val;
struct seq_file *sfile;
- struct gpio_desc *desc;
- struct gpio_chip *gc;
- struct irq_sim *sim;
if (*ppos != 0)
return -EINVAL;
@@ -244,35 +299,9 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
sfile = file->private_data;
priv = sfile->private;
- chip = priv->chip;
- gc = &chip->gc;
- desc = &gc->gpiodev->descs[priv->offset];
- sim = &chip->irqsim;
-
- mutex_lock(&chip->lock);
-
- if (test_bit(FLAG_REQUESTED, &desc->flags) &&
- !test_bit(FLAG_IS_OUT, &desc->flags)) {
- curr = __gpio_mockup_get(chip, priv->offset);
- if (curr == val)
- goto out;
-
- irq = irq_sim_irqnum(sim, priv->offset);
- irq_type = irq_get_trigger_type(irq);
-
- if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
- (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))
- irq_sim_fire(sim, priv->offset);
- }
-
- /* Change the value unless we're actively driving the line. */
- if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
- !test_bit(FLAG_IS_OUT, &desc->flags))
- __gpio_mockup_set(chip, priv->offset, val);
-
-out:
- chip->lines[priv->offset].pull = val;
- mutex_unlock(&chip->lock);
+ rv = gpio_mockup_apply_pull(priv->chip, priv->offset, val);
+ if (rv)
+ return rv;
return size;
}
@@ -309,6 +338,7 @@ static const struct file_operations gpio_mockup_debugfs_ops = {
.read = gpio_mockup_debugfs_read,
.write = gpio_mockup_debugfs_write,
.llseek = no_llseek,
+ .release = single_release,
};
static void gpio_mockup_debugfs_setup(struct device *dev,
@@ -336,36 +366,24 @@ static void gpio_mockup_debugfs_setup(struct device *dev,
priv->chip = chip;
priv->offset = i;
- priv->desc = &gc->gpiodev->descs[i];
+ priv->desc = gpiochip_get_desc(gc, i);
debugfs_create_file(name, 0200, chip->dbg_dir, priv,
&gpio_mockup_debugfs_ops);
}
-
- return;
}
-static int gpio_mockup_name_lines(struct device *dev,
- struct gpio_mockup_chip *chip)
+static void gpio_mockup_dispose_mappings(void *data)
{
+ struct gpio_mockup_chip *chip = data;
struct gpio_chip *gc = &chip->gc;
- char **names;
- int i;
-
- names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL);
- if (!names)
- return -ENOMEM;
+ int i, irq;
for (i = 0; i < gc->ngpio; i++) {
- names[i] = devm_kasprintf(dev, GFP_KERNEL,
- "%s-%d", gc->label, i);
- if (!names[i])
- return -ENOMEM;
+ irq = irq_find_mapping(chip->irq_sim_domain, i);
+ if (irq)
+ irq_dispose_mapping(irq);
}
-
- gc->names = (const char *const *)names;
-
- return 0;
}
static int gpio_mockup_probe(struct platform_device *pdev)
@@ -374,7 +392,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)
struct gpio_chip *gc;
struct device *dev;
const char *name;
- int rv, base;
+ int rv, base, i;
u16 ngpio;
dev = &pdev->dev;
@@ -387,21 +405,14 @@ static int gpio_mockup_probe(struct platform_device *pdev)
if (rv)
return rv;
- rv = device_property_read_string(dev, "chip-name", &name);
+ rv = device_property_read_string(dev, "chip-label", &name);
if (rv)
- name = NULL;
+ name = dev_name(dev);
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
- if (!name) {
- name = devm_kasprintf(dev, GFP_KERNEL,
- "%s-%c", pdev->name, pdev->id + 'A');
- if (!name)
- return -ENOMEM;
- }
-
mutex_init(&chip->lock);
gc = &chip->gc;
@@ -417,6 +428,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)
gc->direction_output = gpio_mockup_dirout;
gc->direction_input = gpio_mockup_dirin;
gc->get_direction = gpio_mockup_get_direction;
+ gc->set_config = gpio_mockup_set_config;
gc->to_irq = gpio_mockup_to_irq;
gc->free = gpio_mockup_free;
@@ -425,14 +437,16 @@ static int gpio_mockup_probe(struct platform_device *pdev)
if (!chip->lines)
return -ENOMEM;
- if (device_property_read_bool(dev, "named-gpio-lines")) {
- rv = gpio_mockup_name_lines(dev, chip);
- if (rv)
- return rv;
- }
+ for (i = 0; i < gc->ngpio; i++)
+ chip->lines[i].dir = GPIO_LINE_DIRECTION_IN;
+
+ chip->irq_sim_domain = devm_irq_domain_create_sim(dev, NULL,
+ gc->ngpio);
+ if (IS_ERR(chip->irq_sim_domain))
+ return PTR_ERR(chip->irq_sim_domain);
- rv = devm_irq_sim_init(dev, &chip->irqsim, gc->ngpio);
- if (rv < 0)
+ rv = devm_add_action_or_reset(dev, gpio_mockup_dispose_mappings, chip);
+ if (rv)
return rv;
rv = devm_gpiochip_add_data(dev, &chip->gc, chip);
@@ -444,9 +458,16 @@ static int gpio_mockup_probe(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id gpio_mockup_of_match[] = {
+ { .compatible = "gpio-mockup", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, gpio_mockup_of_match);
+
static struct platform_driver gpio_mockup_driver = {
.driver = {
- .name = GPIO_MOCKUP_NAME,
+ .name = "gpio-mockup",
+ .of_match_table = gpio_mockup_of_match,
},
.probe = gpio_mockup_probe,
};
@@ -456,26 +477,80 @@ static struct platform_device *gpio_mockup_pdevs[GPIO_MOCKUP_MAX_GC];
static void gpio_mockup_unregister_pdevs(void)
{
struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
int i;
for (i = 0; i < GPIO_MOCKUP_MAX_GC; i++) {
pdev = gpio_mockup_pdevs[i];
+ if (!pdev)
+ continue;
- if (pdev)
- platform_device_unregister(pdev);
+ fwnode = dev_fwnode(&pdev->dev);
+ platform_device_unregister(pdev);
+ fwnode_remove_software_node(fwnode);
}
}
-static int __init gpio_mockup_init(void)
+static int __init gpio_mockup_register_chip(int idx)
{
struct property_entry properties[GPIO_MOCKUP_MAX_PROP];
- int i, prop, num_chips, err = 0, base;
struct platform_device_info pdevinfo;
struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
+ char **line_names = NULL;
+ char chip_label[32];
+ int prop = 0, base;
u16 ngpio;
- if ((gpio_mockup_num_ranges < 2) ||
- (gpio_mockup_num_ranges % 2) ||
+ memset(properties, 0, sizeof(properties));
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+
+ snprintf(chip_label, sizeof(chip_label), "gpio-mockup-%c", idx + 'A');
+ properties[prop++] = PROPERTY_ENTRY_STRING("chip-label", chip_label);
+
+ base = gpio_mockup_range_base(idx);
+ if (base >= 0)
+ properties[prop++] = PROPERTY_ENTRY_U32("gpio-base", base);
+
+ ngpio = base < 0 ? gpio_mockup_range_ngpio(idx)
+ : gpio_mockup_range_ngpio(idx) - base;
+ properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);
+
+ if (gpio_mockup_named_lines) {
+ line_names = kasprintf_strarray(GFP_KERNEL, chip_label, ngpio);
+ if (!line_names)
+ return -ENOMEM;
+
+ properties[prop++] = PROPERTY_ENTRY_STRING_ARRAY_LEN(
+ "gpio-line-names", line_names, ngpio);
+ }
+
+ fwnode = fwnode_create_software_node(properties, NULL);
+ if (IS_ERR(fwnode))
+ return PTR_ERR(fwnode);
+
+ pdevinfo.name = "gpio-mockup";
+ pdevinfo.id = idx;
+ pdevinfo.fwnode = fwnode;
+
+ pdev = platform_device_register_full(&pdevinfo);
+ kfree_strarray(line_names, ngpio);
+ if (IS_ERR(pdev)) {
+ fwnode_remove_software_node(fwnode);
+ pr_err("error registering device");
+ return PTR_ERR(pdev);
+ }
+
+ gpio_mockup_pdevs[idx] = pdev;
+
+ return 0;
+}
+
+static int __init gpio_mockup_init(void)
+{
+ int i, num_chips, err;
+
+ if ((gpio_mockup_num_ranges % 2) ||
(gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES))
return -EINVAL;
@@ -495,41 +570,19 @@ static int __init gpio_mockup_init(void)
err = platform_driver_register(&gpio_mockup_driver);
if (err) {
- gpio_mockup_err("error registering platform driver\n");
+ pr_err("error registering platform driver\n");
+ debugfs_remove_recursive(gpio_mockup_dbg_dir);
return err;
}
for (i = 0; i < num_chips; i++) {
- memset(properties, 0, sizeof(properties));
- memset(&pdevinfo, 0, sizeof(pdevinfo));
- prop = 0;
-
- base = gpio_mockup_range_base(i);
- if (base >= 0)
- properties[prop++] = PROPERTY_ENTRY_U32("gpio-base",
- base);
-
- ngpio = base < 0 ? gpio_mockup_range_ngpio(i)
- : gpio_mockup_range_ngpio(i) - base;
- properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);
-
- if (gpio_mockup_named_lines)
- properties[prop++] = PROPERTY_ENTRY_BOOL(
- "named-gpio-lines");
-
- pdevinfo.name = GPIO_MOCKUP_NAME;
- pdevinfo.id = i;
- pdevinfo.properties = properties;
-
- pdev = platform_device_register_full(&pdevinfo);
- if (IS_ERR(pdev)) {
- gpio_mockup_err("error registering device");
+ err = gpio_mockup_register_chip(i);
+ if (err) {
platform_driver_unregister(&gpio_mockup_driver);
gpio_mockup_unregister_pdevs();
- return PTR_ERR(pdev);
+ debugfs_remove_recursive(gpio_mockup_dbg_dir);
+ return err;
}
-
- gpio_mockup_pdevs[i] = pdev;
}
return 0;
diff --git a/drivers/gpio/gpio-moxtet.c b/drivers/gpio/gpio-moxtet.c
new file mode 100644
index 000000000000..61f9efd6c64f
--- /dev/null
+++ b/drivers/gpio/gpio-moxtet.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Turris Mox Moxtet GPIO expander
+ *
+ * Copyright (C) 2018 Marek Behún <kabel@kernel.org>
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/moxtet.h>
+#include <linux/module.h>
+
+#define MOXTET_GPIO_NGPIOS 12
+#define MOXTET_GPIO_INPUTS 4
+
+struct moxtet_gpio_desc {
+ u16 in_mask;
+ u16 out_mask;
+};
+
+static const struct moxtet_gpio_desc descs[] = {
+ [TURRIS_MOX_MODULE_SFP] = {
+ .in_mask = GENMASK(2, 0),
+ .out_mask = GENMASK(5, 4),
+ },
+};
+
+struct moxtet_gpio_chip {
+ struct device *dev;
+ struct gpio_chip gpio_chip;
+ const struct moxtet_gpio_desc *desc;
+};
+
+static int moxtet_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
+{
+ struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
+ int ret;
+
+ if (chip->desc->in_mask & BIT(offset)) {
+ ret = moxtet_device_read(chip->dev);
+ } else if (chip->desc->out_mask & BIT(offset)) {
+ ret = moxtet_device_written(chip->dev);
+ if (ret >= 0)
+ ret <<= MOXTET_GPIO_INPUTS;
+ } else {
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & BIT(offset));
+}
+
+static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
+ int val)
+{
+ struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
+ int state;
+
+ state = moxtet_device_written(chip->dev);
+ if (state < 0)
+ return;
+
+ offset -= MOXTET_GPIO_INPUTS;
+
+ if (val)
+ state |= BIT(offset);
+ else
+ state &= ~BIT(offset);
+
+ moxtet_device_write(chip->dev, state);
+}
+
+static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
+
+ /* All lines are hard wired to be either input or output, not both. */
+ if (chip->desc->in_mask & BIT(offset))
+ return GPIO_LINE_DIRECTION_IN;
+ else if (chip->desc->out_mask & BIT(offset))
+ return GPIO_LINE_DIRECTION_OUT;
+ else
+ return -EINVAL;
+}
+
+static int moxtet_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
+
+ if (chip->desc->in_mask & BIT(offset))
+ return 0;
+ else if (chip->desc->out_mask & BIT(offset))
+ return -ENOTSUPP;
+ else
+ return -EINVAL;
+}
+
+static int moxtet_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int val)
+{
+ struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
+
+ if (chip->desc->out_mask & BIT(offset))
+ moxtet_gpio_set_value(gc, offset, val);
+ else if (chip->desc->in_mask & BIT(offset))
+ return -ENOTSUPP;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int moxtet_gpio_probe(struct device *dev)
+{
+ struct moxtet_gpio_chip *chip;
+ struct device_node *nc = dev->of_node;
+ int id;
+
+ id = to_moxtet_device(dev)->id;
+
+ if (id >= ARRAY_SIZE(descs)) {
+ dev_err(dev, "%pOF Moxtet device id 0x%x is not supported by gpio-moxtet driver\n",
+ nc, id);
+ return -ENOTSUPP;
+ }
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = dev;
+ chip->gpio_chip.parent = dev;
+ chip->desc = &descs[id];
+
+ dev_set_drvdata(dev, chip);
+
+ chip->gpio_chip.label = dev_name(dev);
+ chip->gpio_chip.get_direction = moxtet_gpio_get_direction;
+ chip->gpio_chip.direction_input = moxtet_gpio_direction_input;
+ chip->gpio_chip.direction_output = moxtet_gpio_direction_output;
+ chip->gpio_chip.get = moxtet_gpio_get_value;
+ chip->gpio_chip.set = moxtet_gpio_set_value;
+ chip->gpio_chip.base = -1;
+
+ chip->gpio_chip.ngpio = MOXTET_GPIO_NGPIOS;
+
+ chip->gpio_chip.can_sleep = true;
+ chip->gpio_chip.owner = THIS_MODULE;
+
+ return devm_gpiochip_add_data(dev, &chip->gpio_chip, chip);
+}
+
+static const struct of_device_id moxtet_gpio_dt_ids[] = {
+ { .compatible = "cznic,moxtet-gpio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, moxtet_gpio_dt_ids);
+
+static const enum turris_mox_module_id moxtet_gpio_module_table[] = {
+ TURRIS_MOX_MODULE_SFP,
+ 0,
+};
+
+static struct moxtet_driver moxtet_gpio_driver = {
+ .driver = {
+ .name = "moxtet-gpio",
+ .of_match_table = moxtet_gpio_dt_ids,
+ .probe = moxtet_gpio_probe,
+ },
+ .id_table = moxtet_gpio_module_table,
+};
+module_moxtet_driver(moxtet_gpio_driver);
+
+MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
+MODULE_DESCRIPTION("Turris Mox Moxtet GPIO expander");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index c8673a5d9412..a964e25ea620 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -9,6 +9,7 @@
* kind, whether express or implied.
*/
+#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>
@@ -18,10 +19,13 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/property.h>
+#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/gpio/driver.h>
#include <linux/bitops.h>
+#include <linux/interrupt.h>
#define MPC8XXX_GPIO_PINS 32
@@ -32,6 +36,7 @@
#define GPIO_IMR 0x10
#define GPIO_ICR 0x14
#define GPIO_ICR2 0x18
+#define GPIO_IBE 0x18
struct mpc8xxx_gpio_chip {
struct gpio_chip gc;
@@ -42,7 +47,7 @@ struct mpc8xxx_gpio_chip {
unsigned offset, int value);
struct irq_domain *irq;
- unsigned int irqn;
+ int irqn;
};
/*
@@ -105,20 +110,19 @@ static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
return -ENXIO;
}
-static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc)
+static irqreturn_t mpc8xxx_gpio_irq_cascade(int irq, void *data)
{
- struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc);
- struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = data;
struct gpio_chip *gc = &mpc8xxx_gc->gc;
- unsigned int mask;
+ unsigned long mask;
+ int i;
mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_IER)
& gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR);
- if (mask)
- generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq,
- 32 - ffs(mask)));
- if (chip->irq_eoi)
- chip->irq_eoi(&desc->irq_data);
+ for_each_set_bit(i, &mask, 32)
+ generic_handle_domain_irq(mpc8xxx_gc->irq, 31 - i);
+
+ return IRQ_HANDLED;
}
static void mpc8xxx_irq_unmask(struct irq_data *d)
@@ -291,6 +295,8 @@ static const struct of_device_id mpc8xxx_gpio_ids[] = {
{ .compatible = "fsl,mpc5121-gpio", .data = &mpc512x_gpio_devtype, },
{ .compatible = "fsl,mpc5125-gpio", .data = &mpc5125_gpio_devtype, },
{ .compatible = "fsl,pq3-gpio", },
+ { .compatible = "fsl,ls1028a-gpio", },
+ { .compatible = "fsl,ls1088a-gpio", },
{ .compatible = "fsl,qoriq-gpio", },
{}
};
@@ -300,8 +306,8 @@ static int mpc8xxx_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct mpc8xxx_gpio_chip *mpc8xxx_gc;
struct gpio_chip *gc;
- const struct mpc8xxx_gpio_devtype *devtype =
- of_device_get_match_data(&pdev->dev);
+ const struct mpc8xxx_gpio_devtype *devtype = NULL;
+ struct fwnode_handle *fwnode;
int ret;
mpc8xxx_gc = devm_kzalloc(&pdev->dev, sizeof(*mpc8xxx_gc), GFP_KERNEL);
@@ -312,20 +318,21 @@ static int mpc8xxx_probe(struct platform_device *pdev)
raw_spin_lock_init(&mpc8xxx_gc->lock);
- mpc8xxx_gc->regs = of_iomap(np, 0);
- if (!mpc8xxx_gc->regs)
- return -ENOMEM;
+ mpc8xxx_gc->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mpc8xxx_gc->regs))
+ return PTR_ERR(mpc8xxx_gc->regs);
gc = &mpc8xxx_gc->gc;
+ gc->parent = &pdev->dev;
- if (of_property_read_bool(np, "little-endian")) {
+ if (device_property_read_bool(&pdev->dev, "little-endian")) {
ret = bgpio_init(gc, &pdev->dev, 4,
mpc8xxx_gc->regs + GPIO_DAT,
NULL, NULL,
mpc8xxx_gc->regs + GPIO_DIR, NULL,
BGPIOF_BIG_ENDIAN);
if (ret)
- goto err;
+ return ret;
dev_dbg(&pdev->dev, "GPIO registers are LITTLE endian\n");
} else {
ret = bgpio_init(gc, &pdev->dev, 4,
@@ -335,12 +342,13 @@ static int mpc8xxx_probe(struct platform_device *pdev)
BGPIOF_BIG_ENDIAN
| BGPIOF_BIG_ENDIAN_BYTE_ORDER);
if (ret)
- goto err;
+ return ret;
dev_dbg(&pdev->dev, "GPIO registers are BIG endian\n");
}
mpc8xxx_gc->direction_output = gc->direction_output;
+ devtype = device_get_match_data(&pdev->dev);
if (!devtype)
devtype = &mpc8xxx_gpio_devtype_default;
@@ -348,7 +356,8 @@ static int mpc8xxx_probe(struct platform_device *pdev)
* It's assumed that only a single type of gpio controller is available
* on the current machine, so overwriting global data is fine.
*/
- mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type;
+ if (devtype->irq_set_type)
+ mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type;
if (devtype->gpio_dir_out)
gc->direction_output = devtype->gpio_dir_out;
@@ -357,19 +366,36 @@ static int mpc8xxx_probe(struct platform_device *pdev)
gc->to_irq = mpc8xxx_gpio_to_irq;
- ret = gpiochip_add_data(gc, mpc8xxx_gc);
+ /*
+ * The GPIO Input Buffer Enable register(GPIO_IBE) is used to control
+ * the input enable of each individual GPIO port. When an individual
+ * GPIO port’s direction is set to input (GPIO_GPDIR[DRn=0]), the
+ * associated input enable must be set (GPIOxGPIE[IEn]=1) to propagate
+ * the port value to the GPIO Data Register.
+ */
+ fwnode = dev_fwnode(&pdev->dev);
+ if (of_device_is_compatible(np, "fsl,qoriq-gpio") ||
+ of_device_is_compatible(np, "fsl,ls1028a-gpio") ||
+ of_device_is_compatible(np, "fsl,ls1088a-gpio") ||
+ is_acpi_node(fwnode))
+ gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff);
+
+ ret = devm_gpiochip_add_data(&pdev->dev, gc, mpc8xxx_gc);
if (ret) {
- pr_err("%pOF: GPIO chip registration failed with status %d\n",
- np, ret);
- goto err;
+ dev_err(&pdev->dev,
+ "GPIO chip registration failed with status %d\n", ret);
+ return ret;
}
- mpc8xxx_gc->irqn = irq_of_parse_and_map(np, 0);
- if (!mpc8xxx_gc->irqn)
- return 0;
+ mpc8xxx_gc->irqn = platform_get_irq(pdev, 0);
+ if (mpc8xxx_gc->irqn < 0)
+ return mpc8xxx_gc->irqn;
+
+ mpc8xxx_gc->irq = irq_domain_create_linear(fwnode,
+ MPC8XXX_GPIO_PINS,
+ &mpc8xxx_gpio_irq_ops,
+ mpc8xxx_gc);
- mpc8xxx_gc->irq = irq_domain_add_linear(np, MPC8XXX_GPIO_PINS,
- &mpc8xxx_gpio_irq_ops, mpc8xxx_gc);
if (!mpc8xxx_gc->irq)
return 0;
@@ -377,11 +403,20 @@ static int mpc8xxx_probe(struct platform_device *pdev)
gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff);
gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0);
- irq_set_chained_handler_and_data(mpc8xxx_gc->irqn,
- mpc8xxx_gpio_irq_cascade, mpc8xxx_gc);
+ ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn,
+ mpc8xxx_gpio_irq_cascade,
+ IRQF_NO_THREAD | IRQF_SHARED, "gpio-cascade",
+ mpc8xxx_gc);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to devm_request_irq(%d), ret = %d\n",
+ mpc8xxx_gc->irqn, ret);
+ goto err;
+ }
+
return 0;
err:
- iounmap(mpc8xxx_gc->regs);
+ irq_domain_remove(mpc8xxx_gc->irq);
return ret;
}
@@ -394,18 +429,24 @@ static int mpc8xxx_remove(struct platform_device *pdev)
irq_domain_remove(mpc8xxx_gc->irq);
}
- gpiochip_remove(&mpc8xxx_gc->gc);
- iounmap(mpc8xxx_gc->regs);
-
return 0;
}
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id gpio_acpi_ids[] = {
+ {"NXP0031",},
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, gpio_acpi_ids);
+#endif
+
static struct platform_driver mpc8xxx_plat_driver = {
.probe = mpc8xxx_probe,
.remove = mpc8xxx_remove,
.driver = {
.name = "gpio-mpc8xxx",
.of_match_table = mpc8xxx_gpio_ids,
+ .acpi_match_table = ACPI_PTR(gpio_acpi_ids),
},
};
diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c
new file mode 100644
index 000000000000..b2c90bdd39d0
--- /dev/null
+++ b/drivers/gpio/gpio-msc313.c
@@ -0,0 +1,718 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2020 Daniel Palmer<daniel@thingy.jp> */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/gpio/msc313-gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#define DRIVER_NAME "gpio-msc313"
+
+#define MSC313_GPIO_IN BIT(0)
+#define MSC313_GPIO_OUT BIT(4)
+#define MSC313_GPIO_OEN BIT(5)
+
+/*
+ * These bits need to be saved to correctly restore the
+ * gpio state when resuming from suspend to memory.
+ */
+#define MSC313_GPIO_BITSTOSAVE (MSC313_GPIO_OUT | MSC313_GPIO_OEN)
+
+/* pad names for fuart, same for all SoCs so far */
+#define MSC313_PINNAME_FUART_RX "fuart_rx"
+#define MSC313_PINNAME_FUART_TX "fuart_tx"
+#define MSC313_PINNAME_FUART_CTS "fuart_cts"
+#define MSC313_PINNAME_FUART_RTS "fuart_rts"
+
+/* pad names for sr, mercury5 is different */
+#define MSC313_PINNAME_SR_IO2 "sr_io2"
+#define MSC313_PINNAME_SR_IO3 "sr_io3"
+#define MSC313_PINNAME_SR_IO4 "sr_io4"
+#define MSC313_PINNAME_SR_IO5 "sr_io5"
+#define MSC313_PINNAME_SR_IO6 "sr_io6"
+#define MSC313_PINNAME_SR_IO7 "sr_io7"
+#define MSC313_PINNAME_SR_IO8 "sr_io8"
+#define MSC313_PINNAME_SR_IO9 "sr_io9"
+#define MSC313_PINNAME_SR_IO10 "sr_io10"
+#define MSC313_PINNAME_SR_IO11 "sr_io11"
+#define MSC313_PINNAME_SR_IO12 "sr_io12"
+#define MSC313_PINNAME_SR_IO13 "sr_io13"
+#define MSC313_PINNAME_SR_IO14 "sr_io14"
+#define MSC313_PINNAME_SR_IO15 "sr_io15"
+#define MSC313_PINNAME_SR_IO16 "sr_io16"
+#define MSC313_PINNAME_SR_IO17 "sr_io17"
+
+/* pad names for sd, same for all SoCs so far */
+#define MSC313_PINNAME_SD_CLK "sd_clk"
+#define MSC313_PINNAME_SD_CMD "sd_cmd"
+#define MSC313_PINNAME_SD_D0 "sd_d0"
+#define MSC313_PINNAME_SD_D1 "sd_d1"
+#define MSC313_PINNAME_SD_D2 "sd_d2"
+#define MSC313_PINNAME_SD_D3 "sd_d3"
+
+/* pad names for i2c1, same for all SoCs so for */
+#define MSC313_PINNAME_I2C1_SCL "i2c1_scl"
+#define MSC313_PINNAME_I2C1_SCA "i2c1_sda"
+
+/* pad names for spi0, same for all SoCs so far */
+#define MSC313_PINNAME_SPI0_CZ "spi0_cz"
+#define MSC313_PINNAME_SPI0_CK "spi0_ck"
+#define MSC313_PINNAME_SPI0_DI "spi0_di"
+#define MSC313_PINNAME_SPI0_DO "spi0_do"
+
+#define FUART_NAMES \
+ MSC313_PINNAME_FUART_RX, \
+ MSC313_PINNAME_FUART_TX, \
+ MSC313_PINNAME_FUART_CTS, \
+ MSC313_PINNAME_FUART_RTS
+
+#define OFF_FUART_RX 0x50
+#define OFF_FUART_TX 0x54
+#define OFF_FUART_CTS 0x58
+#define OFF_FUART_RTS 0x5c
+
+#define FUART_OFFSETS \
+ OFF_FUART_RX, \
+ OFF_FUART_TX, \
+ OFF_FUART_CTS, \
+ OFF_FUART_RTS
+
+#define SR_NAMES \
+ MSC313_PINNAME_SR_IO2, \
+ MSC313_PINNAME_SR_IO3, \
+ MSC313_PINNAME_SR_IO4, \
+ MSC313_PINNAME_SR_IO5, \
+ MSC313_PINNAME_SR_IO6, \
+ MSC313_PINNAME_SR_IO7, \
+ MSC313_PINNAME_SR_IO8, \
+ MSC313_PINNAME_SR_IO9, \
+ MSC313_PINNAME_SR_IO10, \
+ MSC313_PINNAME_SR_IO11, \
+ MSC313_PINNAME_SR_IO12, \
+ MSC313_PINNAME_SR_IO13, \
+ MSC313_PINNAME_SR_IO14, \
+ MSC313_PINNAME_SR_IO15, \
+ MSC313_PINNAME_SR_IO16, \
+ MSC313_PINNAME_SR_IO17
+
+#define OFF_SR_IO2 0x88
+#define OFF_SR_IO3 0x8c
+#define OFF_SR_IO4 0x90
+#define OFF_SR_IO5 0x94
+#define OFF_SR_IO6 0x98
+#define OFF_SR_IO7 0x9c
+#define OFF_SR_IO8 0xa0
+#define OFF_SR_IO9 0xa4
+#define OFF_SR_IO10 0xa8
+#define OFF_SR_IO11 0xac
+#define OFF_SR_IO12 0xb0
+#define OFF_SR_IO13 0xb4
+#define OFF_SR_IO14 0xb8
+#define OFF_SR_IO15 0xbc
+#define OFF_SR_IO16 0xc0
+#define OFF_SR_IO17 0xc4
+
+#define SR_OFFSETS \
+ OFF_SR_IO2, \
+ OFF_SR_IO3, \
+ OFF_SR_IO4, \
+ OFF_SR_IO5, \
+ OFF_SR_IO6, \
+ OFF_SR_IO7, \
+ OFF_SR_IO8, \
+ OFF_SR_IO9, \
+ OFF_SR_IO10, \
+ OFF_SR_IO11, \
+ OFF_SR_IO12, \
+ OFF_SR_IO13, \
+ OFF_SR_IO14, \
+ OFF_SR_IO15, \
+ OFF_SR_IO16, \
+ OFF_SR_IO17
+
+#define SD_NAMES \
+ MSC313_PINNAME_SD_CLK, \
+ MSC313_PINNAME_SD_CMD, \
+ MSC313_PINNAME_SD_D0, \
+ MSC313_PINNAME_SD_D1, \
+ MSC313_PINNAME_SD_D2, \
+ MSC313_PINNAME_SD_D3
+
+#define OFF_SD_CLK 0x140
+#define OFF_SD_CMD 0x144
+#define OFF_SD_D0 0x148
+#define OFF_SD_D1 0x14c
+#define OFF_SD_D2 0x150
+#define OFF_SD_D3 0x154
+
+#define SD_OFFSETS \
+ OFF_SD_CLK, \
+ OFF_SD_CMD, \
+ OFF_SD_D0, \
+ OFF_SD_D1, \
+ OFF_SD_D2, \
+ OFF_SD_D3
+
+#define I2C1_NAMES \
+ MSC313_PINNAME_I2C1_SCL, \
+ MSC313_PINNAME_I2C1_SCA
+
+#define OFF_I2C1_SCL 0x188
+#define OFF_I2C1_SCA 0x18c
+
+#define I2C1_OFFSETS \
+ OFF_I2C1_SCL, \
+ OFF_I2C1_SCA
+
+#define SPI0_NAMES \
+ MSC313_PINNAME_SPI0_CZ, \
+ MSC313_PINNAME_SPI0_CK, \
+ MSC313_PINNAME_SPI0_DI, \
+ MSC313_PINNAME_SPI0_DO
+
+#define OFF_SPI0_CZ 0x1c0
+#define OFF_SPI0_CK 0x1c4
+#define OFF_SPI0_DI 0x1c8
+#define OFF_SPI0_DO 0x1cc
+
+#define SPI0_OFFSETS \
+ OFF_SPI0_CZ, \
+ OFF_SPI0_CK, \
+ OFF_SPI0_DI, \
+ OFF_SPI0_DO
+
+struct msc313_gpio_data {
+ const char * const *names;
+ const unsigned int *offsets;
+ const unsigned int num;
+};
+
+#define MSC313_GPIO_CHIPDATA(_chip) \
+static const struct msc313_gpio_data _chip##_data = { \
+ .names = _chip##_names, \
+ .offsets = _chip##_offsets, \
+ .num = ARRAY_SIZE(_chip##_offsets), \
+}
+
+#ifdef CONFIG_MACH_INFINITY
+static const char * const msc313_names[] = {
+ FUART_NAMES,
+ SR_NAMES,
+ SD_NAMES,
+ I2C1_NAMES,
+ SPI0_NAMES,
+};
+
+static const unsigned int msc313_offsets[] = {
+ FUART_OFFSETS,
+ SR_OFFSETS,
+ SD_OFFSETS,
+ I2C1_OFFSETS,
+ SPI0_OFFSETS,
+};
+
+MSC313_GPIO_CHIPDATA(msc313);
+
+/*
+ * Unlike the msc313(e) the ssd20xd have a bunch of pins
+ * that are actually called gpio probably because they
+ * have no dedicated function.
+ */
+#define SSD20XD_PINNAME_GPIO0 "gpio0"
+#define SSD20XD_PINNAME_GPIO1 "gpio1"
+#define SSD20XD_PINNAME_GPIO2 "gpio2"
+#define SSD20XD_PINNAME_GPIO3 "gpio3"
+#define SSD20XD_PINNAME_GPIO4 "gpio4"
+#define SSD20XD_PINNAME_GPIO5 "gpio5"
+#define SSD20XD_PINNAME_GPIO6 "gpio6"
+#define SSD20XD_PINNAME_GPIO7 "gpio7"
+#define SSD20XD_PINNAME_GPIO10 "gpio10"
+#define SSD20XD_PINNAME_GPIO11 "gpio11"
+#define SSD20XD_PINNAME_GPIO12 "gpio12"
+#define SSD20XD_PINNAME_GPIO13 "gpio13"
+#define SSD20XD_PINNAME_GPIO14 "gpio14"
+#define SSD20XD_PINNAME_GPIO85 "gpio85"
+#define SSD20XD_PINNAME_GPIO86 "gpio86"
+#define SSD20XD_PINNAME_GPIO90 "gpio90"
+
+#define SSD20XD_GPIO_NAMES SSD20XD_PINNAME_GPIO0, \
+ SSD20XD_PINNAME_GPIO1, \
+ SSD20XD_PINNAME_GPIO2, \
+ SSD20XD_PINNAME_GPIO3, \
+ SSD20XD_PINNAME_GPIO4, \
+ SSD20XD_PINNAME_GPIO5, \
+ SSD20XD_PINNAME_GPIO6, \
+ SSD20XD_PINNAME_GPIO7, \
+ SSD20XD_PINNAME_GPIO10, \
+ SSD20XD_PINNAME_GPIO11, \
+ SSD20XD_PINNAME_GPIO12, \
+ SSD20XD_PINNAME_GPIO13, \
+ SSD20XD_PINNAME_GPIO14, \
+ SSD20XD_PINNAME_GPIO85, \
+ SSD20XD_PINNAME_GPIO86, \
+ SSD20XD_PINNAME_GPIO90
+
+#define SSD20XD_GPIO_OFF_GPIO0 0x0
+#define SSD20XD_GPIO_OFF_GPIO1 0x4
+#define SSD20XD_GPIO_OFF_GPIO2 0x8
+#define SSD20XD_GPIO_OFF_GPIO3 0xc
+#define SSD20XD_GPIO_OFF_GPIO4 0x10
+#define SSD20XD_GPIO_OFF_GPIO5 0x14
+#define SSD20XD_GPIO_OFF_GPIO6 0x18
+#define SSD20XD_GPIO_OFF_GPIO7 0x1c
+#define SSD20XD_GPIO_OFF_GPIO10 0x28
+#define SSD20XD_GPIO_OFF_GPIO11 0x2c
+#define SSD20XD_GPIO_OFF_GPIO12 0x30
+#define SSD20XD_GPIO_OFF_GPIO13 0x34
+#define SSD20XD_GPIO_OFF_GPIO14 0x38
+#define SSD20XD_GPIO_OFF_GPIO85 0x100
+#define SSD20XD_GPIO_OFF_GPIO86 0x104
+#define SSD20XD_GPIO_OFF_GPIO90 0x114
+
+#define SSD20XD_GPIO_OFFSETS SSD20XD_GPIO_OFF_GPIO0, \
+ SSD20XD_GPIO_OFF_GPIO1, \
+ SSD20XD_GPIO_OFF_GPIO2, \
+ SSD20XD_GPIO_OFF_GPIO3, \
+ SSD20XD_GPIO_OFF_GPIO4, \
+ SSD20XD_GPIO_OFF_GPIO5, \
+ SSD20XD_GPIO_OFF_GPIO6, \
+ SSD20XD_GPIO_OFF_GPIO7, \
+ SSD20XD_GPIO_OFF_GPIO10, \
+ SSD20XD_GPIO_OFF_GPIO11, \
+ SSD20XD_GPIO_OFF_GPIO12, \
+ SSD20XD_GPIO_OFF_GPIO13, \
+ SSD20XD_GPIO_OFF_GPIO14, \
+ SSD20XD_GPIO_OFF_GPIO85, \
+ SSD20XD_GPIO_OFF_GPIO86, \
+ SSD20XD_GPIO_OFF_GPIO90
+
+/* "ttl" pins lcd interface pins */
+#define SSD20XD_PINNAME_TTL0 "ttl0"
+#define SSD20XD_PINNAME_TTL1 "ttl1"
+#define SSD20XD_PINNAME_TTL2 "ttl2"
+#define SSD20XD_PINNAME_TTL3 "ttl3"
+#define SSD20XD_PINNAME_TTL4 "ttl4"
+#define SSD20XD_PINNAME_TTL5 "ttl5"
+#define SSD20XD_PINNAME_TTL6 "ttl6"
+#define SSD20XD_PINNAME_TTL7 "ttl7"
+#define SSD20XD_PINNAME_TTL8 "ttl8"
+#define SSD20XD_PINNAME_TTL9 "ttl9"
+#define SSD20XD_PINNAME_TTL10 "ttl10"
+#define SSD20XD_PINNAME_TTL11 "ttl11"
+#define SSD20XD_PINNAME_TTL12 "ttl12"
+#define SSD20XD_PINNAME_TTL13 "ttl13"
+#define SSD20XD_PINNAME_TTL14 "ttl14"
+#define SSD20XD_PINNAME_TTL15 "ttl15"
+#define SSD20XD_PINNAME_TTL16 "ttl16"
+#define SSD20XD_PINNAME_TTL17 "ttl17"
+#define SSD20XD_PINNAME_TTL18 "ttl18"
+#define SSD20XD_PINNAME_TTL19 "ttl19"
+#define SSD20XD_PINNAME_TTL20 "ttl20"
+#define SSD20XD_PINNAME_TTL21 "ttl21"
+#define SSD20XD_PINNAME_TTL22 "ttl22"
+#define SSD20XD_PINNAME_TTL23 "ttl23"
+#define SSD20XD_PINNAME_TTL24 "ttl24"
+#define SSD20XD_PINNAME_TTL25 "ttl25"
+#define SSD20XD_PINNAME_TTL26 "ttl26"
+#define SSD20XD_PINNAME_TTL27 "ttl27"
+
+#define SSD20XD_TTL_PINNAMES SSD20XD_PINNAME_TTL0, \
+ SSD20XD_PINNAME_TTL1, \
+ SSD20XD_PINNAME_TTL2, \
+ SSD20XD_PINNAME_TTL3, \
+ SSD20XD_PINNAME_TTL4, \
+ SSD20XD_PINNAME_TTL5, \
+ SSD20XD_PINNAME_TTL6, \
+ SSD20XD_PINNAME_TTL7, \
+ SSD20XD_PINNAME_TTL8, \
+ SSD20XD_PINNAME_TTL9, \
+ SSD20XD_PINNAME_TTL10, \
+ SSD20XD_PINNAME_TTL11, \
+ SSD20XD_PINNAME_TTL12, \
+ SSD20XD_PINNAME_TTL13, \
+ SSD20XD_PINNAME_TTL14, \
+ SSD20XD_PINNAME_TTL15, \
+ SSD20XD_PINNAME_TTL16, \
+ SSD20XD_PINNAME_TTL17, \
+ SSD20XD_PINNAME_TTL18, \
+ SSD20XD_PINNAME_TTL19, \
+ SSD20XD_PINNAME_TTL20, \
+ SSD20XD_PINNAME_TTL21, \
+ SSD20XD_PINNAME_TTL22, \
+ SSD20XD_PINNAME_TTL23, \
+ SSD20XD_PINNAME_TTL24, \
+ SSD20XD_PINNAME_TTL25, \
+ SSD20XD_PINNAME_TTL26, \
+ SSD20XD_PINNAME_TTL27
+
+#define SSD20XD_TTL_OFFSET_TTL0 0x80
+#define SSD20XD_TTL_OFFSET_TTL1 0x84
+#define SSD20XD_TTL_OFFSET_TTL2 0x88
+#define SSD20XD_TTL_OFFSET_TTL3 0x8c
+#define SSD20XD_TTL_OFFSET_TTL4 0x90
+#define SSD20XD_TTL_OFFSET_TTL5 0x94
+#define SSD20XD_TTL_OFFSET_TTL6 0x98
+#define SSD20XD_TTL_OFFSET_TTL7 0x9c
+#define SSD20XD_TTL_OFFSET_TTL8 0xa0
+#define SSD20XD_TTL_OFFSET_TTL9 0xa4
+#define SSD20XD_TTL_OFFSET_TTL10 0xa8
+#define SSD20XD_TTL_OFFSET_TTL11 0xac
+#define SSD20XD_TTL_OFFSET_TTL12 0xb0
+#define SSD20XD_TTL_OFFSET_TTL13 0xb4
+#define SSD20XD_TTL_OFFSET_TTL14 0xb8
+#define SSD20XD_TTL_OFFSET_TTL15 0xbc
+#define SSD20XD_TTL_OFFSET_TTL16 0xc0
+#define SSD20XD_TTL_OFFSET_TTL17 0xc4
+#define SSD20XD_TTL_OFFSET_TTL18 0xc8
+#define SSD20XD_TTL_OFFSET_TTL19 0xcc
+#define SSD20XD_TTL_OFFSET_TTL20 0xd0
+#define SSD20XD_TTL_OFFSET_TTL21 0xd4
+#define SSD20XD_TTL_OFFSET_TTL22 0xd8
+#define SSD20XD_TTL_OFFSET_TTL23 0xdc
+#define SSD20XD_TTL_OFFSET_TTL24 0xe0
+#define SSD20XD_TTL_OFFSET_TTL25 0xe4
+#define SSD20XD_TTL_OFFSET_TTL26 0xe8
+#define SSD20XD_TTL_OFFSET_TTL27 0xec
+
+#define SSD20XD_TTL_OFFSETS SSD20XD_TTL_OFFSET_TTL0, \
+ SSD20XD_TTL_OFFSET_TTL1, \
+ SSD20XD_TTL_OFFSET_TTL2, \
+ SSD20XD_TTL_OFFSET_TTL3, \
+ SSD20XD_TTL_OFFSET_TTL4, \
+ SSD20XD_TTL_OFFSET_TTL5, \
+ SSD20XD_TTL_OFFSET_TTL6, \
+ SSD20XD_TTL_OFFSET_TTL7, \
+ SSD20XD_TTL_OFFSET_TTL8, \
+ SSD20XD_TTL_OFFSET_TTL9, \
+ SSD20XD_TTL_OFFSET_TTL10, \
+ SSD20XD_TTL_OFFSET_TTL11, \
+ SSD20XD_TTL_OFFSET_TTL12, \
+ SSD20XD_TTL_OFFSET_TTL13, \
+ SSD20XD_TTL_OFFSET_TTL14, \
+ SSD20XD_TTL_OFFSET_TTL15, \
+ SSD20XD_TTL_OFFSET_TTL16, \
+ SSD20XD_TTL_OFFSET_TTL17, \
+ SSD20XD_TTL_OFFSET_TTL18, \
+ SSD20XD_TTL_OFFSET_TTL19, \
+ SSD20XD_TTL_OFFSET_TTL20, \
+ SSD20XD_TTL_OFFSET_TTL21, \
+ SSD20XD_TTL_OFFSET_TTL22, \
+ SSD20XD_TTL_OFFSET_TTL23, \
+ SSD20XD_TTL_OFFSET_TTL24, \
+ SSD20XD_TTL_OFFSET_TTL25, \
+ SSD20XD_TTL_OFFSET_TTL26, \
+ SSD20XD_TTL_OFFSET_TTL27
+
+/* On the ssd20xd the two normal uarts have dedicated pins */
+#define SSD20XD_PINNAME_UART0_RX "uart0_rx"
+#define SSD20XD_PINNAME_UART0_TX "uart0_tx"
+
+#define SSD20XD_UART0_NAMES \
+ SSD20XD_PINNAME_UART0_RX, \
+ SSD20XD_PINNAME_UART0_TX
+
+#define SSD20XD_PINNAME_UART1_RX "uart1_rx"
+#define SSD20XD_PINNAME_UART1_TX "uart1_tx"
+
+#define SSD20XD_UART1_NAMES \
+ SSD20XD_PINNAME_UART1_RX, \
+ SSD20XD_PINNAME_UART1_TX
+
+#define SSD20XD_OFF_UART0_RX 0x60
+#define SSD20XD_OFF_UART0_TX 0x64
+
+#define SSD20XD_UART0_OFFSETS \
+ SSD20XD_OFF_UART0_RX, \
+ SSD20XD_OFF_UART0_TX
+
+#define SSD20XD_OFF_UART1_RX 0x68
+#define SSD20XD_OFF_UART1_TX 0x6c
+
+#define SSD20XD_UART1_OFFSETS \
+ SSD20XD_OFF_UART1_RX, \
+ SSD20XD_OFF_UART1_TX
+
+/*
+ * ssd20x has the same pin names but different ordering
+ * of the registers that control the gpio.
+ */
+#define SSD20XD_OFF_SD_D0 0x140
+#define SSD20XD_OFF_SD_D1 0x144
+#define SSD20XD_OFF_SD_D2 0x148
+#define SSD20XD_OFF_SD_D3 0x14c
+#define SSD20XD_OFF_SD_CMD 0x150
+#define SSD20XD_OFF_SD_CLK 0x154
+
+#define SSD20XD_SD_OFFSETS SSD20XD_OFF_SD_CLK, \
+ SSD20XD_OFF_SD_CMD, \
+ SSD20XD_OFF_SD_D0, \
+ SSD20XD_OFF_SD_D1, \
+ SSD20XD_OFF_SD_D2, \
+ SSD20XD_OFF_SD_D3
+
+static const char * const ssd20xd_names[] = {
+ FUART_NAMES,
+ SD_NAMES,
+ SSD20XD_UART0_NAMES,
+ SSD20XD_UART1_NAMES,
+ SSD20XD_TTL_PINNAMES,
+ SSD20XD_GPIO_NAMES,
+};
+
+static const unsigned int ssd20xd_offsets[] = {
+ FUART_OFFSETS,
+ SSD20XD_SD_OFFSETS,
+ SSD20XD_UART0_OFFSETS,
+ SSD20XD_UART1_OFFSETS,
+ SSD20XD_TTL_OFFSETS,
+ SSD20XD_GPIO_OFFSETS,
+};
+
+MSC313_GPIO_CHIPDATA(ssd20xd);
+#endif
+
+struct msc313_gpio {
+ void __iomem *base;
+ const struct msc313_gpio_data *gpio_data;
+ u8 *saved;
+};
+
+static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct msc313_gpio *gpio = gpiochip_get_data(chip);
+ u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]);
+
+ if (value)
+ gpioreg |= MSC313_GPIO_OUT;
+ else
+ gpioreg &= ~MSC313_GPIO_OUT;
+
+ writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]);
+}
+
+static int msc313_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct msc313_gpio *gpio = gpiochip_get_data(chip);
+
+ return readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]) & MSC313_GPIO_IN;
+}
+
+static int msc313_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+ struct msc313_gpio *gpio = gpiochip_get_data(chip);
+ u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]);
+
+ gpioreg |= MSC313_GPIO_OEN;
+ writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]);
+
+ return 0;
+}
+
+static int msc313_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct msc313_gpio *gpio = gpiochip_get_data(chip);
+ u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]);
+
+ gpioreg &= ~MSC313_GPIO_OEN;
+ if (value)
+ gpioreg |= MSC313_GPIO_OUT;
+ else
+ gpioreg &= ~MSC313_GPIO_OUT;
+ writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]);
+
+ return 0;
+}
+
+/*
+ * The interrupt handling happens in the parent interrupt controller,
+ * we don't do anything here.
+ */
+static struct irq_chip msc313_gpio_irqchip = {
+ .name = "GPIO",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_set_type = irq_chip_set_type_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
+/*
+ * The parent interrupt controller needs the GIC interrupt type set to GIC_SPI
+ * so we need to provide the fwspec. Essentially gpiochip_populate_parent_fwspec_twocell
+ * that puts GIC_SPI into the first cell.
+ */
+static void *msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc,
+ unsigned int parent_hwirq,
+ unsigned int parent_type)
+{
+ struct irq_fwspec *fwspec;
+
+ fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
+ if (!fwspec)
+ return NULL;
+
+ fwspec->fwnode = gc->irq.parent_domain->fwnode;
+ fwspec->param_count = 3;
+ fwspec->param[0] = GIC_SPI;
+ fwspec->param[1] = parent_hwirq;
+ fwspec->param[2] = parent_type;
+
+ return fwspec;
+}
+
+static int msc313e_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
+ unsigned int child,
+ unsigned int child_type,
+ unsigned int *parent,
+ unsigned int *parent_type)
+{
+ struct msc313_gpio *priv = gpiochip_get_data(chip);
+ unsigned int offset = priv->gpio_data->offsets[child];
+
+ /*
+ * only the spi0 pins have interrupts on the parent
+ * on all of the known chips and so far they are all
+ * mapped to the same place
+ */
+ if (offset >= OFF_SPI0_CZ && offset <= OFF_SPI0_DO) {
+ *parent_type = child_type;
+ *parent = ((offset - OFF_SPI0_CZ) >> 2) + 28;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int msc313_gpio_probe(struct platform_device *pdev)
+{
+ const struct msc313_gpio_data *match_data;
+ struct msc313_gpio *gpio;
+ struct gpio_chip *gpiochip;
+ struct gpio_irq_chip *gpioirqchip;
+ struct irq_domain *parent_domain;
+ struct device_node *parent_node;
+ struct device *dev = &pdev->dev;
+
+ match_data = of_device_get_match_data(dev);
+ if (!match_data)
+ return -EINVAL;
+
+ parent_node = of_irq_find_parent(dev->of_node);
+ if (!parent_node)
+ return -ENODEV;
+
+ parent_domain = irq_find_host(parent_node);
+ if (!parent_domain)
+ return -ENODEV;
+
+ gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->gpio_data = match_data;
+
+ gpio->saved = devm_kcalloc(dev, gpio->gpio_data->num, sizeof(*gpio->saved), GFP_KERNEL);
+ if (!gpio->saved)
+ return -ENOMEM;
+
+ gpio->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(gpio->base))
+ return PTR_ERR(gpio->base);
+
+ platform_set_drvdata(pdev, gpio);
+
+ gpiochip = devm_kzalloc(dev, sizeof(*gpiochip), GFP_KERNEL);
+ if (!gpiochip)
+ return -ENOMEM;
+
+ gpiochip->label = DRIVER_NAME;
+ gpiochip->parent = dev;
+ gpiochip->request = gpiochip_generic_request;
+ gpiochip->free = gpiochip_generic_free;
+ gpiochip->direction_input = msc313_gpio_direction_input;
+ gpiochip->direction_output = msc313_gpio_direction_output;
+ gpiochip->get = msc313_gpio_get;
+ gpiochip->set = msc313_gpio_set;
+ gpiochip->base = -1;
+ gpiochip->ngpio = gpio->gpio_data->num;
+ gpiochip->names = gpio->gpio_data->names;
+
+ gpioirqchip = &gpiochip->irq;
+ gpioirqchip->chip = &msc313_gpio_irqchip;
+ gpioirqchip->fwnode = of_node_to_fwnode(dev->of_node);
+ gpioirqchip->parent_domain = parent_domain;
+ gpioirqchip->child_to_parent_hwirq = msc313e_gpio_child_to_parent_hwirq;
+ gpioirqchip->populate_parent_alloc_arg = msc313_gpio_populate_parent_fwspec;
+ gpioirqchip->handler = handle_bad_irq;
+ gpioirqchip->default_type = IRQ_TYPE_NONE;
+
+ return devm_gpiochip_add_data(dev, gpiochip, gpio);
+}
+
+static int msc313_gpio_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id msc313_gpio_of_match[] = {
+#ifdef CONFIG_MACH_INFINITY
+ {
+ .compatible = "mstar,msc313-gpio",
+ .data = &msc313_data,
+ },
+ {
+ .compatible = "sstar,ssd20xd-gpio",
+ .data = &ssd20xd_data,
+ },
+#endif
+ { }
+};
+
+/*
+ * The GPIO controller loses the state of the registers when the
+ * SoC goes into suspend to memory mode so we need to save some
+ * of the register bits before suspending and put it back when resuming
+ */
+static int __maybe_unused msc313_gpio_suspend(struct device *dev)
+{
+ struct msc313_gpio *gpio = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < gpio->gpio_data->num; i++)
+ gpio->saved[i] = readb_relaxed(gpio->base + gpio->gpio_data->offsets[i]) & MSC313_GPIO_BITSTOSAVE;
+
+ return 0;
+}
+
+static int __maybe_unused msc313_gpio_resume(struct device *dev)
+{
+ struct msc313_gpio *gpio = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < gpio->gpio_data->num; i++)
+ writeb_relaxed(gpio->saved[i], gpio->base + gpio->gpio_data->offsets[i]);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume);
+
+static struct platform_driver msc313_gpio_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = msc313_gpio_of_match,
+ .pm = &msc313_gpio_ops,
+ },
+ .probe = msc313_gpio_probe,
+ .remove = msc313_gpio_remove,
+};
+builtin_platform_driver(msc313_gpio_driver);
diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c
deleted file mode 100644
index 7e3c96e4ab2c..000000000000
--- a/drivers/gpio/gpio-msic.c
+++ /dev/null
@@ -1,314 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Intel Medfield MSIC GPIO driver>
- * Copyright (c) 2011, Intel Corporation.
- *
- * Author: Mathias Nyman <mathias.nyman@linux.intel.com>
- * Based on intel_pmic_gpio.c
- */
-
-#include <linux/gpio/driver.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mfd/intel_msic.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-/* the offset for the mapping of global gpio pin to irq */
-#define MSIC_GPIO_IRQ_OFFSET 0x100
-
-#define MSIC_GPIO_DIR_IN 0
-#define MSIC_GPIO_DIR_OUT BIT(5)
-#define MSIC_GPIO_TRIG_FALL BIT(1)
-#define MSIC_GPIO_TRIG_RISE BIT(2)
-
-/* masks for msic gpio output GPIOxxxxCTLO registers */
-#define MSIC_GPIO_DIR_MASK BIT(5)
-#define MSIC_GPIO_DRV_MASK BIT(4)
-#define MSIC_GPIO_REN_MASK BIT(3)
-#define MSIC_GPIO_RVAL_MASK (BIT(2) | BIT(1))
-#define MSIC_GPIO_DOUT_MASK BIT(0)
-
-/* masks for msic gpio input GPIOxxxxCTLI registers */
-#define MSIC_GPIO_GLBYP_MASK BIT(5)
-#define MSIC_GPIO_DBNC_MASK (BIT(4) | BIT(3))
-#define MSIC_GPIO_INTCNT_MASK (BIT(2) | BIT(1))
-#define MSIC_GPIO_DIN_MASK BIT(0)
-
-#define MSIC_NUM_GPIO 24
-
-struct msic_gpio {
- struct platform_device *pdev;
- struct mutex buslock;
- struct gpio_chip chip;
- int irq;
- unsigned irq_base;
- unsigned long trig_change_mask;
- unsigned trig_type;
-};
-
-/*
- * MSIC has 24 gpios, 16 low voltage (1.2-1.8v) and 8 high voltage (3v).
- * Both the high and low voltage gpios are divided in two banks.
- * GPIOs are numbered with GPIO0LV0 as gpio_base in the following order:
- * GPIO0LV0..GPIO0LV7: low voltage, bank 0, gpio_base
- * GPIO1LV0..GPIO1LV7: low voltage, bank 1, gpio_base + 8
- * GPIO0HV0..GPIO0HV3: high voltage, bank 0, gpio_base + 16
- * GPIO1HV0..GPIO1HV3: high voltage, bank 1, gpio_base + 20
- */
-
-static int msic_gpio_to_ireg(unsigned offset)
-{
- if (offset >= MSIC_NUM_GPIO)
- return -EINVAL;
-
- if (offset < 8)
- return INTEL_MSIC_GPIO0LV0CTLI - offset;
- if (offset < 16)
- return INTEL_MSIC_GPIO1LV0CTLI - offset + 8;
- if (offset < 20)
- return INTEL_MSIC_GPIO0HV0CTLI - offset + 16;
-
- return INTEL_MSIC_GPIO1HV0CTLI - offset + 20;
-}
-
-static int msic_gpio_to_oreg(unsigned offset)
-{
- if (offset >= MSIC_NUM_GPIO)
- return -EINVAL;
-
- if (offset < 8)
- return INTEL_MSIC_GPIO0LV0CTLO - offset;
- if (offset < 16)
- return INTEL_MSIC_GPIO1LV0CTLO - offset + 8;
- if (offset < 20)
- return INTEL_MSIC_GPIO0HV0CTLO - offset + 16;
-
- return INTEL_MSIC_GPIO1HV0CTLO - offset + 20;
-}
-
-static int msic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- int reg;
-
- reg = msic_gpio_to_oreg(offset);
- if (reg < 0)
- return reg;
-
- return intel_msic_reg_update(reg, MSIC_GPIO_DIR_IN, MSIC_GPIO_DIR_MASK);
-}
-
-static int msic_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- int reg;
- unsigned mask;
-
- value = (!!value) | MSIC_GPIO_DIR_OUT;
- mask = MSIC_GPIO_DIR_MASK | MSIC_GPIO_DOUT_MASK;
-
- reg = msic_gpio_to_oreg(offset);
- if (reg < 0)
- return reg;
-
- return intel_msic_reg_update(reg, value, mask);
-}
-
-static int msic_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- u8 r;
- int ret;
- int reg;
-
- reg = msic_gpio_to_ireg(offset);
- if (reg < 0)
- return reg;
-
- ret = intel_msic_reg_read(reg, &r);
- if (ret < 0)
- return ret;
-
- return !!(r & MSIC_GPIO_DIN_MASK);
-}
-
-static void msic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- int reg;
-
- reg = msic_gpio_to_oreg(offset);
- if (reg < 0)
- return;
-
- intel_msic_reg_update(reg, !!value , MSIC_GPIO_DOUT_MASK);
-}
-
-/*
- * This is called from genirq with mg->buslock locked and
- * irq_desc->lock held. We can not access the scu bus here, so we
- * store the change and update in the bus_sync_unlock() function below
- */
-static int msic_irq_type(struct irq_data *data, unsigned type)
-{
- struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
- u32 gpio = data->irq - mg->irq_base;
-
- if (gpio >= mg->chip.ngpio)
- return -EINVAL;
-
- /* mark for which gpio the trigger changed, protected by buslock */
- mg->trig_change_mask |= (1 << gpio);
- mg->trig_type = type;
-
- return 0;
-}
-
-static int msic_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- struct msic_gpio *mg = gpiochip_get_data(chip);
- return mg->irq_base + offset;
-}
-
-static void msic_bus_lock(struct irq_data *data)
-{
- struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
- mutex_lock(&mg->buslock);
-}
-
-static void msic_bus_sync_unlock(struct irq_data *data)
-{
- struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
- int offset;
- int reg;
- u8 trig = 0;
-
- /* We can only get one change at a time as the buslock covers the
- entire transaction. The irq_desc->lock is dropped before we are
- called but that is fine */
- if (mg->trig_change_mask) {
- offset = __ffs(mg->trig_change_mask);
-
- reg = msic_gpio_to_ireg(offset);
- if (reg < 0)
- goto out;
-
- if (mg->trig_type & IRQ_TYPE_EDGE_RISING)
- trig |= MSIC_GPIO_TRIG_RISE;
- if (mg->trig_type & IRQ_TYPE_EDGE_FALLING)
- trig |= MSIC_GPIO_TRIG_FALL;
-
- intel_msic_reg_update(reg, trig, MSIC_GPIO_INTCNT_MASK);
- mg->trig_change_mask = 0;
- }
-out:
- mutex_unlock(&mg->buslock);
-}
-
-/* Firmware does all the masking and unmasking for us, no masking here. */
-static void msic_irq_unmask(struct irq_data *data) { }
-
-static void msic_irq_mask(struct irq_data *data) { }
-
-static struct irq_chip msic_irqchip = {
- .name = "MSIC-GPIO",
- .irq_mask = msic_irq_mask,
- .irq_unmask = msic_irq_unmask,
- .irq_set_type = msic_irq_type,
- .irq_bus_lock = msic_bus_lock,
- .irq_bus_sync_unlock = msic_bus_sync_unlock,
-};
-
-static void msic_gpio_irq_handler(struct irq_desc *desc)
-{
- struct irq_data *data = irq_desc_get_irq_data(desc);
- struct msic_gpio *mg = irq_data_get_irq_handler_data(data);
- struct irq_chip *chip = irq_data_get_irq_chip(data);
- struct intel_msic *msic = pdev_to_intel_msic(mg->pdev);
- unsigned long pending;
- int i;
- int bitnr;
- u8 pin;
-
- for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) {
- intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin);
- pending = pin;
-
- for_each_set_bit(bitnr, &pending, BITS_PER_BYTE)
- generic_handle_irq(mg->irq_base + i * BITS_PER_BYTE + bitnr);
- }
- chip->irq_eoi(data);
-}
-
-static int platform_msic_gpio_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct intel_msic_gpio_pdata *pdata = dev_get_platdata(dev);
- struct msic_gpio *mg;
- int irq = platform_get_irq(pdev, 0);
- int retval;
- int i;
-
- if (irq < 0) {
- dev_err(dev, "no IRQ line: %d\n", irq);
- return irq;
- }
-
- if (!pdata || !pdata->gpio_base) {
- dev_err(dev, "incorrect or missing platform data\n");
- return -EINVAL;
- }
-
- mg = kzalloc(sizeof(*mg), GFP_KERNEL);
- if (!mg)
- return -ENOMEM;
-
- dev_set_drvdata(dev, mg);
-
- mg->pdev = pdev;
- mg->irq = irq;
- mg->irq_base = pdata->gpio_base + MSIC_GPIO_IRQ_OFFSET;
- mg->chip.label = "msic_gpio";
- mg->chip.direction_input = msic_gpio_direction_input;
- mg->chip.direction_output = msic_gpio_direction_output;
- mg->chip.get = msic_gpio_get;
- mg->chip.set = msic_gpio_set;
- mg->chip.to_irq = msic_gpio_to_irq;
- mg->chip.base = pdata->gpio_base;
- mg->chip.ngpio = MSIC_NUM_GPIO;
- mg->chip.can_sleep = true;
- mg->chip.parent = dev;
-
- mutex_init(&mg->buslock);
-
- retval = gpiochip_add_data(&mg->chip, mg);
- if (retval) {
- dev_err(dev, "Adding MSIC gpio chip failed\n");
- goto err;
- }
-
- for (i = 0; i < mg->chip.ngpio; i++) {
- irq_set_chip_data(i + mg->irq_base, mg);
- irq_set_chip_and_handler(i + mg->irq_base,
- &msic_irqchip,
- handle_simple_irq);
- }
- irq_set_chained_handler_and_data(mg->irq, msic_gpio_irq_handler, mg);
-
- return 0;
-err:
- kfree(mg);
- return retval;
-}
-
-static struct platform_driver platform_msic_gpio_driver = {
- .driver = {
- .name = "msic_gpio",
- },
- .probe = platform_msic_gpio_probe,
-};
-
-static int __init platform_msic_gpio_init(void)
-{
- return platform_driver_register(&platform_msic_gpio_driver);
-}
-subsys_initcall(platform_msic_gpio_init);
diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c
index 79654fb2e50f..d8a26e503ca5 100644
--- a/drivers/gpio/gpio-mt7621.c
+++ b/drivers/gpio/gpio-mt7621.c
@@ -95,9 +95,7 @@ mediatek_gpio_irq_handler(int irq, void *data)
pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
- u32 map = irq_find_mapping(gc->irq.domain, bit);
-
- generic_handle_irq(map);
+ generic_handle_domain_irq(gc->irq.domain, bit);
mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
ret |= IRQ_HANDLED;
}
@@ -207,8 +205,7 @@ mediatek_gpio_xlate(struct gpio_chip *chip,
}
static int
-mediatek_gpio_bank_probe(struct device *dev,
- struct device_node *node, int bank)
+mediatek_gpio_bank_probe(struct device *dev, int bank)
{
struct mtk *mtk = dev_get_drvdata(dev);
struct mtk_gc *rg;
@@ -219,7 +216,6 @@ mediatek_gpio_bank_probe(struct device *dev,
memset(rg, 0, sizeof(*rg));
spin_lock_init(&rg->lock);
- rg->chip.of_node = node;
rg->bank = bank;
dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE);
@@ -227,8 +223,8 @@ mediatek_gpio_bank_probe(struct device *dev,
ctrl = mtk->base + GPIO_REG_DCLR + (rg->bank * GPIO_BANK_STRIDE);
diro = mtk->base + GPIO_REG_CTRL + (rg->bank * GPIO_BANK_STRIDE);
- ret = bgpio_init(&rg->chip, dev, 4,
- dat, set, ctrl, diro, NULL, 0);
+ ret = bgpio_init(&rg->chip, dev, 4, dat, set, ctrl, diro, NULL,
+ BGPIOF_NO_SET_ON_INPUT);
if (ret) {
dev_err(dev, "bgpio_init() failed\n");
return ret;
@@ -241,25 +237,19 @@ mediatek_gpio_bank_probe(struct device *dev,
if (!rg->chip.label)
return -ENOMEM;
- ret = devm_gpiochip_add_data(dev, &rg->chip, mtk);
- if (ret < 0) {
- dev_err(dev, "Could not register gpio %d, ret=%d\n",
- rg->chip.ngpio, ret);
- return ret;
- }
-
+ rg->chip.offset = bank * MTK_BANK_WIDTH;
rg->irq_chip.name = dev_name(dev);
- rg->irq_chip.parent_device = dev;
rg->irq_chip.irq_unmask = mediatek_gpio_irq_unmask;
rg->irq_chip.irq_mask = mediatek_gpio_irq_mask;
rg->irq_chip.irq_mask_ack = mediatek_gpio_irq_mask;
rg->irq_chip.irq_set_type = mediatek_gpio_irq_type;
if (mtk->gpio_irq) {
+ struct gpio_irq_chip *girq;
+
/*
- * Manually request the irq here instead of passing
- * a flow-handler to gpiochip_set_chained_irqchip,
- * because the irq is shared.
+ * Directly request the irq here instead of passing
+ * a flow-handler because the irq is shared.
*/
ret = devm_request_irq(dev, mtk->gpio_irq,
mediatek_gpio_irq_handler, IRQF_SHARED,
@@ -271,15 +261,21 @@ mediatek_gpio_bank_probe(struct device *dev,
return ret;
}
- ret = gpiochip_irqchip_add(&rg->chip, &rg->irq_chip,
- 0, handle_simple_irq, IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "failed to add gpiochip_irqchip\n");
- return ret;
- }
+ girq = &rg->chip.irq;
+ girq->chip = &rg->irq_chip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ }
- gpiochip_set_chained_irqchip(&rg->chip, &rg->irq_chip,
- mtk->gpio_irq, NULL);
+ ret = devm_gpiochip_add_data(dev, &rg->chip, mtk);
+ if (ret < 0) {
+ dev_err(dev, "Could not register gpio %d, ret=%d\n",
+ rg->chip.ngpio, ret);
+ return ret;
}
/* set polarity to low for all gpios */
@@ -312,7 +308,7 @@ mediatek_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mtk);
for (i = 0; i < MTK_BANK_CNT; i++) {
- ret = mediatek_gpio_bank_probe(dev, np, i);
+ ret = mediatek_gpio_bank_probe(dev, i);
if (ret)
return ret;
}
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index 869d47f89599..2db19cd640a4 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -46,7 +46,6 @@
#include <linux/irqdomain.h>
#include <linux/mfd/syscon.h>
#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
@@ -71,7 +70,12 @@
*/
#define PWM_BLINK_ON_DURATION_OFF 0x0
#define PWM_BLINK_OFF_DURATION_OFF 0x4
+#define PWM_BLINK_COUNTER_B_OFF 0x8
+/* Armada 8k variant gpios register offsets */
+#define AP80X_GPIO0_OFF_A8K 0x1040
+#define CP11X_GPIO0_OFF_A8K 0x100
+#define CP11X_GPIO1_OFF_A8K 0x140
/* The MV78200 has per-CPU registers for edge mask and level mask */
#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
@@ -79,8 +83,7 @@
/*
* The Armada XP has per-CPU registers for interrupt cause, interrupt
- * mask and interrupt level mask. Those are relative to the
- * percpu_membase.
+ * mask and interrupt level mask. Those are in percpu_regs range.
*/
#define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4)
#define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4)
@@ -94,7 +97,8 @@
#define MVEBU_MAX_GPIO_PER_BANK 32
struct mvebu_pwm {
- void __iomem *membase;
+ struct regmap *regs;
+ u32 offset;
unsigned long clk_rate;
struct gpio_desc *gpiod;
struct pwm_chip chip;
@@ -280,17 +284,17 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val)
}
/*
- * Functions returning addresses of individual registers for a given
+ * Functions returning offsets of individual registers for a given
* PWM controller.
*/
-static void __iomem *mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm)
+static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm)
{
- return mvpwm->membase + PWM_BLINK_ON_DURATION_OFF;
+ return mvpwm->offset + PWM_BLINK_ON_DURATION_OFF;
}
-static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm)
+static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm)
{
- return mvpwm->membase + PWM_BLINK_OFF_DURATION_OFF;
+ return mvpwm->offset + PWM_BLINK_OFF_DURATION_OFF;
}
/*
@@ -384,7 +388,10 @@ static int mvebu_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u);
- return !!(u & BIT(pin));
+ if (u & BIT(pin))
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned int pin)
@@ -429,6 +436,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)
u32 mask = d->mask;
irq_gc_lock(gc);
+ mvebu_gpio_write_edge_cause(mvchip, ~mask);
ct->mask_cache_priv |= mask;
mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv);
irq_gc_unlock(gc);
@@ -597,6 +605,13 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
+static const struct regmap_config mvebu_gpio_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+};
+
/*
* Functions implementing the pwm_chip methods
*/
@@ -657,32 +672,22 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip,
spin_lock_irqsave(&mvpwm->lock, flags);
- val = (unsigned long long)
- readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
- val *= NSEC_PER_SEC;
- do_div(val, mvpwm->clk_rate);
- if (val > UINT_MAX)
- state->duty_cycle = UINT_MAX;
- else if (val)
- state->duty_cycle = val;
+ regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), &u);
+ /* Hardware treats zero as 2^32. See mvebu_pwm_apply(). */
+ if (u > 0)
+ val = u;
else
- state->duty_cycle = 1;
-
- val = (unsigned long long)
- readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm));
- val *= NSEC_PER_SEC;
- do_div(val, mvpwm->clk_rate);
- if (val < state->duty_cycle) {
- state->period = 1;
- } else {
- val -= state->duty_cycle;
- if (val > UINT_MAX)
- state->period = UINT_MAX;
- else if (val)
- state->period = val;
- else
- state->period = 1;
- }
+ val = UINT_MAX + 1ULL;
+ state->duty_cycle = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC,
+ mvpwm->clk_rate);
+
+ regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u);
+ /* period = on + off duration */
+ if (u > 0)
+ val += u;
+ else
+ val += UINT_MAX + 1ULL;
+ state->period = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, mvpwm->clk_rate);
regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u);
if (u)
@@ -694,7 +699,7 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip,
}
static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
- struct pwm_state *state)
+ const struct pwm_state *state)
{
struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
@@ -702,29 +707,40 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned long flags;
unsigned int on, off;
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
val = (unsigned long long) mvpwm->clk_rate * state->duty_cycle;
do_div(val, NSEC_PER_SEC);
- if (val > UINT_MAX)
+ if (val > UINT_MAX + 1ULL)
return -EINVAL;
- if (val)
+ /*
+ * Zero on/off values don't work as expected. Experimentation shows
+ * that zero value is treated as 2^32. This behavior is not documented.
+ */
+ if (val == UINT_MAX + 1ULL)
+ on = 0;
+ else if (val)
on = val;
else
on = 1;
- val = (unsigned long long) mvpwm->clk_rate *
- (state->period - state->duty_cycle);
+ val = (unsigned long long) mvpwm->clk_rate * state->period;
do_div(val, NSEC_PER_SEC);
- if (val > UINT_MAX)
+ val -= on;
+ if (val > UINT_MAX + 1ULL)
return -EINVAL;
- if (val)
+ if (val == UINT_MAX + 1ULL)
+ off = 0;
+ else if (val)
off = val;
else
off = 1;
spin_lock_irqsave(&mvpwm->lock, flags);
- writel_relaxed(on, mvebu_pwmreg_blink_on_duration(mvpwm));
- writel_relaxed(off, mvebu_pwmreg_blink_off_duration(mvpwm));
+ regmap_write(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), on);
+ regmap_write(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), off);
if (state->enabled)
mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 1);
else
@@ -749,10 +765,10 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
&mvpwm->blink_select);
- mvpwm->blink_on_duration =
- readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
- mvpwm->blink_off_duration =
- readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm));
+ regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm),
+ &mvpwm->blink_on_duration);
+ regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm),
+ &mvpwm->blink_off_duration);
}
static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
@@ -761,10 +777,10 @@ static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
mvpwm->blink_select);
- writel_relaxed(mvpwm->blink_on_duration,
- mvebu_pwmreg_blink_on_duration(mvpwm));
- writel_relaxed(mvpwm->blink_off_duration,
- mvebu_pwmreg_blink_off_duration(mvpwm));
+ regmap_write(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm),
+ mvpwm->blink_on_duration);
+ regmap_write(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm),
+ mvpwm->blink_off_duration);
}
static int mvebu_pwm_probe(struct platform_device *pdev,
@@ -773,48 +789,81 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
{
struct device *dev = &pdev->dev;
struct mvebu_pwm *mvpwm;
- struct resource *res;
+ void __iomem *base;
+ u32 offset;
u32 set;
- if (!of_device_is_compatible(mvchip->chip.of_node,
- "marvell,armada-370-gpio"))
- return 0;
-
- /*
- * There are only two sets of PWM configuration registers for
- * all the GPIO lines on those SoCs which this driver reserves
- * for the first two GPIO chips. So if the resource is missing
- * we can't treat it as an error.
- */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
- if (!res)
+ if (of_device_is_compatible(mvchip->chip.of_node,
+ "marvell,armada-370-gpio")) {
+ /*
+ * There are only two sets of PWM configuration registers for
+ * all the GPIO lines on those SoCs which this driver reserves
+ * for the first two GPIO chips. So if the resource is missing
+ * we can't treat it as an error.
+ */
+ if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"))
+ return 0;
+ offset = 0;
+ } else if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
+ int ret = of_property_read_u32(dev->of_node,
+ "marvell,pwm-offset", &offset);
+ if (ret < 0)
+ return 0;
+ } else {
return 0;
+ }
if (IS_ERR(mvchip->clk))
return PTR_ERR(mvchip->clk);
- /*
- * Use set A for lines of GPIO chip with id 0, B for GPIO chip
- * with id 1. Don't allow further GPIO chips to be used for PWM.
- */
- if (id == 0)
- set = 0;
- else if (id == 1)
- set = U32_MAX;
- else
- return -EINVAL;
- regmap_write(mvchip->regs,
- GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
-
mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
if (!mvpwm)
return -ENOMEM;
mvchip->mvpwm = mvpwm;
mvpwm->mvchip = mvchip;
+ mvpwm->offset = offset;
+
+ if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
+ mvpwm->regs = mvchip->regs;
+
+ switch (mvchip->offset) {
+ case AP80X_GPIO0_OFF_A8K:
+ case CP11X_GPIO0_OFF_A8K:
+ /* Blink counter A */
+ set = 0;
+ break;
+ case CP11X_GPIO1_OFF_A8K:
+ /* Blink counter B */
+ set = U32_MAX;
+ mvpwm->offset += PWM_BLINK_COUNTER_B_OFF;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ base = devm_platform_ioremap_resource_byname(pdev, "pwm");
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base,
+ &mvebu_gpio_regmap_config);
+ if (IS_ERR(mvpwm->regs))
+ return PTR_ERR(mvpwm->regs);
+
+ /*
+ * Use set A for lines of GPIO chip with id 0, B for GPIO chip
+ * with id 1. Don't allow further GPIO chips to be used for PWM.
+ */
+ if (id == 0)
+ set = 0;
+ else if (id == 1)
+ set = U32_MAX;
+ else
+ return -EINVAL;
+ }
- mvpwm->membase = devm_ioremap_resource(dev, res);
- if (IS_ERR(mvpwm->membase))
- return PTR_ERR(mvpwm->membase);
+ regmap_write(mvchip->regs,
+ GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
mvpwm->clk_rate = clk_get_rate(mvchip->clk);
if (!mvpwm->clk_rate) {
@@ -825,13 +874,6 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
mvpwm->chip.dev = dev;
mvpwm->chip.ops = &mvebu_pwm_ops;
mvpwm->chip.npwm = mvchip->chip.ngpio;
- /*
- * There may already be some PWM allocated, so we can't force
- * mvpwm->chip.base to a fixed point like mvchip->chip.base.
- * So, we let pwmchip_add() do the numbering and take the next free
- * region.
- */
- mvpwm->chip.base = -1;
spin_lock_init(&mvpwm->lock);
@@ -845,6 +887,7 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk;
+ const char *label;
int i;
regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out);
@@ -856,15 +899,10 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
edg_msk = mvebu_gpio_read_edge_mask(mvchip);
lvl_msk = mvebu_gpio_read_level_mask(mvchip);
- for (i = 0; i < chip->ngpio; i++) {
- const char *label;
+ for_each_requested_gpio(chip, i, label) {
u32 msk;
bool is_out;
- label = gpiochip_is_requested(chip, i);
- if (!label)
- continue;
-
msk = BIT(i);
is_out = !(io_conf & msk);
@@ -1025,13 +1063,6 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
return 0;
}
-static const struct regmap_config mvebu_gpio_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .fast_io = true,
-};
-
static int mvebu_gpio_probe_raw(struct platform_device *pdev,
struct mvebu_gpio_chip *mvchip)
{
@@ -1104,7 +1135,11 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
/* Some gpio controllers do not provide irq support */
- have_irqs = of_irq_count(np) != 0;
+ err = platform_irq_count(pdev);
+ if (err < 0)
+ return err;
+
+ have_irqs = err != 0;
mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip),
GFP_KERNEL);
@@ -1144,7 +1179,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK;
mvchip->chip.ngpio = ngpios;
mvchip->chip.can_sleep = false;
- mvchip->chip.of_node = np;
mvchip->chip.dbg_show = mvebu_gpio_dbg_show;
if (soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K)
@@ -1196,6 +1230,13 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
+ /* Some MVEBU SoCs have simple PWM support for GPIO lines */
+ if (IS_ENABLED(CONFIG_PWM)) {
+ err = mvebu_pwm_probe(pdev, mvchip, id);
+ if (err)
+ return err;
+ }
+
/* Some gpio controllers do not provide irq support */
if (!have_irqs)
return 0;
@@ -1205,7 +1246,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
if (!mvchip->domain) {
dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
mvchip->chip.label);
- return -ENODEV;
+ err = -ENODEV;
+ goto err_pwm;
}
err = irq_alloc_domain_generic_chips(
@@ -1245,7 +1287,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
* pins.
*/
for (i = 0; i < 4; i++) {
- int irq = platform_get_irq(pdev, i);
+ int irq = platform_get_irq_optional(pdev, i);
if (irq < 0)
continue;
@@ -1253,14 +1295,12 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
mvchip);
}
- /* Some MVEBU SoCs have simple PWM support for GPIO lines */
- if (IS_ENABLED(CONFIG_PWM))
- return mvebu_pwm_probe(pdev, mvchip, id);
-
return 0;
err_domain:
irq_domain_remove(mvchip->domain);
+err_pwm:
+ pwmchip_remove(&mvchip->mvpwm->chip);
return err;
}
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index b2813580c582..c871602fc5ba 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -15,6 +15,7 @@
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/syscore_ops.h>
@@ -23,13 +24,6 @@
#include <linux/of_device.h>
#include <linux/bug.h>
-enum mxc_gpio_hwtype {
- IMX1_GPIO, /* runs on i.mx1 */
- IMX21_GPIO, /* runs on i.mx21 and i.mx27 */
- IMX31_GPIO, /* runs on i.mx31 */
- IMX35_GPIO, /* runs on all other i.mx */
-};
-
/* device type dependent stuff */
struct mxc_gpio_hwdata {
unsigned dr_reg;
@@ -67,6 +61,7 @@ struct mxc_gpio_port {
u32 both_edges;
struct mxc_gpio_reg_saved gpio_saved_reg;
bool power_off;
+ const struct mxc_gpio_hwdata *hwdata;
};
static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
@@ -114,50 +109,30 @@ static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
.fall_edge = 0x03,
};
-static enum mxc_gpio_hwtype mxc_gpio_hwtype;
-static struct mxc_gpio_hwdata *mxc_gpio_hwdata;
-
-#define GPIO_DR (mxc_gpio_hwdata->dr_reg)
-#define GPIO_GDIR (mxc_gpio_hwdata->gdir_reg)
-#define GPIO_PSR (mxc_gpio_hwdata->psr_reg)
-#define GPIO_ICR1 (mxc_gpio_hwdata->icr1_reg)
-#define GPIO_ICR2 (mxc_gpio_hwdata->icr2_reg)
-#define GPIO_IMR (mxc_gpio_hwdata->imr_reg)
-#define GPIO_ISR (mxc_gpio_hwdata->isr_reg)
-#define GPIO_EDGE_SEL (mxc_gpio_hwdata->edge_sel_reg)
-
-#define GPIO_INT_LOW_LEV (mxc_gpio_hwdata->low_level)
-#define GPIO_INT_HIGH_LEV (mxc_gpio_hwdata->high_level)
-#define GPIO_INT_RISE_EDGE (mxc_gpio_hwdata->rise_edge)
-#define GPIO_INT_FALL_EDGE (mxc_gpio_hwdata->fall_edge)
+#define GPIO_DR (port->hwdata->dr_reg)
+#define GPIO_GDIR (port->hwdata->gdir_reg)
+#define GPIO_PSR (port->hwdata->psr_reg)
+#define GPIO_ICR1 (port->hwdata->icr1_reg)
+#define GPIO_ICR2 (port->hwdata->icr2_reg)
+#define GPIO_IMR (port->hwdata->imr_reg)
+#define GPIO_ISR (port->hwdata->isr_reg)
+#define GPIO_EDGE_SEL (port->hwdata->edge_sel_reg)
+
+#define GPIO_INT_LOW_LEV (port->hwdata->low_level)
+#define GPIO_INT_HIGH_LEV (port->hwdata->high_level)
+#define GPIO_INT_RISE_EDGE (port->hwdata->rise_edge)
+#define GPIO_INT_FALL_EDGE (port->hwdata->fall_edge)
#define GPIO_INT_BOTH_EDGES 0x4
-static const struct platform_device_id mxc_gpio_devtype[] = {
- {
- .name = "imx1-gpio",
- .driver_data = IMX1_GPIO,
- }, {
- .name = "imx21-gpio",
- .driver_data = IMX21_GPIO,
- }, {
- .name = "imx31-gpio",
- .driver_data = IMX31_GPIO,
- }, {
- .name = "imx35-gpio",
- .driver_data = IMX35_GPIO,
- }, {
- /* sentinel */
- }
-};
-
static const struct of_device_id mxc_gpio_dt_ids[] = {
- { .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
- { .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
- { .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
- { .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
- { .compatible = "fsl,imx7d-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
+ { .compatible = "fsl,imx1-gpio", .data = &imx1_imx21_gpio_hwdata },
+ { .compatible = "fsl,imx21-gpio", .data = &imx1_imx21_gpio_hwdata },
+ { .compatible = "fsl,imx31-gpio", .data = &imx31_gpio_hwdata },
+ { .compatible = "fsl,imx35-gpio", .data = &imx35_gpio_hwdata },
+ { .compatible = "fsl,imx7d-gpio", .data = &imx35_gpio_hwdata },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, mxc_gpio_dt_ids);
/*
* MX2 has one interrupt *for all* gpio ports. The list is used
@@ -266,7 +241,7 @@ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
if (port->both_edges & (1 << irqoffset))
mxc_flip_edge(port, irqoffset);
- generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
+ generic_handle_domain_irq(port->domain, irqoffset);
irq_stat &= ~(1 << irqoffset);
}
@@ -359,7 +334,7 @@ static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
ct->chip.irq_unmask = irq_gc_mask_set_bit;
ct->chip.irq_set_type = gpio_set_irq_type;
ct->chip.irq_set_wake = gpio_set_wake_irq;
- ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+ ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND;
ct->regs.ack = GPIO_ISR;
ct->regs.mask = GPIO_IMR;
@@ -370,36 +345,6 @@ static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
return rv;
}
-static void mxc_gpio_get_hw(struct platform_device *pdev)
-{
- const struct of_device_id *of_id =
- of_match_device(mxc_gpio_dt_ids, &pdev->dev);
- enum mxc_gpio_hwtype hwtype;
-
- if (of_id)
- pdev->id_entry = of_id->data;
- hwtype = pdev->id_entry->driver_data;
-
- if (mxc_gpio_hwtype) {
- /*
- * The driver works with a reasonable presupposition,
- * that is all gpio ports must be the same type when
- * running on one soc.
- */
- BUG_ON(mxc_gpio_hwtype != hwtype);
- return;
- }
-
- if (hwtype == IMX35_GPIO)
- mxc_gpio_hwdata = &imx35_gpio_hwdata;
- else if (hwtype == IMX31_GPIO)
- mxc_gpio_hwdata = &imx31_gpio_hwdata;
- else
- mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;
-
- mxc_gpio_hwtype = hwtype;
-}
-
static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct mxc_gpio_port *port = gpiochip_get_data(gc);
@@ -411,36 +356,40 @@ static int mxc_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mxc_gpio_port *port;
+ int irq_count;
int irq_base;
int err;
- mxc_gpio_get_hw(pdev);
-
port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
port->dev = &pdev->dev;
+ port->hwdata = device_get_match_data(&pdev->dev);
+
port->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(port->base))
return PTR_ERR(port->base);
- port->irq_high = platform_get_irq(pdev, 1);
- if (port->irq_high < 0)
- port->irq_high = 0;
+ irq_count = platform_irq_count(pdev);
+ if (irq_count < 0)
+ return irq_count;
+
+ if (irq_count > 1) {
+ port->irq_high = platform_get_irq(pdev, 1);
+ if (port->irq_high < 0)
+ port->irq_high = 0;
+ }
port->irq = platform_get_irq(pdev, 0);
if (port->irq < 0)
return port->irq;
/* the controller clock is optional */
- port->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(port->clk)) {
- if (PTR_ERR(port->clk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- port->clk = NULL;
- }
+ port->clk = devm_clk_get_optional(&pdev->dev, NULL);
+ if (IS_ERR(port->clk))
+ return PTR_ERR(port->clk);
err = clk_prepare_enable(port->clk);
if (err) {
@@ -455,7 +404,7 @@ static int mxc_gpio_probe(struct platform_device *pdev)
writel(0, port->base + GPIO_IMR);
writel(~0, port->base + GPIO_ISR);
- if (mxc_gpio_hwtype == IMX21_GPIO) {
+ if (of_device_is_compatible(np, "fsl,imx21-gpio")) {
/*
* Setup one handler for all GPIO interrupts. Actually setting
* the handler is needed only once, but doing it for every port
@@ -481,11 +430,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
if (err)
goto out_bgio;
- if (of_property_read_bool(np, "gpio-ranges")) {
- port->gc.request = gpiochip_generic_request;
- port->gc.free = gpiochip_generic_free;
- }
-
+ port->gc.request = gpiochip_generic_request;
+ port->gc.free = gpiochip_generic_free;
port->gc.to_irq = mxc_gpio_to_irq;
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32;
@@ -593,7 +539,6 @@ static struct platform_driver mxc_gpio_driver = {
.suppress_bind_attrs = true,
},
.probe = mxc_gpio_probe,
- .id_table = mxc_gpio_devtype,
};
static int __init gpio_mxc_init(void)
@@ -603,3 +548,7 @@ static int __init gpio_mxc_init(void)
return platform_driver_register(&mxc_gpio_driver);
}
subsys_initcall(gpio_mxc_init);
+
+MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
+MODULE_DESCRIPTION("i.MX GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index 5e5437a2c607..c5166cd47c9c 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -60,11 +60,6 @@ static inline int is_imx23_gpio(struct mxs_gpio_port *port)
return port->devid == IMX23_GPIO;
}
-static inline int is_imx28_gpio(struct mxs_gpio_port *port)
-{
- return port->devid == IMX28_GPIO;
-}
-
/* Note: This driver assumes 32 GPIOs are handled in one register */
static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
@@ -162,7 +157,7 @@ static void mxs_gpio_irq_handler(struct irq_desc *desc)
if (port->both_edges & (1 << irqoffset))
mxs_flip_edge(port, irqoffset);
- generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
+ generic_handle_domain_irq(port->domain, irqoffset);
irq_stat &= ~(1 << irqoffset);
}
}
@@ -234,35 +229,25 @@ static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
return rv;
}
-static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
{
struct mxs_gpio_port *port = gpiochip_get_data(gc);
return irq_find_mapping(port->domain, offset);
}
-static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
+static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
struct mxs_gpio_port *port = gpiochip_get_data(gc);
u32 mask = 1 << offset;
u32 dir;
dir = readl(port->base + PINCTRL_DOE(port));
- return !(dir & mask);
-}
+ if (dir & mask)
+ return GPIO_LINE_DIRECTION_OUT;
-static const struct platform_device_id mxs_gpio_ids[] = {
- {
- .name = "imx23-gpio",
- .driver_data = IMX23_GPIO,
- }, {
- .name = "imx28-gpio",
- .driver_data = IMX28_GPIO,
- }, {
- /* sentinel */
- }
-};
-MODULE_DEVICE_TABLE(platform, mxs_gpio_ids);
+ return GPIO_LINE_DIRECTION_IN;
+}
static const struct of_device_id mxs_gpio_dt_ids[] = {
{ .compatible = "fsl,imx23-gpio", .data = (void *) IMX23_GPIO, },
@@ -367,7 +352,6 @@ static struct platform_driver mxs_gpio_driver = {
.suppress_bind_attrs = true,
},
.probe = mxs_gpio_probe,
- .id_table = mxs_gpio_ids,
};
static int __init mxs_gpio_init(void)
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index d0f27084a942..80ddc43fd875 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -29,6 +29,7 @@
#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
struct gpio_regs {
+ u32 sysconfig;
u32 irqenable1;
u32 irqenable2;
u32 wake_en;
@@ -60,6 +61,7 @@ struct gpio_bank {
struct clk *dbck;
struct notifier_block nb;
unsigned int is_suspended:1;
+ unsigned int needs_resume:1;
u32 mod_usage;
u32 irq_usage;
u32 dbck_enable_mask;
@@ -609,8 +611,7 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags);
- generic_handle_irq(irq_find_mapping(bank->chip.irq.domain,
- bit));
+ generic_handle_domain_irq(bank->chip.irq.domain, bit);
raw_spin_unlock_irqrestore(&bank->wa_lock,
wa_lock_flags);
@@ -805,8 +806,10 @@ static int omap_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = gpiochip_get_data(chip);
- return !!(readl_relaxed(bank->base + bank->regs->direction) &
- BIT(offset));
+ if (readl_relaxed(bank->base + bank->regs->direction) & BIT(offset))
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int omap_gpio_input(struct gpio_chip *chip, unsigned offset)
@@ -894,12 +897,23 @@ static int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset,
unsigned long config)
{
u32 debounce;
+ int ret = -ENOTSUPP;
- if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
- return -ENOTSUPP;
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ ret = gpiochip_generic_config(chip, offset, config);
+ break;
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ debounce = pinconf_to_config_argument(config);
+ ret = omap_gpio_debounce(chip, offset, debounce);
+ break;
+ default:
+ break;
+ }
- debounce = pinconf_to_config_argument(config);
- return omap_gpio_debounce(chip, offset, debounce);
+ return ret;
}
static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
@@ -972,7 +986,8 @@ static void omap_gpio_mod_init(struct gpio_bank *bank)
writel_relaxed(0, base + bank->regs->ctrl);
}
-static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
+static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc,
+ struct device *pm_dev)
{
struct gpio_irq_chip *irq;
static int gpio;
@@ -1035,12 +1050,10 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
irq->first = irq_base;
ret = gpiochip_add_data(&bank->chip, bank);
- if (ret) {
- dev_err(bank->chip.parent,
- "Could not register gpio chip %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(bank->chip.parent, ret, "Could not register gpio chip\n");
+ irq_domain_set_pm_device(bank->chip.irq.domain, pm_dev);
ret = devm_request_irq(bank->chip.parent, bank->irq,
omap_gpio_irq_handler,
0, dev_name(bank->chip.parent), bank);
@@ -1058,6 +1071,7 @@ static void omap_gpio_init_context(struct gpio_bank *p)
const struct omap_gpio_reg_offs *regs = p->regs;
void __iomem *base = p->base;
+ p->context.sysconfig = readl_relaxed(base + regs->sysconfig);
p->context.ctrl = readl_relaxed(base + regs->ctrl);
p->context.oe = readl_relaxed(base + regs->direction);
p->context.wake_en = readl_relaxed(base + regs->wkup_en);
@@ -1077,6 +1091,7 @@ static void omap_gpio_restore_context(struct gpio_bank *bank)
const struct omap_gpio_reg_offs *regs = bank->regs;
void __iomem *base = bank->base;
+ writel_relaxed(bank->context.sysconfig, base + regs->sysconfig);
writel_relaxed(bank->context.wake_en, base + regs->wkup_en);
writel_relaxed(bank->context.ctrl, base + regs->ctrl);
writel_relaxed(bank->context.leveldetect0, base + regs->leveldetect0);
@@ -1104,6 +1119,10 @@ static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
bank->saved_datain = readl_relaxed(base + bank->regs->datain);
+ /* Save syconfig, it's runtime value can be different from init value */
+ if (bank->loses_context)
+ bank->context.sysconfig = readl_relaxed(base + bank->regs->sysconfig);
+
if (!bank->enabled_non_wakeup_gpios)
goto update_gpio_context_count;
@@ -1235,30 +1254,40 @@ static int gpio_omap_cpu_notifier(struct notifier_block *nb,
{
struct gpio_bank *bank;
unsigned long flags;
+ int ret = NOTIFY_OK;
+ u32 isr, mask;
bank = container_of(nb, struct gpio_bank, nb);
raw_spin_lock_irqsave(&bank->lock, flags);
+ if (bank->is_suspended)
+ goto out_unlock;
+
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
- if (bank->is_suspended)
+ mask = omap_get_gpio_irqbank_mask(bank);
+ isr = readl_relaxed(bank->base + bank->regs->irqstatus) & mask;
+ if (isr) {
+ ret = NOTIFY_BAD;
break;
+ }
omap_gpio_idle(bank, true);
break;
case CPU_CLUSTER_PM_ENTER_FAILED:
case CPU_CLUSTER_PM_EXIT:
- if (bank->is_suspended)
- break;
omap_gpio_unidle(bank);
break;
}
+
+out_unlock:
raw_spin_unlock_irqrestore(&bank->lock, flags);
- return NOTIFY_OK;
+ return ret;
}
static const struct omap_gpio_reg_offs omap2_gpio_regs = {
.revision = OMAP24XX_GPIO_REVISION,
+ .sysconfig = OMAP24XX_GPIO_SYSCONFIG,
.direction = OMAP24XX_GPIO_OE,
.datain = OMAP24XX_GPIO_DATAIN,
.dataout = OMAP24XX_GPIO_DATAOUT,
@@ -1282,6 +1311,7 @@ static const struct omap_gpio_reg_offs omap2_gpio_regs = {
static const struct omap_gpio_reg_offs omap4_gpio_regs = {
.revision = OMAP4_GPIO_REVISION,
+ .sysconfig = OMAP4_GPIO_SYSCONFIG,
.direction = OMAP4_GPIO_OE,
.datain = OMAP4_GPIO_DATAIN,
.dataout = OMAP4_GPIO_DATAOUT,
@@ -1344,15 +1374,14 @@ static int omap_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
- const struct of_device_id *match;
const struct omap_gpio_platform_data *pdata;
struct gpio_bank *bank;
struct irq_chip *irqc;
int ret;
- match = of_match_device(of_match_ptr(omap_gpio_match), dev);
+ pdata = device_get_match_data(dev);
- pdata = match ? match->data : dev_get_platdata(dev);
+ pdata = pdata ?: dev_get_platdata(dev);
if (!pdata)
return -EINVAL;
@@ -1375,16 +1404,12 @@ static int omap_gpio_probe(struct platform_device *pdev)
irqc->irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
irqc->name = dev_name(&pdev->dev);
irqc->flags = IRQCHIP_MASK_ON_SUSPEND;
- irqc->parent_device = dev;
bank->irq = platform_get_irq(pdev, 0);
if (bank->irq <= 0) {
if (!bank->irq)
bank->irq = -ENXIO;
- if (bank->irq != -EPROBE_DEFER)
- dev_err(dev,
- "can't get irq resource ret=%d\n", bank->irq);
- return bank->irq;
+ return dev_err_probe(dev, bank->irq, "can't get irq resource\n");
}
bank->chip.parent = dev;
@@ -1395,9 +1420,6 @@ static int omap_gpio_probe(struct platform_device *pdev)
bank->is_mpuio = pdata->is_mpuio;
bank->non_wakeup_gpios = pdata->non_wakeup_gpios;
bank->regs = pdata->regs;
-#ifdef CONFIG_OF_GPIO
- bank->chip.of_node = of_node_get(node);
-#endif
if (node) {
if (!of_property_read_bool(node, "ti,gpio-always-on"))
@@ -1445,7 +1467,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
omap_gpio_mod_init(bank);
- ret = omap_gpio_chip_init(bank, irqc);
+ ret = omap_gpio_chip_init(bank, irqc, dev);
if (ret) {
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
@@ -1503,9 +1525,34 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
return 0;
}
+static int __maybe_unused omap_gpio_suspend(struct device *dev)
+{
+ struct gpio_bank *bank = dev_get_drvdata(dev);
+
+ if (bank->is_suspended)
+ return 0;
+
+ bank->needs_resume = 1;
+
+ return omap_gpio_runtime_suspend(dev);
+}
+
+static int __maybe_unused omap_gpio_resume(struct device *dev)
+{
+ struct gpio_bank *bank = dev_get_drvdata(dev);
+
+ if (!bank->needs_resume)
+ return 0;
+
+ bank->needs_resume = 0;
+
+ return omap_gpio_runtime_resume(dev);
+}
+
static const struct dev_pm_ops gpio_pm_ops = {
SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume)
};
static struct platform_driver omap_gpio_driver = {
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index e8e9029ba5bd..bac10c2faf56 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -170,9 +170,7 @@ static int palmas_gpio_probe(struct platform_device *pdev)
palmas_gpio->gpio_chip.set = palmas_gpio_set;
palmas_gpio->gpio_chip.get = palmas_gpio_get;
palmas_gpio->gpio_chip.parent = &pdev->dev;
-#ifdef CONFIG_OF_GPIO
- palmas_gpio->gpio_chip.of_node = pdev->dev.of_node;
-#endif
+
palmas_pdata = dev_get_platdata(palmas->dev);
if (palmas_pdata && palmas_pdata->gpio_base)
palmas_gpio->gpio_chip.base = palmas_pdata->gpio_base;
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 378b206d2dc9..08bc52c3cdcb 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -9,6 +9,7 @@
*/
#include <linux/acpi.h>
+#include <linux/bitmap.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
@@ -28,9 +29,9 @@
#define PCA953X_INVERT 0x02
#define PCA953X_DIRECTION 0x03
-#define REG_ADDR_MASK 0x3f
-#define REG_ADDR_EXT 0x40
-#define REG_ADDR_AI 0x80
+#define REG_ADDR_MASK GENMASK(5, 0)
+#define REG_ADDR_EXT BIT(6)
+#define REG_ADDR_AI BIT(7)
#define PCA957X_IN 0x00
#define PCA957X_INVRT 0x01
@@ -55,23 +56,25 @@
#define PCAL6524_OUT_INDCONF 0x2c
#define PCAL6524_DEBOUNCE 0x2d
-#define PCA_GPIO_MASK 0x00FF
+#define PCA_GPIO_MASK GENMASK(7, 0)
-#define PCAL_GPIO_MASK 0x1f
-#define PCAL_PINCTRL_MASK 0x60
+#define PCAL_GPIO_MASK GENMASK(4, 0)
+#define PCAL_PINCTRL_MASK GENMASK(6, 5)
-#define PCA_INT 0x0100
-#define PCA_PCAL 0x0200
+#define PCA_INT BIT(8)
+#define PCA_PCAL BIT(9)
#define PCA_LATCH_INT (PCA_PCAL | PCA_INT)
-#define PCA953X_TYPE 0x1000
-#define PCA957X_TYPE 0x2000
-#define PCA_TYPE_MASK 0xF000
+#define PCA953X_TYPE BIT(12)
+#define PCA957X_TYPE BIT(13)
+#define PCA_TYPE_MASK GENMASK(15, 12)
#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, },
@@ -88,6 +91,8 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6524", 24 | PCA953X_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, },
{ "max7310", 8 | PCA953X_TYPE, },
@@ -106,6 +111,52 @@ static const struct i2c_device_id pca953x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pca953x_id);
+#ifdef CONFIG_GPIO_PCA953X_IRQ
+
+#include <linux/dmi.h>
+
+static const struct acpi_gpio_params pca953x_irq_gpios = { 0, 0, true };
+
+static const struct acpi_gpio_mapping pca953x_acpi_irq_gpios[] = {
+ { "irq-gpios", &pca953x_irq_gpios, 1, ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER },
+ { }
+};
+
+static int pca953x_acpi_get_irq(struct device *dev)
+{
+ int ret;
+
+ ret = devm_acpi_dev_add_driver_gpios(dev, pca953x_acpi_irq_gpios);
+ if (ret)
+ dev_warn(dev, "can't add GPIO ACPI mapping\n");
+
+ ret = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev), "irq-gpios", 0);
+ if (ret < 0)
+ return ret;
+
+ dev_info(dev, "ACPI interrupt quirk (IRQ %d)\n", ret);
+ return ret;
+}
+
+static const struct dmi_system_id pca953x_dmi_acpi_irq_info[] = {
+ {
+ /*
+ * On Intel Galileo Gen 2 board the IRQ pin of one of
+ * the I²C GPIO expanders, which has GpioInt() resource,
+ * is provided as an absolute number instead of being
+ * relative. Since first controller (gpio-sch.c) and
+ * second (gpio-dwapb.c) are at the fixed bases, we may
+ * safely refer to the number in the global space to get
+ * an IRQ out of it.
+ */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"),
+ },
+ },
+ {}
+};
+#endif
+
static const struct acpi_device_id pca953x_acpi_ids[] = {
{ "INT3491", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ }
@@ -114,6 +165,7 @@ MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids);
#define MAX_BANK 5
#define BANK_SZ 8
+#define MAX_LINE (MAX_BANK * BANK_SZ)
#define NBANK(chip) DIV_ROUND_UP(chip->gpio_chip.ngpio, BANK_SZ)
@@ -145,11 +197,10 @@ struct pca953x_chip {
#ifdef CONFIG_GPIO_PCA953X_IRQ
struct mutex irq_lock;
- u8 irq_mask[MAX_BANK];
- u8 irq_stat[MAX_BANK];
- u8 irq_trig_raise[MAX_BANK];
- u8 irq_trig_fall[MAX_BANK];
- struct irq_chip irq_chip;
+ DECLARE_BITMAP(irq_mask, MAX_LINE);
+ DECLARE_BITMAP(irq_stat, MAX_LINE);
+ DECLARE_BITMAP(irq_trig_raise, MAX_LINE);
+ DECLARE_BITMAP(irq_trig_fall, MAX_LINE);
#endif
atomic_t wakeup_path;
@@ -304,40 +355,47 @@ static const struct regmap_config pca953x_i2c_regmap = {
.writeable_reg = pca953x_writeable_register,
.volatile_reg = pca953x_volatile_register,
+ .disable_locking = true,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = 0x7f,
+};
+
+static const struct regmap_config pca953x_ai_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .read_flag_mask = REG_ADDR_AI,
+ .write_flag_mask = REG_ADDR_AI,
+
+ .readable_reg = pca953x_readable_register,
+ .writeable_reg = pca953x_writeable_register,
+ .volatile_reg = pca953x_volatile_register,
+
+ .disable_locking = true,
.cache_type = REGCACHE_RBTREE,
- /* REVISIT: should be 0x7f but some 24 bit chips use REG_ADDR_AI */
- .max_register = 0xff,
+ .max_register = 0x7f,
};
-static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off,
- bool write, bool addrinc)
+static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off)
{
int bank_shift = pca953x_bank_shift(chip);
int addr = (reg & PCAL_GPIO_MASK) << bank_shift;
int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
u8 regaddr = pinctrl | addr | (off / BANK_SZ);
- /* Single byte read doesn't need AI bit set. */
- if (!addrinc)
- return regaddr;
-
- /* Chips with 24 and more GPIOs always support Auto Increment */
- if (write && NBANK(chip) > 2)
- regaddr |= REG_ADDR_AI;
-
- /* PCA9575 needs address-increment on multi-byte writes */
- if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE)
- regaddr |= REG_ADDR_AI;
-
return regaddr;
}
-static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
+static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
{
- u8 regaddr = pca953x_recalc_addr(chip, reg, 0, true, true);
- int ret;
+ u8 regaddr = pca953x_recalc_addr(chip, reg, 0);
+ u8 value[MAX_BANK];
+ int i, ret;
+
+ for (i = 0; i < NBANK(chip); i++)
+ value[i] = bitmap_get_value8(val, i * BANK_SZ);
- ret = regmap_bulk_write(chip->regmap, regaddr, val, NBANK(chip));
+ ret = regmap_bulk_write(chip->regmap, regaddr, value, NBANK(chip));
if (ret < 0) {
dev_err(&chip->client->dev, "failed writing register\n");
return ret;
@@ -346,25 +404,28 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
return 0;
}
-static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
+static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
{
- u8 regaddr = pca953x_recalc_addr(chip, reg, 0, false, true);
- int ret;
+ u8 regaddr = pca953x_recalc_addr(chip, reg, 0);
+ u8 value[MAX_BANK];
+ int i, ret;
- ret = regmap_bulk_read(chip->regmap, regaddr, val, NBANK(chip));
+ ret = regmap_bulk_read(chip->regmap, regaddr, value, NBANK(chip));
if (ret < 0) {
dev_err(&chip->client->dev, "failed reading register\n");
return ret;
}
+ for (i = 0; i < NBANK(chip); i++)
+ bitmap_set_value8(val, value[i], i * BANK_SZ);
+
return 0;
}
static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
- true, false);
+ u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
u8 bit = BIT(off % BANK_SZ);
int ret;
@@ -378,10 +439,8 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
unsigned off, int val)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
- true, false);
- u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
- true, false);
+ u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
+ u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off);
u8 bit = BIT(off % BANK_SZ);
int ret;
@@ -401,8 +460,7 @@ exit:
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off,
- true, false);
+ u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off);
u8 bit = BIT(off % BANK_SZ);
u32 reg_val;
int ret;
@@ -410,13 +468,8 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
mutex_lock(&chip->i2c_lock);
ret = regmap_read(chip->regmap, inreg, &reg_val);
mutex_unlock(&chip->i2c_lock);
- if (ret < 0) {
- /* NOTE: diagnostic already emitted; that's all we should
- * do unless gpio_*_value_cansleep() calls become different
- * from their nonsleeping siblings (and report faults).
- */
- return 0;
- }
+ if (ret < 0)
+ return ret;
return !!(reg_val & bit);
}
@@ -424,8 +477,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
- true, false);
+ u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off);
u8 bit = BIT(off % BANK_SZ);
mutex_lock(&chip->i2c_lock);
@@ -436,8 +488,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
- true, false);
+ u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
u8 bit = BIT(off % BANK_SZ);
u32 reg_val;
int ret;
@@ -448,16 +499,34 @@ static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
if (ret < 0)
return ret;
- return !!(reg_val & bit);
+ if (reg_val & bit)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int pca953x_gpio_get_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct pca953x_chip *chip = gpiochip_get_data(gc);
+ DECLARE_BITMAP(reg_val, MAX_LINE);
+ int ret;
+
+ mutex_lock(&chip->i2c_lock);
+ ret = pca953x_read_regs(chip, chip->regs->input, reg_val);
+ mutex_unlock(&chip->i2c_lock);
+ if (ret)
+ return ret;
+
+ bitmap_replace(bits, bits, reg_val, mask, gc->ngpio);
+ return 0;
}
static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- unsigned int bank_mask, bank_val;
- int bank;
- u8 reg_val[MAX_BANK];
+ DECLARE_BITMAP(reg_val, MAX_LINE);
int ret;
mutex_lock(&chip->i2c_lock);
@@ -465,16 +534,7 @@ static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
if (ret)
goto exit;
- for (bank = 0; bank < NBANK(chip); bank++) {
- bank_mask = mask[bank / sizeof(*mask)] >>
- ((bank % sizeof(*mask)) * 8);
- if (bank_mask) {
- bank_val = bits[bank / sizeof(*bits)] >>
- ((bank % sizeof(*bits)) * 8);
- bank_val &= bank_mask;
- reg_val[bank] = (reg_val[bank] & ~bank_mask) | bank_val;
- }
- }
+ bitmap_replace(reg_val, reg_val, bits, mask, gc->ngpio);
pca953x_write_regs(chip, chip->regs->output, reg_val);
exit:
@@ -485,10 +545,8 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
unsigned int offset,
unsigned long config)
{
- u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset,
- true, false);
- u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset,
- true, false);
+ u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset);
+ u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset);
u8 bit = BIT(offset % BANK_SZ);
int ret;
@@ -501,21 +559,21 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
mutex_lock(&chip->i2c_lock);
- /* Disable pull-up/pull-down */
- ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0);
- if (ret)
- goto exit;
-
/* Configure pull-up/pull-down */
if (config == PIN_CONFIG_BIAS_PULL_UP)
ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit);
else if (config == PIN_CONFIG_BIAS_PULL_DOWN)
ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0);
+ else
+ ret = 0;
if (ret)
goto exit;
- /* Enable pull-up/pull-down */
- ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit);
+ /* Disable/Enable pull-up/pull-down */
+ if (config == PIN_CONFIG_BIAS_DISABLE)
+ ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0);
+ else
+ ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit);
exit:
mutex_unlock(&chip->i2c_lock);
@@ -527,9 +585,11 @@ static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- switch (config) {
+ switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_DISABLE:
return pca953x_gpio_set_pull_up_down(chip, offset, config);
default:
return -ENOTSUPP;
@@ -547,6 +607,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
gc->get = pca953x_gpio_get_value;
gc->set = pca953x_gpio_set_value;
gc->get_direction = pca953x_gpio_get_direction;
+ gc->get_multiple = pca953x_gpio_get_multiple;
gc->set_multiple = pca953x_gpio_set_multiple;
gc->set_config = pca953x_gpio_set_config;
gc->can_sleep = true;
@@ -564,16 +625,20 @@ static void pca953x_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pca953x_chip *chip = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
- chip->irq_mask[d->hwirq / BANK_SZ] &= ~(1 << (d->hwirq % BANK_SZ));
+ clear_bit(hwirq, chip->irq_mask);
+ gpiochip_disable_irq(gc, hwirq);
}
static void pca953x_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pca953x_chip *chip = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
- chip->irq_mask[d->hwirq / BANK_SZ] |= 1 << (d->hwirq % BANK_SZ);
+ gpiochip_enable_irq(gc, hwirq);
+ set_bit(hwirq, chip->irq_mask);
}
static int pca953x_irq_set_wake(struct irq_data *d, unsigned int on)
@@ -601,37 +666,30 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 new_irqs;
- int level, i;
- u8 invert_irq_mask[MAX_BANK];
- int reg_direction[MAX_BANK];
-
- regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction,
- NBANK(chip));
+ DECLARE_BITMAP(irq_mask, MAX_LINE);
+ DECLARE_BITMAP(reg_direction, MAX_LINE);
+ int level;
if (chip->driver_data & PCA_PCAL) {
/* Enable latch on interrupt-enabled inputs */
pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask);
- for (i = 0; i < NBANK(chip); i++)
- invert_irq_mask[i] = ~chip->irq_mask[i];
+ bitmap_complement(irq_mask, chip->irq_mask, gc->ngpio);
/* Unmask enabled interrupts */
- pca953x_write_regs(chip, PCAL953X_INT_MASK, invert_irq_mask);
+ pca953x_write_regs(chip, PCAL953X_INT_MASK, irq_mask);
}
+ /* Switch direction to input if needed */
+ pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+
+ bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, 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 (i = 0; i < NBANK(chip); i++) {
- new_irqs = chip->irq_trig_fall[i] | chip->irq_trig_raise[i];
- new_irqs &= reg_direction[i];
-
- while (new_irqs) {
- level = __ffs(new_irqs);
- pca953x_gpio_direction_input(&chip->gpio_chip,
- level + (BANK_SZ * i));
- new_irqs &= ~(1 << level);
- }
- }
+ for_each_set_bit(level, irq_mask, gc->ngpio)
+ pca953x_gpio_direction_input(&chip->gpio_chip, level);
mutex_unlock(&chip->irq_lock);
}
@@ -640,8 +698,7 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pca953x_chip *chip = gpiochip_get_data(gc);
- int bank_nb = d->hwirq / BANK_SZ;
- u8 mask = 1 << (d->hwirq % BANK_SZ);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
if (!(type & IRQ_TYPE_EDGE_BOTH)) {
dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
@@ -649,15 +706,8 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- if (type & IRQ_TYPE_EDGE_FALLING)
- chip->irq_trig_fall[bank_nb] |= mask;
- else
- chip->irq_trig_fall[bank_nb] &= ~mask;
-
- if (type & IRQ_TYPE_EDGE_RISING)
- chip->irq_trig_raise[bank_nb] |= mask;
- else
- chip->irq_trig_raise[bank_nb] &= ~mask;
+ assign_bit(hwirq, chip->irq_trig_fall, type & IRQ_TYPE_EDGE_FALLING);
+ assign_bit(hwirq, chip->irq_trig_raise, type & IRQ_TYPE_EDGE_RISING);
return 0;
}
@@ -666,21 +716,41 @@ static void pca953x_irq_shutdown(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 mask = 1 << (d->hwirq % BANK_SZ);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ clear_bit(hwirq, chip->irq_trig_raise);
+ clear_bit(hwirq, chip->irq_trig_fall);
+}
+
+static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
- chip->irq_trig_raise[d->hwirq / BANK_SZ] &= ~mask;
- chip->irq_trig_fall[d->hwirq / BANK_SZ] &= ~mask;
+ seq_printf(p, dev_name(gc->parent));
}
-static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
+static const struct irq_chip pca953x_irq_chip = {
+ .irq_mask = pca953x_irq_mask,
+ .irq_unmask = pca953x_irq_unmask,
+ .irq_set_wake = pca953x_irq_set_wake,
+ .irq_bus_lock = pca953x_irq_bus_lock,
+ .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock,
+ .irq_set_type = pca953x_irq_set_type,
+ .irq_shutdown = pca953x_irq_shutdown,
+ .irq_print_chip = pca953x_irq_print_chip,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pending)
{
- u8 cur_stat[MAX_BANK];
- u8 old_stat[MAX_BANK];
- bool pending_seen = false;
- bool trigger_seen = false;
- u8 trigger[MAX_BANK];
- int reg_direction[MAX_BANK];
- int ret, i;
+ struct gpio_chip *gc = &chip->gpio_chip;
+ DECLARE_BITMAP(reg_direction, MAX_LINE);
+ DECLARE_BITMAP(old_stat, MAX_LINE);
+ DECLARE_BITMAP(cur_stat, MAX_LINE);
+ DECLARE_BITMAP(new_stat, MAX_LINE);
+ DECLARE_BITMAP(trigger, MAX_LINE);
+ int ret;
if (chip->driver_data & PCA_PCAL) {
/* Read the current interrupt status from the device */
@@ -689,20 +759,16 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
return false;
/* Check latched inputs and clear interrupt status */
- ret = pca953x_read_regs(chip, PCA953X_INPUT, cur_stat);
+ ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
if (ret)
return false;
- for (i = 0; i < NBANK(chip); i++) {
- /* Apply filter for rising/falling edge selection */
- pending[i] = (~cur_stat[i] & chip->irq_trig_fall[i]) |
- (cur_stat[i] & chip->irq_trig_raise[i]);
- pending[i] &= trigger[i];
- if (pending[i])
- pending_seen = true;
- }
+ /* Apply filter for rising/falling edge selection */
+ bitmap_replace(new_stat, chip->irq_trig_fall, chip->irq_trig_raise, cur_stat, gc->ngpio);
- return pending_seen;
+ bitmap_and(pending, new_stat, trigger, gc->ngpio);
+
+ return !bitmap_empty(pending, gc->ngpio);
}
ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
@@ -710,66 +776,73 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
return false;
/* Remove output pins from the equation */
- regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction,
- NBANK(chip));
- for (i = 0; i < NBANK(chip); i++)
- cur_stat[i] &= reg_direction[i];
+ pca953x_read_regs(chip, chip->regs->direction, reg_direction);
- memcpy(old_stat, chip->irq_stat, NBANK(chip));
+ bitmap_copy(old_stat, chip->irq_stat, gc->ngpio);
- for (i = 0; i < NBANK(chip); i++) {
- trigger[i] = (cur_stat[i] ^ old_stat[i]) & chip->irq_mask[i];
- if (trigger[i])
- trigger_seen = true;
- }
+ bitmap_and(new_stat, cur_stat, reg_direction, gc->ngpio);
+ bitmap_xor(cur_stat, new_stat, old_stat, gc->ngpio);
+ bitmap_and(trigger, cur_stat, chip->irq_mask, gc->ngpio);
+
+ bitmap_copy(chip->irq_stat, new_stat, gc->ngpio);
- if (!trigger_seen)
+ if (bitmap_empty(trigger, gc->ngpio))
return false;
- memcpy(chip->irq_stat, cur_stat, NBANK(chip));
+ bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio);
+ bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio);
+ bitmap_or(new_stat, old_stat, cur_stat, gc->ngpio);
+ bitmap_and(pending, new_stat, trigger, gc->ngpio);
- for (i = 0; i < NBANK(chip); i++) {
- pending[i] = (old_stat[i] & chip->irq_trig_fall[i]) |
- (cur_stat[i] & chip->irq_trig_raise[i]);
- pending[i] &= trigger[i];
- if (pending[i])
- pending_seen = true;
- }
-
- return pending_seen;
+ return !bitmap_empty(pending, gc->ngpio);
}
static irqreturn_t pca953x_irq_handler(int irq, void *devid)
{
struct pca953x_chip *chip = devid;
- u8 pending[MAX_BANK];
- u8 level;
- unsigned nhandled = 0;
- int i;
-
- if (!pca953x_irq_pending(chip, pending))
- return IRQ_NONE;
-
- for (i = 0; i < NBANK(chip); i++) {
- while (pending[i]) {
- level = __ffs(pending[i]);
- handle_nested_irq(irq_find_mapping(chip->gpio_chip.irq.domain,
- level + (BANK_SZ * i)));
- pending[i] &= ~(1 << level);
- nhandled++;
+ struct gpio_chip *gc = &chip->gpio_chip;
+ DECLARE_BITMAP(pending, MAX_LINE);
+ int level;
+ bool ret;
+
+ bitmap_zero(pending, MAX_LINE);
+
+ mutex_lock(&chip->i2c_lock);
+ ret = pca953x_irq_pending(chip, pending);
+ mutex_unlock(&chip->i2c_lock);
+
+ if (ret) {
+ ret = 0;
+
+ for_each_set_bit(level, pending, gc->ngpio) {
+ int nested_irq = irq_find_mapping(gc->irq.domain, level);
+
+ if (unlikely(nested_irq <= 0)) {
+ dev_warn_ratelimited(gc->parent, "unmapped interrupt %d\n", level);
+ continue;
+ }
+
+ handle_nested_irq(nested_irq);
+ ret = 1;
}
}
- return (nhandled > 0) ? IRQ_HANDLED : IRQ_NONE;
+ return IRQ_RETVAL(ret);
}
-static int pca953x_irq_setup(struct pca953x_chip *chip,
- int irq_base)
+static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base)
{
struct i2c_client *client = chip->client;
- struct irq_chip *irq_chip = &chip->irq_chip;
- int reg_direction[MAX_BANK];
- int ret, i;
+ DECLARE_BITMAP(reg_direction, MAX_LINE);
+ DECLARE_BITMAP(irq_stat, MAX_LINE);
+ struct gpio_irq_chip *girq;
+ int ret;
+
+ if (dmi_first_match(pca953x_dmi_acpi_irq_info)) {
+ ret = pca953x_acpi_get_irq(&client->dev);
+ if (ret > 0)
+ client->irq = ret;
+ }
if (!client->irq)
return 0;
@@ -780,7 +853,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
if (!(chip->driver_data & PCA_INT))
return 0;
- ret = pca953x_read_regs(chip, chip->regs->input, chip->irq_stat);
+ ret = pca953x_read_regs(chip, chip->regs->input, irq_stat);
if (ret)
return ret;
@@ -789,16 +862,24 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
* interrupt. We have to rely on the previous read for
* this purpose.
*/
- regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction,
- NBANK(chip));
- for (i = 0; i < NBANK(chip); i++)
- chip->irq_stat[i] &= reg_direction[i];
+ pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ bitmap_and(chip->irq_stat, irq_stat, reg_direction, chip->gpio_chip.ngpio);
mutex_init(&chip->irq_lock);
+ girq = &chip->gpio_chip.irq;
+ gpio_irq_chip_set_chip(girq, &pca953x_irq_chip);
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->threaded = true;
+ girq->first = irq_base; /* FIXME: get rid of this */
+
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pca953x_irq_handler,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT |
- IRQF_SHARED,
+ IRQF_ONESHOT | IRQF_SHARED,
dev_name(&client->dev), chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
@@ -806,26 +887,6 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
return ret;
}
- irq_chip->name = dev_name(&chip->client->dev);
- irq_chip->irq_mask = pca953x_irq_mask;
- irq_chip->irq_unmask = pca953x_irq_unmask;
- irq_chip->irq_set_wake = pca953x_irq_set_wake;
- irq_chip->irq_bus_lock = pca953x_irq_bus_lock;
- irq_chip->irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock;
- irq_chip->irq_set_type = pca953x_irq_set_type;
- irq_chip->irq_shutdown = pca953x_irq_shutdown;
-
- ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, irq_chip,
- irq_base, handle_simple_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(&client->dev,
- "could not connect irqchip to gpiochip\n");
- return ret;
- }
-
- gpiochip_set_nested_irqchip(&chip->gpio_chip, irq_chip, client->irq);
-
return 0;
}
@@ -844,24 +905,24 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
static int device_pca95xx_init(struct pca953x_chip *chip, u32 invert)
{
+ DECLARE_BITMAP(val, MAX_LINE);
int ret;
- u8 val[MAX_BANK];
ret = regcache_sync_region(chip->regmap, chip->regs->output,
chip->regs->output + NBANK(chip));
- if (ret != 0)
+ if (ret)
goto out;
ret = regcache_sync_region(chip->regmap, chip->regs->direction,
chip->regs->direction + NBANK(chip));
- if (ret != 0)
+ if (ret)
goto out;
/* set platform specific polarity inversion */
if (invert)
- memset(val, 0xFF, NBANK(chip));
+ bitmap_fill(val, MAX_LINE);
else
- memset(val, 0, NBANK(chip));
+ bitmap_zero(val, MAX_LINE);
ret = pca953x_write_regs(chip, chip->regs->invert, val);
out:
@@ -870,15 +931,18 @@ out:
static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
{
+ DECLARE_BITMAP(val, MAX_LINE);
+ unsigned int i;
int ret;
- u8 val[MAX_BANK];
ret = device_pca95xx_init(chip, invert);
if (ret)
goto out;
/* To enable register 6, 7 to control pull up and pull down */
- memset(val, 0x02, NBANK(chip));
+ for (i = 0; i < NBANK(chip); i++)
+ bitmap_set_value8(val, 0x02, i * BANK_SZ);
+
ret = pca953x_write_regs(chip, PCA957X_BKEN, val);
if (ret)
goto out;
@@ -888,10 +952,8 @@ out:
return ret;
}
-static const struct of_device_id pca953x_dt_ids[];
-
static int pca953x_probe(struct i2c_client *client,
- const struct i2c_device_id *i2c_id)
+ const struct i2c_device_id *i2c_id)
{
struct pca953x_platform_data *pdata;
struct pca953x_chip *chip;
@@ -899,9 +961,9 @@ static int pca953x_probe(struct i2c_client *client,
int ret;
u32 invert = 0;
struct regulator *reg;
+ const struct regmap_config *regmap_config;
- chip = devm_kzalloc(&client->dev,
- sizeof(struct pca953x_chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
@@ -933,12 +995,9 @@ static int pca953x_probe(struct i2c_client *client,
chip->client = client;
reg = devm_regulator_get(&client->dev, "vcc");
- if (IS_ERR(reg)) {
- ret = PTR_ERR(reg);
- if (ret != -EPROBE_DEFER)
- dev_err(&client->dev, "reg get err: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(reg))
+ return dev_err_probe(&client->dev, PTR_ERR(reg), "reg get err\n");
+
ret = regulator_enable(reg);
if (ret) {
dev_err(&client->dev, "reg en err: %d\n", ret);
@@ -949,24 +1008,30 @@ static int pca953x_probe(struct i2c_client *client,
if (i2c_id) {
chip->driver_data = i2c_id->driver_data;
} else {
- const struct acpi_device_id *acpi_id;
- struct device *dev = &client->dev;
-
- chip->driver_data = (uintptr_t)of_device_get_match_data(dev);
- if (!chip->driver_data) {
- acpi_id = acpi_match_device(pca953x_acpi_ids, dev);
- if (!acpi_id) {
- ret = -ENODEV;
- goto err_exit;
- }
+ const void *match;
- chip->driver_data = acpi_id->driver_data;
+ match = device_get_match_data(&client->dev);
+ if (!match) {
+ ret = -ENODEV;
+ goto err_exit;
}
+
+ chip->driver_data = (uintptr_t)match;
}
i2c_set_clientdata(client, chip);
- chip->regmap = devm_regmap_init_i2c(client, &pca953x_i2c_regmap);
+ pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
+
+ if (NBANK(chip) > 2 || PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
+ dev_info(&client->dev, "using AI\n");
+ regmap_config = &pca953x_ai_i2c_regmap;
+ } else {
+ dev_info(&client->dev, "using no AI\n");
+ regmap_config = &pca953x_i2c_regmap;
+ }
+
+ chip->regmap = devm_regmap_init_i2c(client, regmap_config);
if (IS_ERR(chip->regmap)) {
ret = PTR_ERR(chip->regmap);
goto err_exit;
@@ -997,7 +1062,6 @@ static int pca953x_probe(struct i2c_client *client,
/* initialize cached registers from their original values.
* we can't share this chip with another i2c master.
*/
- pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
chip->regs = &pca953x_regs;
@@ -1009,17 +1073,17 @@ static int pca953x_probe(struct i2c_client *client,
if (ret)
goto err_exit;
- ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
+ ret = pca953x_irq_setup(chip, irq_base);
if (ret)
goto err_exit;
- ret = pca953x_irq_setup(chip, irq_base);
+ ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
if (ret)
goto err_exit;
if (pdata && pdata->setup) {
ret = pdata->setup(client, chip->gpio_chip.base,
- chip->gpio_chip.ngpio, pdata->context);
+ chip->gpio_chip.ngpio, pdata->context);
if (ret < 0)
dev_warn(&client->dev, "setup failed, %d\n", ret);
}
@@ -1039,10 +1103,9 @@ static int pca953x_remove(struct i2c_client *client)
if (pdata && pdata->teardown) {
ret = pdata->teardown(client, chip->gpio_chip.base,
- chip->gpio_chip.ngpio, pdata->context);
+ chip->gpio_chip.ngpio, pdata->context);
if (ret < 0)
- dev_err(&client->dev, "%s failed, %d\n",
- "teardown", ret);
+ dev_err(&client->dev, "teardown failed, %d\n", ret);
} else {
ret = 0;
}
@@ -1057,38 +1120,41 @@ static int pca953x_regcache_sync(struct device *dev)
{
struct pca953x_chip *chip = dev_get_drvdata(dev);
int ret;
+ u8 regaddr;
/*
* The ordering between direction and output is important,
* sync these registers first and only then sync the rest.
*/
- ret = regcache_sync_region(chip->regmap, chip->regs->direction,
- chip->regs->direction + NBANK(chip));
- if (ret != 0) {
+ regaddr = pca953x_recalc_addr(chip, chip->regs->direction, 0);
+ ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip));
+ if (ret) {
dev_err(dev, "Failed to sync GPIO dir registers: %d\n", ret);
return ret;
}
- ret = regcache_sync_region(chip->regmap, chip->regs->output,
- chip->regs->output + NBANK(chip));
- if (ret != 0) {
+ regaddr = pca953x_recalc_addr(chip, chip->regs->output, 0);
+ ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip));
+ if (ret) {
dev_err(dev, "Failed to sync GPIO out registers: %d\n", ret);
return ret;
}
#ifdef CONFIG_GPIO_PCA953X_IRQ
if (chip->driver_data & PCA_PCAL) {
- ret = regcache_sync_region(chip->regmap, PCAL953X_IN_LATCH,
- PCAL953X_IN_LATCH + NBANK(chip));
- if (ret != 0) {
+ regaddr = pca953x_recalc_addr(chip, PCAL953X_IN_LATCH, 0);
+ ret = regcache_sync_region(chip->regmap, regaddr,
+ regaddr + NBANK(chip));
+ if (ret) {
dev_err(dev, "Failed to sync INT latch registers: %d\n",
ret);
return ret;
}
- ret = regcache_sync_region(chip->regmap, PCAL953X_INT_MASK,
- PCAL953X_INT_MASK + NBANK(chip));
- if (ret != 0) {
+ regaddr = pca953x_recalc_addr(chip, PCAL953X_INT_MASK, 0);
+ ret = regcache_sync_region(chip->regmap, regaddr,
+ regaddr + NBANK(chip));
+ if (ret) {
dev_err(dev, "Failed to sync INT mask registers: %d\n",
ret);
return ret;
@@ -1120,7 +1186,7 @@ static int pca953x_resume(struct device *dev)
if (!atomic_read(&chip->wakeup_path)) {
ret = regulator_enable(chip->regulator);
- if (ret != 0) {
+ if (ret) {
dev_err(dev, "Failed to enable regulator: %d\n", ret);
return 0;
}
@@ -1133,7 +1199,7 @@ static int pca953x_resume(struct device *dev)
return ret;
ret = regcache_sync(chip->regmap);
- if (ret != 0) {
+ if (ret) {
dev_err(dev, "Failed to restore register map: %d\n", ret);
return ret;
}
@@ -1147,8 +1213,10 @@ static int pca953x_resume(struct device *dev)
#define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_TYPE | __int)
static const struct of_device_id pca953x_dt_ids[] = {
+ { .compatible = "nxp,pca6408", .data = OF_953X(8, PCA_INT), },
{ .compatible = "nxp,pca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), },
+ { .compatible = "nxp,pca9506", .data = OF_953X(40, PCA_INT), },
{ .compatible = "nxp,pca9534", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), },
{ .compatible = "nxp,pca9536", .data = OF_953X( 4, 0), },
@@ -1165,6 +1233,8 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal9554b", .data = OF_953X( 8, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), },
{ .compatible = "maxim,max7310", .data = OF_953X( 8, 0), },
@@ -1182,6 +1252,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "onnn,pca9654", .data = OF_953X( 8, PCA_INT), },
+ { .compatible = "onnn,pca9655", .data = OF_953X(16, PCA_INT), },
{ .compatible = "exar,xra1202", .data = OF_953X( 8, 0), },
{ }
@@ -1196,7 +1267,7 @@ static struct i2c_driver pca953x_driver = {
.name = "pca953x",
.pm = &pca953x_pm_ops,
.of_match_table = pca953x_dt_ids,
- .acpi_match_table = ACPI_PTR(pca953x_acpi_ids),
+ .acpi_match_table = pca953x_acpi_ids,
},
.probe = pca953x_probe,
.remove = pca953x_remove,
diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c
new file mode 100644
index 000000000000..cb2b2f735c15
--- /dev/null
+++ b/drivers/gpio/gpio-pca9570.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for PCA9570 I2C GPO expander
+ *
+ * Copyright (C) 2020 Sungbo Eo <mans0n@gorani.run>
+ *
+ * Based on gpio-tpic2810.c
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+
+/**
+ * struct pca9570 - GPIO driver data
+ * @chip: GPIO controller chip
+ * @lock: Protects write sequences
+ * @out: Buffer for device register
+ */
+struct pca9570 {
+ struct gpio_chip chip;
+ struct mutex lock;
+ u8 out;
+};
+
+static int pca9570_read(struct pca9570 *gpio, u8 *value)
+{
+ struct i2c_client *client = to_i2c_client(gpio->chip.parent);
+ int ret;
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0)
+ return ret;
+
+ *value = ret;
+ return 0;
+}
+
+static int pca9570_write(struct pca9570 *gpio, u8 value)
+{
+ struct i2c_client *client = to_i2c_client(gpio->chip.parent);
+
+ return i2c_smbus_write_byte(client, value);
+}
+
+static int pca9570_get_direction(struct gpio_chip *chip,
+ unsigned offset)
+{
+ /* This device always output */
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int pca9570_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct pca9570 *gpio = gpiochip_get_data(chip);
+ u8 buffer;
+ int ret;
+
+ ret = pca9570_read(gpio, &buffer);
+ if (ret)
+ return ret;
+
+ return !!(buffer & BIT(offset));
+}
+
+static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct pca9570 *gpio = gpiochip_get_data(chip);
+ u8 buffer;
+ int ret;
+
+ mutex_lock(&gpio->lock);
+
+ buffer = gpio->out;
+ if (value)
+ buffer |= BIT(offset);
+ else
+ buffer &= ~BIT(offset);
+
+ ret = pca9570_write(gpio, buffer);
+ if (ret)
+ goto out;
+
+ gpio->out = buffer;
+
+out:
+ mutex_unlock(&gpio->lock);
+}
+
+static int pca9570_probe(struct i2c_client *client)
+{
+ struct pca9570 *gpio;
+
+ gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->chip.label = client->name;
+ gpio->chip.parent = &client->dev;
+ gpio->chip.owner = THIS_MODULE;
+ gpio->chip.get_direction = pca9570_get_direction;
+ gpio->chip.get = pca9570_get;
+ gpio->chip.set = pca9570_set;
+ gpio->chip.base = -1;
+ gpio->chip.ngpio = (uintptr_t)device_get_match_data(&client->dev);
+ gpio->chip.can_sleep = true;
+
+ mutex_init(&gpio->lock);
+
+ /* Read the current output level */
+ pca9570_read(gpio, &gpio->out);
+
+ i2c_set_clientdata(client, gpio);
+
+ return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio);
+}
+
+static const struct i2c_device_id pca9570_id_table[] = {
+ { "pca9570", 4 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, pca9570_id_table);
+
+static const struct of_device_id pca9570_of_match_table[] = {
+ { .compatible = "nxp,pca9570", .data = (void *)4 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pca9570_of_match_table);
+
+static struct i2c_driver pca9570_driver = {
+ .driver = {
+ .name = "pca9570",
+ .of_match_table = pca9570_of_match_table,
+ },
+ .probe_new = pca9570_probe,
+ .id_table = pca9570_id_table,
+};
+module_i2c_driver(pca9570_driver);
+
+MODULE_AUTHOR("Sungbo Eo <mans0n@gorani.run>");
+MODULE_DESCRIPTION("GPIO expander driver for PCA9570");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 14fb8f6a1ad2..59cc27e4de51 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -71,7 +71,6 @@ MODULE_DEVICE_TABLE(of, pcf857x_of_table);
*/
struct pcf857x {
struct gpio_chip chip;
- struct irq_chip irqchip;
struct i2c_client *client;
struct mutex lock; /* protect 'out' */
unsigned out; /* software latch */
@@ -203,15 +202,19 @@ static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on)
static void pcf857x_irq_enable(struct irq_data *data)
{
struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
- gpio->irq_enabled |= (1 << data->hwirq);
+ gpiochip_enable_irq(&gpio->chip, hwirq);
+ gpio->irq_enabled |= (1 << hwirq);
}
static void pcf857x_irq_disable(struct irq_data *data)
{
struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
- gpio->irq_enabled &= ~(1 << data->hwirq);
+ gpio->irq_enabled &= ~(1 << hwirq);
+ gpiochip_disable_irq(&gpio->chip, hwirq);
}
static void pcf857x_irq_bus_lock(struct irq_data *data)
@@ -228,6 +231,20 @@ static void pcf857x_irq_bus_sync_unlock(struct irq_data *data)
mutex_unlock(&gpio->lock);
}
+static const struct irq_chip pcf857x_irq_chip = {
+ .name = "pcf857x",
+ .irq_enable = pcf857x_irq_enable,
+ .irq_disable = pcf857x_irq_disable,
+ .irq_ack = noop,
+ .irq_mask = noop,
+ .irq_unmask = noop,
+ .irq_set_wake = pcf857x_irq_set_wake,
+ .irq_bus_lock = pcf857x_irq_bus_lock,
+ .irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
/*-------------------------------------------------------------------------*/
static int pcf857x_probe(struct i2c_client *client,
@@ -332,31 +349,11 @@ static int pcf857x_probe(struct i2c_client *client,
* reset state. Otherwise it flags pins to be driven low.
*/
gpio->out = ~n_latch;
- gpio->status = gpio->out;
-
- status = devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio);
- if (status < 0)
- goto fail;
+ gpio->status = gpio->read(gpio->client);
/* Enable irqchip if we have an interrupt */
if (client->irq) {
- gpio->irqchip.name = "pcf857x",
- gpio->irqchip.irq_enable = pcf857x_irq_enable,
- gpio->irqchip.irq_disable = pcf857x_irq_disable,
- gpio->irqchip.irq_ack = noop,
- gpio->irqchip.irq_mask = noop,
- gpio->irqchip.irq_unmask = noop,
- gpio->irqchip.irq_set_wake = pcf857x_irq_set_wake,
- gpio->irqchip.irq_bus_lock = pcf857x_irq_bus_lock,
- gpio->irqchip.irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock,
- status = gpiochip_irqchip_add_nested(&gpio->chip,
- &gpio->irqchip,
- 0, handle_level_irq,
- IRQ_TYPE_NONE);
- if (status) {
- dev_err(&client->dev, "cannot add irqchip\n");
- goto fail;
- }
+ struct gpio_irq_chip *girq;
status = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf857x_irq, IRQF_ONESHOT |
@@ -365,10 +362,21 @@ static int pcf857x_probe(struct i2c_client *client,
if (status)
goto fail;
- gpiochip_set_nested_irqchip(&gpio->chip, &gpio->irqchip,
- client->irq);
+ girq = &gpio->chip.irq;
+ gpio_irq_chip_set_chip(girq, &pcf857x_irq_chip);
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
+ girq->threaded = true;
}
+ status = devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio);
+ if (status < 0)
+ goto fail;
+
/* Let platform code set up the GPIOs and their users.
* Now is the first time anyone could use them.
*/
@@ -395,20 +403,12 @@ static int pcf857x_remove(struct i2c_client *client)
{
struct pcf857x_platform_data *pdata = dev_get_platdata(&client->dev);
struct pcf857x *gpio = i2c_get_clientdata(client);
- int status = 0;
- if (pdata && pdata->teardown) {
- status = pdata->teardown(client,
- gpio->chip.base, gpio->chip.ngpio,
+ if (pdata && pdata->teardown)
+ pdata->teardown(client, gpio->chip.base, gpio->chip.ngpio,
pdata->context);
- if (status < 0) {
- dev_err(&client->dev, "%s --> %d\n",
- "teardown", status);
- return status;
- }
- }
- return status;
+ return 0;
}
static void pcf857x_shutdown(struct i2c_client *client)
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index 1d99293096f2..3a0bd8795741 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*/
+#include <linux/bits.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -11,11 +12,11 @@
#include <linux/slab.h>
#define PCH_EDGE_FALLING 0
-#define PCH_EDGE_RISING BIT(0)
-#define PCH_LEVEL_L BIT(1)
-#define PCH_LEVEL_H (BIT(0) | BIT(1))
-#define PCH_EDGE_BOTH BIT(2)
-#define PCH_IM_MASK (BIT(0) | BIT(1) | BIT(2))
+#define PCH_EDGE_RISING 1
+#define PCH_LEVEL_L 2
+#define PCH_LEVEL_H 3
+#define PCH_EDGE_BOTH 4
+#define PCH_IM_MASK GENMASK(2, 0)
#define PCH_IRQ_BASE 24
@@ -94,7 +95,7 @@ struct pch_gpio {
spinlock_t spinlock;
};
-static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
+static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)
{
u32 reg_val;
struct pch_gpio *chip = gpiochip_get_data(gpio);
@@ -103,22 +104,22 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
spin_lock_irqsave(&chip->spinlock, flags);
reg_val = ioread32(&chip->reg->po);
if (val)
- reg_val |= (1 << nr);
+ reg_val |= BIT(nr);
else
- reg_val &= ~(1 << nr);
+ reg_val &= ~BIT(nr);
iowrite32(reg_val, &chip->reg->po);
spin_unlock_irqrestore(&chip->spinlock, flags);
}
-static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
+static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr)
{
struct pch_gpio *chip = gpiochip_get_data(gpio);
- return (ioread32(&chip->reg->pi) >> nr) & 1;
+ return !!(ioread32(&chip->reg->pi) & BIT(nr));
}
-static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr,
int val)
{
struct pch_gpio *chip = gpiochip_get_data(gpio);
@@ -130,13 +131,14 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
reg_val = ioread32(&chip->reg->po);
if (val)
- reg_val |= (1 << nr);
+ reg_val |= BIT(nr);
else
- reg_val &= ~(1 << nr);
+ reg_val &= ~BIT(nr);
iowrite32(reg_val, &chip->reg->po);
- pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
- pm |= (1 << nr);
+ pm = ioread32(&chip->reg->pm);
+ pm &= BIT(gpio_pins[chip->ioh]) - 1;
+ pm |= BIT(nr);
iowrite32(pm, &chip->reg->pm);
spin_unlock_irqrestore(&chip->spinlock, flags);
@@ -144,15 +146,16 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
return 0;
}
-static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr)
{
struct pch_gpio *chip = gpiochip_get_data(gpio);
u32 pm;
unsigned long flags;
spin_lock_irqsave(&chip->spinlock, flags);
- pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
- pm &= ~(1 << nr);
+ pm = ioread32(&chip->reg->pm);
+ pm &= BIT(gpio_pins[chip->ioh]) - 1;
+ pm &= ~BIT(nr);
iowrite32(pm, &chip->reg->pm);
spin_unlock_irqrestore(&chip->spinlock, flags);
@@ -193,9 +196,10 @@ static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip)
iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, &chip->reg->gpio_use_sel);
}
-static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned int offset)
{
struct pch_gpio *chip = gpiochip_get_data(gpio);
+
return chip->irq_base + offset;
}
@@ -226,17 +230,15 @@ static int pch_irq_type(struct irq_data *d, unsigned int type)
int ch, irq = d->irq;
ch = irq - chip->irq_base;
- if (irq <= chip->irq_base + 7) {
+ if (irq < chip->irq_base + 8) {
im_reg = &chip->reg->im0;
- im_pos = ch;
+ im_pos = ch - 0;
} else {
im_reg = &chip->reg->im1;
im_pos = ch - 8;
}
dev_dbg(chip->dev, "irq=%d type=%d ch=%d pos=%d\n", irq, type, ch, im_pos);
- spin_lock_irqsave(&chip->spinlock, flags);
-
switch (type) {
case IRQ_TYPE_EDGE_RISING:
val = PCH_EDGE_RISING;
@@ -254,20 +256,21 @@ static int pch_irq_type(struct irq_data *d, unsigned int type)
val = PCH_LEVEL_L;
break;
default:
- goto unlock;
+ return 0;
}
+ spin_lock_irqsave(&chip->spinlock, flags);
+
/* Set interrupt mode */
im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4));
iowrite32(im | (val << (im_pos * 4)), im_reg);
/* And the handler */
- if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
- else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ else if (type & IRQ_TYPE_EDGE_BOTH)
irq_set_handler_locked(d, handle_edge_irq);
-unlock:
spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
@@ -277,7 +280,7 @@ static void pch_irq_unmask(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct pch_gpio *chip = gc->private;
- iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imaskclr);
+ iowrite32(BIT(d->irq - chip->irq_base), &chip->reg->imaskclr);
}
static void pch_irq_mask(struct irq_data *d)
@@ -285,7 +288,7 @@ static void pch_irq_mask(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct pch_gpio *chip = gc->private;
- iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imask);
+ iowrite32(BIT(d->irq - chip->irq_base), &chip->reg->imask);
}
static void pch_irq_ack(struct irq_data *d)
@@ -293,21 +296,23 @@ static void pch_irq_ack(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct pch_gpio *chip = gc->private;
- iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->iclr);
+ iowrite32(BIT(d->irq - chip->irq_base), &chip->reg->iclr);
}
static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
{
struct pch_gpio *chip = dev_id;
unsigned long reg_val = ioread32(&chip->reg->istatus);
- int i, ret = IRQ_NONE;
+ int i;
- for_each_set_bit(i, &reg_val, gpio_pins[chip->ioh]) {
- dev_dbg(chip->dev, "[%d]:irq=%d status=0x%lx\n", i, irq, reg_val);
+ dev_vdbg(chip->dev, "irq=%d status=0x%lx\n", irq, reg_val);
+
+ reg_val &= BIT(gpio_pins[chip->ioh]) - 1;
+
+ for_each_set_bit(i, &reg_val, gpio_pins[chip->ioh])
generic_handle_irq(chip->irq_base + i);
- ret = IRQ_HANDLED;
- }
- return ret;
+
+ return IRQ_RETVAL(reg_val);
}
static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
@@ -341,66 +346,58 @@ static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
static int pch_gpio_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
+ struct device *dev = &pdev->dev;
s32 ret;
struct pch_gpio *chip;
int irq_base;
- u32 msk;
- chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
- chip->dev = &pdev->dev;
+ chip->dev = dev;
ret = pcim_enable_device(pdev);
if (ret) {
- dev_err(&pdev->dev, "pci_enable_device FAILED");
+ dev_err(dev, "pci_enable_device FAILED");
return ret;
}
- ret = pcim_iomap_regions(pdev, 1 << 1, KBUILD_MODNAME);
+ ret = pcim_iomap_regions(pdev, BIT(1), KBUILD_MODNAME);
if (ret) {
- dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret);
+ dev_err(dev, "pci_request_regions FAILED-%d", ret);
return ret;
}
chip->base = pcim_iomap_table(pdev)[1];
-
- if (pdev->device == 0x8803)
- chip->ioh = INTEL_EG20T_PCH;
- else if (pdev->device == 0x8014)
- chip->ioh = OKISEMI_ML7223m_IOH;
- else if (pdev->device == 0x8043)
- chip->ioh = OKISEMI_ML7223n_IOH;
-
+ chip->ioh = id->driver_data;
chip->reg = chip->base;
pci_set_drvdata(pdev, chip);
spin_lock_init(&chip->spinlock);
pch_gpio_setup(chip);
- ret = devm_gpiochip_add_data(&pdev->dev, &chip->gpio, chip);
+ ret = devm_gpiochip_add_data(dev, &chip->gpio, chip);
if (ret) {
- dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n");
+ dev_err(dev, "PCH gpio: Failed to register GPIO\n");
return ret;
}
- irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0,
+ irq_base = devm_irq_alloc_descs(dev, -1, 0,
gpio_pins[chip->ioh], NUMA_NO_NODE);
if (irq_base < 0) {
- dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n");
+ dev_warn(dev, "PCH gpio: Failed to get IRQ base num\n");
chip->irq_base = -1;
return 0;
}
chip->irq_base = irq_base;
/* Mask all interrupts, but enable them */
- msk = (1 << gpio_pins[chip->ioh]) - 1;
- iowrite32(msk, &chip->reg->imask);
- iowrite32(msk, &chip->reg->ien);
+ iowrite32(BIT(gpio_pins[chip->ioh]) - 1, &chip->reg->imask);
+ iowrite32(BIT(gpio_pins[chip->ioh]) - 1, &chip->reg->ien);
- ret = devm_request_irq(&pdev->dev, pdev->irq, pch_gpio_handler,
+ ret = devm_request_irq(dev, pdev->irq, pch_gpio_handler,
IRQF_SHARED, KBUILD_MODNAME, chip);
if (ret) {
- dev_err(&pdev->dev, "request_irq failed\n");
+ dev_err(dev, "request_irq failed\n");
return ret;
}
@@ -409,8 +406,7 @@ static int pch_gpio_probe(struct pci_dev *pdev,
static int __maybe_unused pch_gpio_suspend(struct device *dev)
{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct pch_gpio *chip = pci_get_drvdata(pdev);
+ struct pch_gpio *chip = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&chip->spinlock, flags);
@@ -422,8 +418,7 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev)
static int __maybe_unused pch_gpio_resume(struct device *dev)
{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct pch_gpio *chip = pci_get_drvdata(pdev);
+ struct pch_gpio *chip = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&chip->spinlock, flags);
@@ -438,10 +433,14 @@ static int __maybe_unused pch_gpio_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
static const struct pci_device_id pch_gpio_pcidev_id[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) },
- { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) },
- { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043) },
- { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8803) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803),
+ .driver_data = INTEL_EG20T_PCH },
+ { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014),
+ .driver_data = OKISEMI_ML7223m_IOH },
+ { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043),
+ .driver_data = OKISEMI_ML7223n_IOH },
+ { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8803),
+ .driver_data = INTEL_EG20T_PCH },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id);
diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c
index 5aa136a6a3e0..71a13a394050 100644
--- a/drivers/gpio/gpio-pci-idio-16.c
+++ b/drivers/gpio/gpio-pci-idio-16.c
@@ -61,9 +61,9 @@ static int idio_16_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
if (offset > 15)
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
}
static int idio_16_gpio_direction_input(struct gpio_chip *chip,
@@ -100,45 +100,23 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- size_t i;
- const unsigned int gpio_reg_size = 8;
- unsigned int bits_offset;
- size_t word_index;
- unsigned int word_offset;
- unsigned long word_mask;
- const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
- unsigned long port_state;
+ unsigned long offset;
+ unsigned long gpio_mask;
void __iomem *ports[] = {
&idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
&idio16gpio->reg->in0_7, &idio16gpio->reg->in8_15,
};
+ void __iomem *port_addr;
+ unsigned long port_state;
/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);
- /* get bits are evaluated a gpio port register at a time */
- for (i = 0; i < ARRAY_SIZE(ports); i++) {
- /* gpio offset in bits array */
- bits_offset = i * gpio_reg_size;
-
- /* word index for bits array */
- word_index = BIT_WORD(bits_offset);
-
- /* gpio offset within current word of bits array */
- word_offset = bits_offset % BITS_PER_LONG;
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ port_addr = ports[offset / 8];
+ port_state = ioread8(port_addr) & gpio_mask;
- /* mask of get bits for current gpio within current word */
- word_mask = mask[word_index] & (port_mask << word_offset);
- if (!word_mask) {
- /* no get bits in this port so skip to next one */
- continue;
- }
-
- /* read bits from current gpio port */
- port_state = ioread8(ports[i]);
-
- /* store acquired bits at respective bits array offset */
- bits[word_index] |= (port_state << word_offset) & word_mask;
+ bitmap_set_value8(bits, port_state, offset);
}
return 0;
@@ -178,30 +156,31 @@ static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
+ unsigned long offset;
+ unsigned long gpio_mask;
+ void __iomem *ports[] = {
+ &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
+ };
+ size_t index;
+ void __iomem *port_addr;
+ unsigned long bitmask;
unsigned long flags;
- unsigned int out_state;
+ unsigned long out_state;
- raw_spin_lock_irqsave(&idio16gpio->lock, flags);
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ index = offset / 8;
+ port_addr = ports[index];
- /* process output lines 0-7 */
- if (*mask & 0xFF) {
- out_state = ioread8(&idio16gpio->reg->out0_7) & ~*mask;
- out_state |= *mask & *bits;
- iowrite8(out_state, &idio16gpio->reg->out0_7);
- }
+ bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
- /* shift to next output line word */
- *mask >>= 8;
+ raw_spin_lock_irqsave(&idio16gpio->lock, flags);
- /* process output lines 8-15 */
- if (*mask & 0xFF) {
- *bits >>= 8;
- out_state = ioread8(&idio16gpio->reg->out8_15) & ~*mask;
- out_state |= *mask & *bits;
- iowrite8(out_state, &idio16gpio->reg->out8_15);
- }
+ out_state = ioread8(port_addr) & ~gpio_mask;
+ out_state |= bitmask;
+ iowrite8(out_state, port_addr);
- raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
+ }
}
static void idio_16_irq_ack(struct irq_data *data)
@@ -281,7 +260,7 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
- generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio));
+ generic_handle_domain_irq(chip->irq.domain, gpio);
raw_spin_lock(&idio16gpio->lock);
@@ -301,6 +280,17 @@ static const char *idio_16_names[IDIO_16_NGPIO] = {
"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
};
+static int idio_16_irq_init_hw(struct gpio_chip *gc)
+{
+ struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc);
+
+ /* Disable IRQ by default and clear any pending interrupt */
+ iowrite8(0, &idio16gpio->reg->irq_ctl);
+ iowrite8(0, &idio16gpio->reg->in0_7);
+
+ return 0;
+}
+
static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *const dev = &pdev->dev;
@@ -308,6 +298,7 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
int err;
const size_t pci_bar_index = 2;
const char *const name = pci_name(pdev);
+ struct gpio_irq_chip *girq;
idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
if (!idio16gpio)
@@ -344,6 +335,16 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
+ girq = &idio16gpio->chip.irq;
+ girq->chip = &idio_16_irqchip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_edge_irq;
+ girq->init_hw = idio_16_irq_init_hw;
+
raw_spin_lock_init(&idio16gpio->lock);
err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
@@ -352,17 +353,6 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err;
}
- /* Disable IRQ by default and clear any pending interrupt */
- iowrite8(0, &idio16gpio->reg->irq_ctl);
- iowrite8(0, &idio16gpio->reg->in0_7);
-
- err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
- handle_edge_irq, IRQ_TYPE_NONE);
- if (err) {
- dev_err(dev, "Could not add irqchip (%d)\n", err);
- return err;
- }
-
err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED,
name, idio16gpio);
if (err) {
diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c
index 52f1647a46fd..8a9b98fa418f 100644
--- a/drivers/gpio/gpio-pcie-idio-24.c
+++ b/drivers/gpio/gpio-pcie-idio-24.c
@@ -28,6 +28,47 @@
#include <linux/spinlock.h>
#include <linux/types.h>
+/*
+ * PLX PEX8311 PCI LCS_INTCSR Interrupt Control/Status
+ *
+ * Bit: Description
+ * 0: Enable Interrupt Sources (Bit 0)
+ * 1: Enable Interrupt Sources (Bit 1)
+ * 2: Generate Internal PCI Bus Internal SERR# Interrupt
+ * 3: Mailbox Interrupt Enable
+ * 4: Power Management Interrupt Enable
+ * 5: Power Management Interrupt
+ * 6: Slave Read Local Data Parity Check Error Enable
+ * 7: Slave Read Local Data Parity Check Error Status
+ * 8: Internal PCI Wire Interrupt Enable
+ * 9: PCI Express Doorbell Interrupt Enable
+ * 10: PCI Abort Interrupt Enable
+ * 11: Local Interrupt Input Enable
+ * 12: Retry Abort Enable
+ * 13: PCI Express Doorbell Interrupt Active
+ * 14: PCI Abort Interrupt Active
+ * 15: Local Interrupt Input Active
+ * 16: Local Interrupt Output Enable
+ * 17: Local Doorbell Interrupt Enable
+ * 18: DMA Channel 0 Interrupt Enable
+ * 19: DMA Channel 1 Interrupt Enable
+ * 20: Local Doorbell Interrupt Active
+ * 21: DMA Channel 0 Interrupt Active
+ * 22: DMA Channel 1 Interrupt Active
+ * 23: Built-In Self-Test (BIST) Interrupt Active
+ * 24: Direct Master was the Bus Master during a Master or Target Abort
+ * 25: DMA Channel 0 was the Bus Master during a Master or Target Abort
+ * 26: DMA Channel 1 was the Bus Master during a Master or Target Abort
+ * 27: Target Abort after internal 256 consecutive Master Retrys
+ * 28: PCI Bus wrote data to LCS_MBOX0
+ * 29: PCI Bus wrote data to LCS_MBOX1
+ * 30: PCI Bus wrote data to LCS_MBOX2
+ * 31: PCI Bus wrote data to LCS_MBOX3
+ */
+#define PLX_PEX8311_PCI_LCS_INTCSR 0x68
+#define INTCSR_INTERNAL_PCI_WIRE BIT(8)
+#define INTCSR_LOCAL_INPUT BIT(11)
+
/**
* struct idio_24_gpio_reg - GPIO device registers structure
* @out0_7: Read: FET Outputs 0-7
@@ -92,6 +133,7 @@ struct idio_24_gpio_reg {
struct idio_24_gpio {
struct gpio_chip chip;
raw_spinlock_t lock;
+ __u8 __iomem *plx;
struct idio_24_gpio_reg __iomem *reg;
unsigned long irq_mask;
};
@@ -104,15 +146,18 @@ static int idio_24_gpio_get_direction(struct gpio_chip *chip,
/* FET Outputs */
if (offset < 24)
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
/* Isolated Inputs */
if (offset < 48)
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
/* TTL/CMOS I/O */
/* OUT MODE = 1 when TTL/CMOS Output Mode is set */
- return !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask);
+ if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int idio_24_gpio_direction_input(struct gpio_chip *chip,
@@ -198,52 +243,34 @@ static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- size_t i;
- const unsigned int gpio_reg_size = 8;
- unsigned int bits_offset;
- size_t word_index;
- unsigned int word_offset;
- unsigned long word_mask;
- const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
- unsigned long port_state;
+ unsigned long offset;
+ unsigned long gpio_mask;
void __iomem *ports[] = {
&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
&idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7,
&idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23,
};
+ size_t index;
+ unsigned long port_state;
const unsigned long out_mode_mask = BIT(1);
/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);
- /* get bits are evaluated a gpio port register at a time */
- for (i = 0; i < ARRAY_SIZE(ports) + 1; i++) {
- /* gpio offset in bits array */
- bits_offset = i * gpio_reg_size;
-
- /* word index for bits array */
- word_index = BIT_WORD(bits_offset);
-
- /* gpio offset within current word of bits array */
- word_offset = bits_offset % BITS_PER_LONG;
-
- /* mask of get bits for current gpio within current word */
- word_mask = mask[word_index] & (port_mask << word_offset);
- if (!word_mask) {
- /* no get bits in this port so skip to next one */
- continue;
- }
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ index = offset / 8;
/* read bits from current gpio port (port 6 is TTL GPIO) */
- if (i < 6)
- port_state = ioread8(ports[i]);
+ if (index < 6)
+ port_state = ioread8(ports[index]);
else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
else
port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
- /* store acquired bits at respective bits array offset */
- bits[word_index] |= (port_state << word_offset) & word_mask;
+ port_state &= gpio_mask;
+
+ bitmap_set_value8(bits, port_state, offset);
}
return 0;
@@ -294,59 +321,48 @@ static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- size_t i;
- unsigned long bits_offset;
+ unsigned long offset;
unsigned long gpio_mask;
- const unsigned int gpio_reg_size = 8;
- const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
- unsigned long flags;
- unsigned int out_state;
void __iomem *ports[] = {
&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
&idio24gpio->reg->out16_23
};
+ size_t index;
+ unsigned long bitmask;
+ unsigned long flags;
+ unsigned long out_state;
const unsigned long out_mode_mask = BIT(1);
- const unsigned int ttl_offset = 48;
- const size_t ttl_i = BIT_WORD(ttl_offset);
- const unsigned int word_offset = ttl_offset % BITS_PER_LONG;
- const unsigned long ttl_mask = (mask[ttl_i] >> word_offset) & port_mask;
- const unsigned long ttl_bits = (bits[ttl_i] >> word_offset) & ttl_mask;
-
- /* set bits are processed a gpio port register at a time */
- for (i = 0; i < ARRAY_SIZE(ports); i++) {
- /* gpio offset in bits array */
- bits_offset = i * gpio_reg_size;
-
- /* check if any set bits for current port */
- gpio_mask = (*mask >> bits_offset) & port_mask;
- if (!gpio_mask) {
- /* no set bits for this port so move on to next port */
- continue;
- }
- raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+ for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
+ index = offset / 8;
- /* process output lines */
- out_state = ioread8(ports[i]) & ~gpio_mask;
- out_state |= (*bits >> bits_offset) & gpio_mask;
- iowrite8(out_state, ports[i]);
+ bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
- raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
- }
+ raw_spin_lock_irqsave(&idio24gpio->lock, flags);
- /* check if setting TTL lines and if they are in output mode */
- if (!ttl_mask || !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
- return;
+ /* read bits from current gpio port (port 6 is TTL GPIO) */
+ if (index < 6) {
+ out_state = ioread8(ports[index]);
+ } else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) {
+ out_state = ioread8(&idio24gpio->reg->ttl_out0_7);
+ } else {
+ /* skip TTL GPIO if set for input */
+ raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+ continue;
+ }
- /* handle TTL output */
- raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+ /* set requested bit states */
+ out_state &= ~gpio_mask;
+ out_state |= bitmask;
- /* process output lines */
- out_state = ioread8(&idio24gpio->reg->ttl_out0_7) & ~ttl_mask;
- out_state |= ttl_bits;
- iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
+ /* write bits for current gpio port (port 6 is TTL GPIO) */
+ if (index < 6)
+ iowrite8(out_state, ports[index]);
+ else
+ iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
- raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+ }
}
static void idio_24_irq_ack(struct irq_data *data)
@@ -360,13 +376,13 @@ static void idio_24_irq_mask(struct irq_data *data)
unsigned long flags;
const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
unsigned char new_irq_mask;
- const unsigned long bank_offset = bit_offset/8 * 8;
+ const unsigned long bank_offset = bit_offset / 8;
unsigned char cos_enable_state;
raw_spin_lock_irqsave(&idio24gpio->lock, flags);
- idio24gpio->irq_mask &= BIT(bit_offset);
- new_irq_mask = idio24gpio->irq_mask >> bank_offset;
+ idio24gpio->irq_mask &= ~BIT(bit_offset);
+ new_irq_mask = idio24gpio->irq_mask >> bank_offset * 8;
if (!new_irq_mask) {
cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
@@ -389,12 +405,12 @@ static void idio_24_irq_unmask(struct irq_data *data)
unsigned long flags;
unsigned char prev_irq_mask;
const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
- const unsigned long bank_offset = bit_offset/8 * 8;
+ const unsigned long bank_offset = bit_offset / 8;
unsigned char cos_enable_state;
raw_spin_lock_irqsave(&idio24gpio->lock, flags);
- prev_irq_mask = idio24gpio->irq_mask >> bank_offset;
+ prev_irq_mask = idio24gpio->irq_mask >> bank_offset * 8;
idio24gpio->irq_mask |= BIT(bit_offset);
if (!prev_irq_mask) {
@@ -452,8 +468,7 @@ static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
irq_mask = idio24gpio->irq_mask & irq_status;
for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
- generic_handle_irq(irq_find_mapping(chip->irq.domain,
- gpio + 24));
+ generic_handle_domain_irq(chip->irq.domain, gpio + 24);
raw_spin_lock(&idio24gpio->lock);
@@ -481,8 +496,10 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct device *const dev = &pdev->dev;
struct idio_24_gpio *idio24gpio;
int err;
+ const size_t pci_plx_bar_index = 1;
const size_t pci_bar_index = 2;
const char *const name = pci_name(pdev);
+ struct gpio_irq_chip *girq;
idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
if (!idio24gpio)
@@ -494,12 +511,13 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err;
}
- err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
+ err = pcim_iomap_regions(pdev, BIT(pci_plx_bar_index) | BIT(pci_bar_index), name);
if (err) {
dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
return err;
}
+ idio24gpio->plx = pcim_iomap_table(pdev)[pci_plx_bar_index];
idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
idio24gpio->chip.label = name;
@@ -516,10 +534,25 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
idio24gpio->chip.set = idio_24_gpio_set;
idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;
+ girq = &idio24gpio->chip.irq;
+ girq->chip = &idio_24_irqchip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_edge_irq;
+
raw_spin_lock_init(&idio24gpio->lock);
/* Software board reset */
iowrite8(0, &idio24gpio->reg->soft_reset);
+ /*
+ * enable PLX PEX8311 internal PCI wire interrupt and local interrupt
+ * input
+ */
+ iowrite8((INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT) >> 8,
+ idio24gpio->plx + PLX_PEX8311_PCI_LCS_INTCSR + 1);
err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
if (err) {
@@ -527,13 +560,6 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err;
}
- err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0,
- handle_edge_irq, IRQ_TYPE_NONE);
- if (err) {
- dev_err(dev, "Could not add irqchip (%d)\n", err);
- return err;
- }
-
err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
name, idio24gpio);
if (err) {
diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c
index f809a5a8e9eb..81a47ae09ff8 100644
--- a/drivers/gpio/gpio-pisosr.c
+++ b/drivers/gpio/gpio-pisosr.c
@@ -65,7 +65,7 @@ static int pisosr_gpio_get_direction(struct gpio_chip *chip,
unsigned offset)
{
/* This device always input */
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
}
static int pisosr_gpio_direction_input(struct gpio_chip *chip,
@@ -96,16 +96,16 @@ static int pisosr_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct pisosr_gpio *gpio = gpiochip_get_data(chip);
- unsigned int nbytes = DIV_ROUND_UP(chip->ngpio, 8);
- unsigned int i, j;
+ unsigned long offset;
+ unsigned long gpio_mask;
+ unsigned long buffer_state;
pisosr_gpio_refresh(gpio);
bitmap_zero(bits, chip->ngpio);
- for (i = 0; i < nbytes; i++) {
- j = i / sizeof(unsigned long);
- bits[j] |= ((unsigned long) gpio->buffer[i])
- << (8 * (i % sizeof(unsigned long)));
+ for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
+ buffer_state = gpio->buffer[offset / 8] & gpio_mask;
+ bitmap_set_value8(bits, buffer_state, offset);
}
return 0;
@@ -148,12 +148,9 @@ static int pisosr_gpio_probe(struct spi_device *spi)
return -ENOMEM;
gpio->load_gpio = devm_gpiod_get_optional(dev, "load", GPIOD_OUT_LOW);
- if (IS_ERR(gpio->load_gpio)) {
- ret = PTR_ERR(gpio->load_gpio);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Unable to allocate load GPIO\n");
- return ret;
- }
+ if (IS_ERR(gpio->load_gpio))
+ return dev_err_probe(dev, PTR_ERR(gpio->load_gpio),
+ "Unable to allocate load GPIO\n");
mutex_init(&gpio->lock);
@@ -166,15 +163,13 @@ static int pisosr_gpio_probe(struct spi_device *spi)
return 0;
}
-static int pisosr_gpio_remove(struct spi_device *spi)
+static void pisosr_gpio_remove(struct spi_device *spi)
{
struct pisosr_gpio *gpio = spi_get_drvdata(spi);
gpiochip_remove(&gpio->chip);
mutex_destroy(&gpio->lock);
-
- return 0;
}
static const struct spi_device_id pisosr_gpio_id_table[] = {
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 722ce5cf861e..6464056cb6ae 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
+#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/device.h>
@@ -51,7 +52,6 @@ struct pl061 {
void __iomem *base;
struct gpio_chip gc;
- struct irq_chip irq_chip;
int parent_irq;
#ifdef CONFIG_PM
@@ -63,7 +63,10 @@ static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
{
struct pl061 *pl061 = gpiochip_get_data(gc);
- return !(readb(pl061->base + GPIODIR) & BIT(offset));
+ if (readb(pl061->base + GPIODIR) & BIT(offset))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
@@ -219,8 +222,8 @@ static void pl061_irq_handler(struct irq_desc *desc)
pending = readb(pl061->base + GPIOMIS);
if (pending) {
for_each_set_bit(offset, &pending, PL061_GPIO_NR)
- generic_handle_irq(irq_find_mapping(gc->irq.domain,
- offset));
+ generic_handle_domain_irq(gc->irq.domain,
+ offset);
}
chained_irq_exit(irqchip, desc);
@@ -237,6 +240,8 @@ static void pl061_irq_mask(struct irq_data *d)
gpioie = readb(pl061->base + GPIOIE) & ~mask;
writeb(gpioie, pl061->base + GPIOIE);
raw_spin_unlock(&pl061->lock);
+
+ gpiochip_disable_irq(gc, d->hwirq);
}
static void pl061_irq_unmask(struct irq_data *d)
@@ -246,6 +251,8 @@ static void pl061_irq_unmask(struct irq_data *d)
u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
u8 gpioie;
+ gpiochip_enable_irq(gc, d->hwirq);
+
raw_spin_lock(&pl061->lock);
gpioie = readb(pl061->base + GPIOIE) | mask;
writeb(gpioie, pl061->base + GPIOIE);
@@ -279,6 +286,24 @@ static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
return irq_set_irq_wake(pl061->parent_irq, state);
}
+static void pl061_irq_print_chip(struct irq_data *data, struct seq_file *p)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+
+ seq_printf(p, dev_name(gc->parent));
+}
+
+static const struct irq_chip pl061_irq_chip = {
+ .irq_ack = pl061_irq_ack,
+ .irq_mask = pl061_irq_mask,
+ .irq_unmask = pl061_irq_unmask,
+ .irq_set_type = pl061_irq_type,
+ .irq_set_wake = pl061_irq_set_wake,
+ .irq_print_chip = pl061_irq_print_chip,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -295,11 +320,8 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
return PTR_ERR(pl061->base);
raw_spin_lock_init(&pl061->lock);
- if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
- pl061->gc.request = gpiochip_generic_request;
- pl061->gc.free = gpiochip_generic_free;
- }
-
+ pl061->gc.request = gpiochip_generic_request;
+ pl061->gc.free = gpiochip_generic_free;
pl061->gc.base = -1;
pl061->gc.get_direction = pl061_get_direction;
pl061->gc.direction_input = pl061_direction_input;
@@ -314,23 +336,14 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
/*
* irq_chip support
*/
- pl061->irq_chip.name = dev_name(dev);
- pl061->irq_chip.irq_ack = pl061_irq_ack;
- pl061->irq_chip.irq_mask = pl061_irq_mask;
- pl061->irq_chip.irq_unmask = pl061_irq_unmask;
- pl061->irq_chip.irq_set_type = pl061_irq_type;
- pl061->irq_chip.irq_set_wake = pl061_irq_set_wake;
-
writeb(0, pl061->base + GPIOIE); /* disable irqs */
irq = adev->irq[0];
- if (irq < 0) {
- dev_err(&adev->dev, "invalid IRQ\n");
- return -ENODEV;
- }
+ if (!irq)
+ dev_warn(&adev->dev, "IRQ support disabled\n");
pl061->parent_irq = irq;
girq = &pl061->gc.irq;
- girq->chip = &pl061->irq_chip;
+ gpio_irq_chip_set_chip(girq, &pl061_irq_chip);
girq->parent_handler = pl061_irq_handler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
@@ -410,6 +423,7 @@ static const struct amba_id pl061_ids[] = {
},
{ 0, 0 },
};
+MODULE_DEVICE_TABLE(amba, pl061_ids);
static struct amba_driver pl061_gpio_driver = {
.drv = {
@@ -421,9 +435,6 @@ static struct amba_driver pl061_gpio_driver = {
.id_table = pl061_ids,
.probe = pl061_probe,
};
+module_amba_driver(pl061_gpio_driver);
-static int __init pl061_gpio_init(void)
-{
- return amba_driver_register(&pl061_gpio_driver);
-}
-device_initcall(pl061_gpio_init);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c
index 24228cf79afc..e518490c4b68 100644
--- a/drivers/gpio/gpio-pmic-eic-sprd.c
+++ b/drivers/gpio/gpio-pmic-eic-sprd.c
@@ -48,7 +48,7 @@ enum {
* struct sprd_pmic_eic - PMIC EIC controller
* @chip: the gpio_chip structure.
* @intc: the irq_chip structure.
- * @regmap: the regmap from the parent device.
+ * @map: the regmap from the parent device.
* @offset: the EIC controller's offset address of the PMIC.
* @reg: the array to cache the EIC registers.
* @buslock: for bus lock/sync and unlock.
@@ -305,10 +305,8 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev)
mutex_init(&pmic_eic->buslock);
pmic_eic->irq = platform_get_irq(pdev, 0);
- if (pmic_eic->irq < 0) {
- dev_err(&pdev->dev, "Failed to get PMIC EIC interrupt.\n");
+ if (pmic_eic->irq < 0)
return pmic_eic->irq;
- }
pmic_eic->map = dev_get_regmap(pdev->dev.parent, NULL);
if (!pmic_eic->map)
@@ -333,7 +331,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev)
pmic_eic->chip.ngpio = SPRD_PMIC_EIC_NR;
pmic_eic->chip.base = -1;
pmic_eic->chip.parent = &pdev->dev;
- pmic_eic->chip.of_node = pdev->dev.of_node;
pmic_eic->chip.direction_input = sprd_pmic_eic_direction_input;
pmic_eic->chip.request = sprd_pmic_eic_request;
pmic_eic->chip.free = sprd_pmic_eic_free;
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index 9888b62f37af..c7fbfa3ae43b 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -343,8 +343,7 @@ static int pxa_gpio_of_xlate(struct gpio_chip *gc,
}
#endif
-static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio,
- struct device_node *np, void __iomem *regbase)
+static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio, void __iomem *regbase)
{
int i, gpio, nbanks = DIV_ROUND_UP(ngpio, 32);
struct pxa_gpio_bank *bank;
@@ -354,6 +353,7 @@ static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio,
if (!pchip->banks)
return -ENOMEM;
+ pchip->chip.parent = pchip->dev;
pchip->chip.label = "gpio-pxa";
pchip->chip.direction_input = pxa_gpio_direction_input;
pchip->chip.direction_output = pxa_gpio_direction_output;
@@ -361,14 +361,10 @@ static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio,
pchip->chip.set = pxa_gpio_set;
pchip->chip.to_irq = pxa_gpio_to_irq;
pchip->chip.ngpio = ngpio;
-
- if (pxa_gpio_has_pinctrl()) {
- pchip->chip.request = gpiochip_generic_request;
- pchip->chip.free = gpiochip_generic_free;
- }
+ pchip->chip.request = gpiochip_generic_request;
+ pchip->chip.free = gpiochip_generic_free;
#ifdef CONFIG_OF_GPIO
- pchip->chip.of_node = np;
pchip->chip.of_xlate = pxa_gpio_of_xlate;
pchip->chip.of_gpio_n_cells = 2;
#endif
@@ -458,9 +454,8 @@ static irqreturn_t pxa_gpio_demux_handler(int in_irq, void *d)
for_each_set_bit(n, &gedr, BITS_PER_LONG) {
loop = 1;
- generic_handle_irq(
- irq_find_mapping(pchip->irqdomain,
- gpio + n));
+ generic_handle_domain_irq(pchip->irqdomain,
+ gpio + n);
}
}
handled += loop;
@@ -474,9 +469,9 @@ static irqreturn_t pxa_gpio_direct_handler(int in_irq, void *d)
struct pxa_gpio_chip *pchip = d;
if (in_irq == pchip->irq0) {
- generic_handle_irq(irq_find_mapping(pchip->irqdomain, 0));
+ generic_handle_domain_irq(pchip->irqdomain, 0);
} else if (in_irq == pchip->irq1) {
- generic_handle_irq(irq_find_mapping(pchip->irqdomain, 1));
+ generic_handle_domain_irq(pchip->irqdomain, 1);
} else {
pr_err("%s() unknown irq %d\n", __func__, in_irq);
return IRQ_NONE;
@@ -652,8 +647,8 @@ static int pxa_gpio_probe(struct platform_device *pdev)
if (!pchip->irqdomain)
return -ENOMEM;
- irq0 = platform_get_irq_byname(pdev, "gpio0");
- irq1 = platform_get_irq_byname(pdev, "gpio1");
+ irq0 = platform_get_irq_byname_optional(pdev, "gpio0");
+ irq1 = platform_get_irq_byname_optional(pdev, "gpio1");
irq_mux = platform_get_irq_byname(pdev, "gpio_mux");
if ((irq0 > 0 && irq1 <= 0) || (irq0 <= 0 && irq1 > 0)
|| (irq_mux <= 0))
@@ -663,8 +658,8 @@ static int pxa_gpio_probe(struct platform_device *pdev)
pchip->irq1 = irq1;
gpio_reg_base = devm_platform_ioremap_resource(pdev, 0);
- if (!gpio_reg_base)
- return -EINVAL;
+ if (IS_ERR(gpio_reg_base))
+ return PTR_ERR(gpio_reg_base);
clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
@@ -679,8 +674,7 @@ static int pxa_gpio_probe(struct platform_device *pdev)
}
/* Initialize GPIO chips */
- ret = pxa_init_gpio_chip(pchip, pxa_last_gpio + 1, pdev->dev.of_node,
- gpio_reg_base);
+ ret = pxa_init_gpio_chip(pchip, pxa_last_gpio + 1, gpio_reg_base);
if (ret) {
clk_put(clk);
return ret;
diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c
index b77ea16ffa03..3c414e0005fc 100644
--- a/drivers/gpio/gpio-raspberrypi-exp.c
+++ b/drivers/gpio/gpio-raspberrypi-exp.c
@@ -147,7 +147,10 @@ static int rpi_exp_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
get.gpio);
return ret ? ret : -EIO;
}
- return !get.direction;
+ if (get.direction)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off)
@@ -205,7 +208,7 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev)
return -ENOENT;
}
- fw = rpi_firmware_get(fw_node);
+ fw = devm_rpi_firmware_get(&pdev->dev, fw_node);
of_node_put(fw_node);
if (!fw)
return -EPROBE_DEFER;
@@ -218,7 +221,6 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev)
rpi_gpio->gc.parent = dev;
rpi_gpio->gc.label = MODULE_NAME;
rpi_gpio->gc.owner = THIS_MODULE;
- rpi_gpio->gc.of_node = np;
rpi_gpio->gc.base = -1;
rpi_gpio->gc.ngpio = NUM_GPIO;
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index 187984d26f47..5b117f3bd322 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -32,32 +32,38 @@ struct gpio_rcar_bank_info {
u32 intmsk;
};
+struct gpio_rcar_info {
+ bool has_outdtsel;
+ bool has_both_edge_trigger;
+ bool has_always_in;
+ bool has_inen;
+};
+
struct gpio_rcar_priv {
void __iomem *base;
spinlock_t lock;
struct device *dev;
struct gpio_chip gpio_chip;
- struct irq_chip irq_chip;
unsigned int irq_parent;
atomic_t wakeup_path;
- bool has_outdtsel;
- bool has_both_edge_trigger;
+ struct gpio_rcar_info info;
struct gpio_rcar_bank_info bank_info;
};
-#define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */
-#define INOUTSEL 0x04 /* General Input/Output Switching Register */
-#define OUTDT 0x08 /* General Output Register */
-#define INDT 0x0c /* General Input Register */
-#define INTDT 0x10 /* Interrupt Display Register */
-#define INTCLR 0x14 /* Interrupt Clear Register */
-#define INTMSK 0x18 /* Interrupt Mask Register */
-#define MSKCLR 0x1c /* Interrupt Mask Clear Register */
-#define POSNEG 0x20 /* Positive/Negative Logic Select Register */
-#define EDGLEVEL 0x24 /* Edge/level Select Register */
-#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */
-#define OUTDTSEL 0x40 /* Output Data Select Register */
-#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
+#define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */
+#define INOUTSEL 0x04 /* General Input/Output Switching Register */
+#define OUTDT 0x08 /* General Output Register */
+#define INDT 0x0c /* General Input Register */
+#define INTDT 0x10 /* Interrupt Display Register */
+#define INTCLR 0x14 /* Interrupt Clear Register */
+#define INTMSK 0x18 /* Interrupt Mask Register */
+#define MSKCLR 0x1c /* Interrupt Mask Clear Register */
+#define POSNEG 0x20 /* Positive/Negative Logic Select Register */
+#define EDGLEVEL 0x24 /* Edge/level Select Register */
+#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */
+#define OUTDTSEL 0x40 /* Output Data Select Register */
+#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
+#define INEN 0x50 /* General Input Enable Register */
#define RCAR_MAX_GPIO_PER_BANK 32
@@ -89,16 +95,20 @@ static void gpio_rcar_irq_disable(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_rcar_priv *p = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
- gpio_rcar_write(p, INTMSK, ~BIT(irqd_to_hwirq(d)));
+ gpio_rcar_write(p, INTMSK, ~BIT(hwirq));
+ gpiochip_disable_irq(gc, hwirq);
}
static void gpio_rcar_irq_enable(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_rcar_priv *p = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
- gpio_rcar_write(p, MSKCLR, BIT(irqd_to_hwirq(d)));
+ gpiochip_enable_irq(gc, hwirq);
+ gpio_rcar_write(p, MSKCLR, BIT(hwirq));
}
static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p,
@@ -116,14 +126,14 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p,
spin_lock_irqsave(&p->lock, flags);
- /* Configure postive or negative logic in POSNEG */
+ /* Configure positive or negative logic in POSNEG */
gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge);
/* Configure edge or level trigger in EDGLEVEL */
gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger);
/* Select one edge or both edges in BOTHEDGE */
- if (p->has_both_edge_trigger)
+ if (p->info.has_both_edge_trigger)
gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both);
/* Select "Interrupt Input Mode" in IOINTSEL */
@@ -162,7 +172,7 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type)
false);
break;
case IRQ_TYPE_EDGE_BOTH:
- if (!p->has_both_edge_trigger)
+ if (!p->info.has_both_edge_trigger)
return -EINVAL;
gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false,
true);
@@ -196,6 +206,17 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
return 0;
}
+static const struct irq_chip gpio_rcar_irq_chip = {
+ .name = "gpio-rcar",
+ .irq_mask = gpio_rcar_irq_disable,
+ .irq_unmask = gpio_rcar_irq_enable,
+ .irq_set_type = gpio_rcar_irq_set_type,
+ .irq_set_wake = gpio_rcar_irq_set_wake,
+ .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_MASK_ON_SUSPEND,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
{
struct gpio_rcar_priv *p = dev_id;
@@ -206,8 +227,8 @@ static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
gpio_rcar_read(p, INTMSK))) {
offset = __ffs(pending);
gpio_rcar_write(p, INTCLR, BIT(offset));
- generic_handle_irq(irq_find_mapping(p->gpio_chip.irq.domain,
- offset));
+ generic_handle_domain_irq(p->gpio_chip.irq.domain,
+ offset);
irqs_handled++;
}
@@ -228,7 +249,7 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
spin_lock_irqsave(&p->lock, flags);
- /* Configure postive logic in POSNEG */
+ /* Configure positive logic in POSNEG */
gpio_rcar_modify_bit(p, POSNEG, gpio, false);
/* Select "General Input/Output Mode" in IOINTSEL */
@@ -238,7 +259,7 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
gpio_rcar_modify_bit(p, INOUTSEL, gpio, output);
/* Select General Output Register to output data in OUTDTSEL */
- if (p->has_outdtsel && output)
+ if (p->info.has_outdtsel && output)
gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false);
spin_unlock_irqrestore(&p->lock, flags);
@@ -250,8 +271,10 @@ static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset)
int error;
error = pm_runtime_get_sync(p->dev);
- if (error < 0)
+ if (error < 0) {
+ pm_runtime_put(p->dev);
return error;
+ }
error = pinctrl_gpio_request(chip->base + offset);
if (error)
@@ -279,7 +302,10 @@ static int gpio_rcar_get_direction(struct gpio_chip *chip, unsigned int offset)
{
struct gpio_rcar_priv *p = gpiochip_get_data(chip);
- return !(gpio_rcar_read(p, INOUTSEL) & BIT(offset));
+ if (gpio_rcar_read(p, INOUTSEL) & BIT(offset))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -290,14 +316,51 @@ static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset)
static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset)
{
+ struct gpio_rcar_priv *p = gpiochip_get_data(chip);
u32 bit = BIT(offset);
- /* testing on r8a7790 shows that INDT does not show correct pin state
- * when configured as output, so use OUTDT in case of output pins */
- if (gpio_rcar_read(gpiochip_get_data(chip), INOUTSEL) & bit)
- return !!(gpio_rcar_read(gpiochip_get_data(chip), OUTDT) & bit);
+ /*
+ * Before R-Car Gen3, INDT does not show correct pin state when
+ * configured as output, so use OUTDT in case of output pins
+ */
+ if (!p->info.has_always_in && (gpio_rcar_read(p, INOUTSEL) & bit))
+ return !!(gpio_rcar_read(p, OUTDT) & bit);
else
- return !!(gpio_rcar_read(gpiochip_get_data(chip), INDT) & bit);
+ return !!(gpio_rcar_read(p, INDT) & bit);
+}
+
+static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpio_rcar_priv *p = gpiochip_get_data(chip);
+ u32 bankmask, outputs, m, val = 0;
+ unsigned long flags;
+
+ bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0);
+ if (chip->valid_mask)
+ bankmask &= chip->valid_mask[0];
+
+ if (!bankmask)
+ return 0;
+
+ if (p->info.has_always_in) {
+ bits[0] = gpio_rcar_read(p, INDT) & bankmask;
+ return 0;
+ }
+
+ spin_lock_irqsave(&p->lock, flags);
+ outputs = gpio_rcar_read(p, INOUTSEL);
+ m = outputs & bankmask;
+ if (m)
+ val |= gpio_rcar_read(p, OUTDT) & m;
+
+ m = ~outputs & bankmask;
+ if (m)
+ val |= gpio_rcar_read(p, INDT) & m;
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ bits[0] = val;
+ return 0;
}
static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value)
@@ -341,49 +404,38 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset,
return 0;
}
-struct gpio_rcar_info {
- bool has_outdtsel;
- bool has_both_edge_trigger;
-};
-
static const struct gpio_rcar_info gpio_rcar_info_gen1 = {
.has_outdtsel = false,
.has_both_edge_trigger = false,
+ .has_always_in = false,
+ .has_inen = false,
};
static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
.has_outdtsel = true,
.has_both_edge_trigger = true,
+ .has_always_in = false,
+ .has_inen = false,
+};
+
+static const struct gpio_rcar_info gpio_rcar_info_gen3 = {
+ .has_outdtsel = true,
+ .has_both_edge_trigger = true,
+ .has_always_in = true,
+ .has_inen = false,
+};
+
+static const struct gpio_rcar_info gpio_rcar_info_gen4 = {
+ .has_outdtsel = true,
+ .has_both_edge_trigger = true,
+ .has_always_in = true,
+ .has_inen = true,
};
static const struct of_device_id gpio_rcar_of_table[] = {
{
- .compatible = "renesas,gpio-r8a7743",
- /* RZ/G1 GPIO is identical to R-Car Gen2. */
- .data = &gpio_rcar_info_gen2,
- }, {
- .compatible = "renesas,gpio-r8a7790",
- .data = &gpio_rcar_info_gen2,
- }, {
- .compatible = "renesas,gpio-r8a7791",
- .data = &gpio_rcar_info_gen2,
- }, {
- .compatible = "renesas,gpio-r8a7792",
- .data = &gpio_rcar_info_gen2,
- }, {
- .compatible = "renesas,gpio-r8a7793",
- .data = &gpio_rcar_info_gen2,
- }, {
- .compatible = "renesas,gpio-r8a7794",
- .data = &gpio_rcar_info_gen2,
- }, {
- .compatible = "renesas,gpio-r8a7795",
- /* Gen3 GPIO is identical to Gen2. */
- .data = &gpio_rcar_info_gen2,
- }, {
- .compatible = "renesas,gpio-r8a7796",
- /* Gen3 GPIO is identical to Gen2. */
- .data = &gpio_rcar_info_gen2,
+ .compatible = "renesas,gpio-r8a779a0",
+ .data = &gpio_rcar_info_gen4,
}, {
.compatible = "renesas,rcar-gen1-gpio",
.data = &gpio_rcar_info_gen1,
@@ -392,8 +444,10 @@ static const struct of_device_id gpio_rcar_of_table[] = {
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,rcar-gen3-gpio",
- /* Gen3 GPIO is identical to Gen2. */
- .data = &gpio_rcar_info_gen2,
+ .data = &gpio_rcar_info_gen3,
+ }, {
+ .compatible = "renesas,rcar-gen4-gpio",
+ .data = &gpio_rcar_info_gen4,
}, {
.compatible = "renesas,gpio-rcar",
.data = &gpio_rcar_info_gen1,
@@ -412,8 +466,7 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
int ret;
info = of_device_get_match_data(p->dev);
- p->has_outdtsel = info->has_outdtsel;
- p->has_both_edge_trigger = info->has_both_edge_trigger;
+ p->info = *info;
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
*npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK;
@@ -427,12 +480,22 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
return 0;
}
+static void gpio_rcar_enable_inputs(struct gpio_rcar_priv *p)
+{
+ u32 mask = GENMASK(p->gpio_chip.ngpio - 1, 0);
+
+ /* Select "Input Enable" in INEN */
+ if (p->gpio_chip.valid_mask)
+ mask &= p->gpio_chip.valid_mask[0];
+ if (mask)
+ gpio_rcar_write(p, INEN, gpio_rcar_read(p, INEN) | mask);
+}
+
static int gpio_rcar_probe(struct platform_device *pdev)
{
struct gpio_rcar_priv *p;
- struct resource *irq;
struct gpio_chip *gpio_chip;
- struct irq_chip *irq_chip;
+ struct gpio_irq_chip *girq;
struct device *dev = &pdev->dev;
const char *name = dev_name(dev);
unsigned int npins;
@@ -454,12 +517,10 @@ static int gpio_rcar_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq) {
- dev_err(dev, "missing IRQ\n");
- ret = -EINVAL;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
goto err0;
- }
+ p->irq_parent = ret;
p->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(p->base)) {
@@ -473,6 +534,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
gpio_chip->get_direction = gpio_rcar_get_direction;
gpio_chip->direction_input = gpio_rcar_direction_input;
gpio_chip->get = gpio_rcar_get;
+ gpio_chip->get_multiple = gpio_rcar_get_multiple;
gpio_chip->direction_output = gpio_rcar_direction_output;
gpio_chip->set = gpio_rcar_set;
gpio_chip->set_multiple = gpio_rcar_set_multiple;
@@ -482,14 +544,14 @@ static int gpio_rcar_probe(struct platform_device *pdev)
gpio_chip->base = -1;
gpio_chip->ngpio = npins;
- irq_chip = &p->irq_chip;
- irq_chip->name = name;
- irq_chip->parent_device = dev;
- irq_chip->irq_mask = gpio_rcar_irq_disable;
- irq_chip->irq_unmask = gpio_rcar_irq_enable;
- irq_chip->irq_set_type = gpio_rcar_irq_set_type;
- irq_chip->irq_set_wake = gpio_rcar_irq_set_wake;
- irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
+ girq = &gpio_chip->irq;
+ gpio_irq_chip_set_chip(girq, &gpio_rcar_irq_chip);
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
ret = gpiochip_add_data(gpio_chip, p);
if (ret) {
@@ -497,19 +559,18 @@ static int gpio_rcar_probe(struct platform_device *pdev)
goto err0;
}
- ret = gpiochip_irqchip_add(gpio_chip, irq_chip, 0, handle_level_irq,
- IRQ_TYPE_NONE);
+ irq_domain_set_pm_device(gpio_chip->irq.domain, dev);
+ ret = devm_request_irq(dev, p->irq_parent, gpio_rcar_irq_handler,
+ IRQF_SHARED, name, p);
if (ret) {
- dev_err(dev, "cannot add irqchip\n");
+ dev_err(dev, "failed to request IRQ\n");
goto err1;
}
- p->irq_parent = irq->start;
- if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler,
- IRQF_SHARED, name, p)) {
- dev_err(dev, "failed to request IRQ\n");
- ret = -ENOENT;
- goto err1;
+ if (p->info.has_inen) {
+ pm_runtime_get_sync(dev);
+ gpio_rcar_enable_inputs(p);
+ pm_runtime_put(dev);
}
dev_info(dev, "driving %d GPIOs\n", npins);
@@ -544,7 +605,7 @@ static int gpio_rcar_suspend(struct device *dev)
p->bank_info.intmsk = gpio_rcar_read(p, INTMSK);
p->bank_info.posneg = gpio_rcar_read(p, POSNEG);
p->bank_info.edglevel = gpio_rcar_read(p, EDGLEVEL);
- if (p->has_both_edge_trigger)
+ if (p->info.has_both_edge_trigger)
p->bank_info.bothedge = gpio_rcar_read(p, BOTHEDGE);
if (atomic_read(&p->wakeup_path))
@@ -587,6 +648,9 @@ static int gpio_rcar_resume(struct device *dev)
}
}
+ if (p->info.has_inen)
+ gpio_rcar_enable_inputs(p);
+
return 0;
}
#endif /* CONFIG_PM_SLEEP*/
diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c
new file mode 100644
index 000000000000..62ba18b3a602
--- /dev/null
+++ b/drivers/gpio/gpio-rda.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RDA Micro GPIO driver
+ *
+ * Copyright (C) 2012 RDA Micro Inc.
+ * Copyright (C) 2019 Manivannan Sadhasivam
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#define RDA_GPIO_OEN_VAL 0x00
+#define RDA_GPIO_OEN_SET_OUT 0x04
+#define RDA_GPIO_OEN_SET_IN 0x08
+#define RDA_GPIO_VAL 0x0c
+#define RDA_GPIO_SET 0x10
+#define RDA_GPIO_CLR 0x14
+#define RDA_GPIO_INT_CTRL_SET 0x18
+#define RDA_GPIO_INT_CTRL_CLR 0x1c
+#define RDA_GPIO_INT_CLR 0x20
+#define RDA_GPIO_INT_STATUS 0x24
+
+#define RDA_GPIO_IRQ_RISE_SHIFT 0
+#define RDA_GPIO_IRQ_FALL_SHIFT 8
+#define RDA_GPIO_DEBOUCE_SHIFT 16
+#define RDA_GPIO_LEVEL_SHIFT 24
+
+#define RDA_GPIO_IRQ_MASK 0xff
+
+/* Each bank consists of 32 GPIOs */
+#define RDA_GPIO_BANK_NR 32
+
+struct rda_gpio {
+ struct gpio_chip chip;
+ void __iomem *base;
+ spinlock_t lock;
+ struct irq_chip irq_chip;
+ int irq;
+};
+
+static inline void rda_gpio_update(struct gpio_chip *chip, unsigned int offset,
+ u16 reg, int val)
+{
+ struct rda_gpio *rda_gpio = gpiochip_get_data(chip);
+ void __iomem *base = rda_gpio->base;
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&rda_gpio->lock, flags);
+ tmp = readl_relaxed(base + reg);
+
+ if (val)
+ tmp |= BIT(offset);
+ else
+ tmp &= ~BIT(offset);
+
+ writel_relaxed(tmp, base + reg);
+ spin_unlock_irqrestore(&rda_gpio->lock, flags);
+}
+
+static void rda_gpio_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct rda_gpio *rda_gpio = gpiochip_get_data(chip);
+ void __iomem *base = rda_gpio->base;
+ u32 offset = irqd_to_hwirq(data);
+ u32 value;
+
+ value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT;
+ value |= BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT;
+
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR);
+}
+
+static void rda_gpio_irq_ack(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ u32 offset = irqd_to_hwirq(data);
+
+ rda_gpio_update(chip, offset, RDA_GPIO_INT_CLR, 1);
+}
+
+static int rda_gpio_set_irq(struct gpio_chip *chip, u32 offset,
+ unsigned int flow_type)
+{
+ struct rda_gpio *rda_gpio = gpiochip_get_data(chip);
+ void __iomem *base = rda_gpio->base;
+ u32 value;
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ /* Set rising edge trigger */
+ value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT;
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET);
+
+ /* Switch to edge trigger interrupt */
+ value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT;
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR);
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ /* Set falling edge trigger */
+ value = BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT;
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET);
+
+ /* Switch to edge trigger interrupt */
+ value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT;
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR);
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ /* Set both edge trigger */
+ value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT;
+ value |= BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT;
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET);
+
+ /* Switch to edge trigger interrupt */
+ value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT;
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR);
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ /* Set high level trigger */
+ value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT;
+
+ /* Switch to level trigger interrupt */
+ value |= BIT(offset) << RDA_GPIO_LEVEL_SHIFT;
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET);
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ /* Set low level trigger */
+ value = BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT;
+
+ /* Switch to level trigger interrupt */
+ value |= BIT(offset) << RDA_GPIO_LEVEL_SHIFT;
+ writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void rda_gpio_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ u32 offset = irqd_to_hwirq(data);
+ u32 trigger = irqd_get_trigger_type(data);
+
+ rda_gpio_set_irq(chip, offset, trigger);
+}
+
+static int rda_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ u32 offset = irqd_to_hwirq(data);
+ int ret;
+
+ ret = rda_gpio_set_irq(chip, offset, flow_type);
+ if (ret)
+ return ret;
+
+ if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ irq_set_handler_locked(data, handle_level_irq);
+ else if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ irq_set_handler_locked(data, handle_edge_irq);
+
+ return 0;
+}
+
+static void rda_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
+ struct irq_chip *ic = irq_desc_get_chip(desc);
+ struct rda_gpio *rda_gpio = gpiochip_get_data(chip);
+ unsigned long status;
+ u32 n;
+
+ chained_irq_enter(ic, desc);
+
+ status = readl_relaxed(rda_gpio->base + RDA_GPIO_INT_STATUS);
+ /* Only lower 8 bits are capable of generating interrupts */
+ status &= RDA_GPIO_IRQ_MASK;
+
+ for_each_set_bit(n, &status, RDA_GPIO_BANK_NR)
+ generic_handle_domain_irq(chip->irq.domain, n);
+
+ chained_irq_exit(ic, desc);
+}
+
+static int rda_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_irq_chip *girq;
+ struct rda_gpio *rda_gpio;
+ u32 ngpios;
+ int ret;
+
+ rda_gpio = devm_kzalloc(dev, sizeof(*rda_gpio), GFP_KERNEL);
+ if (!rda_gpio)
+ return -ENOMEM;
+
+ ret = device_property_read_u32(dev, "ngpios", &ngpios);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Not all ports have interrupt capability. For instance, on
+ * RDA8810PL, GPIOC doesn't support interrupt. So we must handle
+ * those also.
+ */
+ rda_gpio->irq = platform_get_irq(pdev, 0);
+
+ rda_gpio->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rda_gpio->base))
+ return PTR_ERR(rda_gpio->base);
+
+ spin_lock_init(&rda_gpio->lock);
+
+ ret = bgpio_init(&rda_gpio->chip, dev, 4,
+ rda_gpio->base + RDA_GPIO_VAL,
+ rda_gpio->base + RDA_GPIO_SET,
+ rda_gpio->base + RDA_GPIO_CLR,
+ rda_gpio->base + RDA_GPIO_OEN_SET_OUT,
+ rda_gpio->base + RDA_GPIO_OEN_SET_IN,
+ BGPIOF_READ_OUTPUT_REG_SET);
+ if (ret) {
+ dev_err(dev, "bgpio_init failed\n");
+ return ret;
+ }
+
+ rda_gpio->chip.label = dev_name(dev);
+ rda_gpio->chip.ngpio = ngpios;
+ rda_gpio->chip.base = -1;
+
+ if (rda_gpio->irq >= 0) {
+ rda_gpio->irq_chip.name = "rda-gpio",
+ rda_gpio->irq_chip.irq_ack = rda_gpio_irq_ack,
+ rda_gpio->irq_chip.irq_mask = rda_gpio_irq_mask,
+ rda_gpio->irq_chip.irq_unmask = rda_gpio_irq_unmask,
+ rda_gpio->irq_chip.irq_set_type = rda_gpio_irq_set_type,
+ rda_gpio->irq_chip.flags = IRQCHIP_SKIP_SET_WAKE,
+
+ girq = &rda_gpio->chip.irq;
+ girq->chip = &rda_gpio->irq_chip;
+ girq->handler = handle_bad_irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->parent_handler = rda_gpio_irq_handler;
+ girq->parent_handler_data = rda_gpio;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = rda_gpio->irq;
+ }
+
+ platform_set_drvdata(pdev, rda_gpio);
+
+ return devm_gpiochip_add_data(dev, &rda_gpio->chip, rda_gpio);
+}
+
+static const struct of_device_id rda_gpio_of_match[] = {
+ { .compatible = "rda,8810pl-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rda_gpio_of_match);
+
+static struct platform_driver rda_gpio_driver = {
+ .probe = rda_gpio_probe,
+ .driver = {
+ .name = "rda-gpio",
+ .of_match_table = rda_gpio_of_match,
+ },
+};
+
+module_platform_driver_probe(rda_gpio_driver, rda_gpio_probe);
+
+MODULE_DESCRIPTION("RDA Micro GPIO driver");
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c
new file mode 100644
index 000000000000..c52b2cb1acae
--- /dev/null
+++ b/drivers/gpio/gpio-realtek-otto.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/gpio/driver.h>
+#include <linux/cpumask.h>
+#include <linux/irq.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+/*
+ * Total register block size is 0x1C for one bank of four ports (A, B, C, D).
+ * An optional second bank, with ports E, F, G, and H, may be present, starting
+ * at register offset 0x1C.
+ */
+
+/*
+ * Pin select: (0) "normal", (1) "dedicate peripheral"
+ * Not used on RTL8380/RTL8390, peripheral selection is managed by control bits
+ * in the peripheral registers.
+ */
+#define REALTEK_GPIO_REG_CNR 0x00
+/* Clear bit (0) for input, set bit (1) for output */
+#define REALTEK_GPIO_REG_DIR 0x08
+#define REALTEK_GPIO_REG_DATA 0x0C
+/* Read bit for IRQ status, write 1 to clear IRQ */
+#define REALTEK_GPIO_REG_ISR 0x10
+/* Two bits per GPIO in IMR registers */
+#define REALTEK_GPIO_REG_IMR 0x14
+#define REALTEK_GPIO_REG_IMR_AB 0x14
+#define REALTEK_GPIO_REG_IMR_CD 0x18
+#define REALTEK_GPIO_IMR_LINE_MASK GENMASK(1, 0)
+#define REALTEK_GPIO_IRQ_EDGE_FALLING 1
+#define REALTEK_GPIO_IRQ_EDGE_RISING 2
+#define REALTEK_GPIO_IRQ_EDGE_BOTH 3
+
+#define REALTEK_GPIO_MAX 32
+#define REALTEK_GPIO_PORTS_PER_BANK 4
+
+/**
+ * realtek_gpio_ctrl - Realtek Otto GPIO driver data
+ *
+ * @gc: Associated gpio_chip instance
+ * @base: Base address of the register block for a GPIO bank
+ * @lock: Lock for accessing the IRQ registers and values
+ * @intr_mask: Mask for interrupts lines
+ * @intr_type: Interrupt type selection
+ *
+ * Because the interrupt mask register (IMR) combines the function of IRQ type
+ * selection and masking, two extra values are stored. @intr_mask is used to
+ * mask/unmask the interrupts for a GPIO port, and @intr_type is used to store
+ * the selected interrupt types. The logical AND of these values is written to
+ * IMR on changes.
+ */
+struct realtek_gpio_ctrl {
+ struct gpio_chip gc;
+ void __iomem *base;
+ void __iomem *cpumask_base;
+ struct cpumask cpu_irq_maskable;
+ raw_spinlock_t lock;
+ u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
+ u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
+ unsigned int (*port_offset_u8)(unsigned int port);
+ unsigned int (*port_offset_u16)(unsigned int port);
+};
+
+/* Expand with more flags as devices with other quirks are added */
+enum realtek_gpio_flags {
+ /*
+ * Allow disabling interrupts, for cases where the port order is
+ * unknown. This may result in a port mismatch between ISR and IMR.
+ * An interrupt would appear to come from a different line than the
+ * line the IRQ handler was assigned to, causing uncaught interrupts.
+ */
+ GPIO_INTERRUPTS_DISABLED = BIT(0),
+ /*
+ * Port order is reversed, meaning DCBA register layout for 1-bit
+ * fields, and [BA, DC] for 2-bit fields.
+ */
+ GPIO_PORTS_REVERSED = BIT(1),
+ /*
+ * Interrupts can be enabled per cpu. This requires a secondary IO
+ * range, where the per-cpu enable masks are located.
+ */
+ GPIO_INTERRUPTS_PER_CPU = BIT(2),
+};
+
+static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+
+ return container_of(gc, struct realtek_gpio_ctrl, gc);
+}
+
+/*
+ * Normal port order register access
+ *
+ * Port information is stored with the first port at offset 0, followed by the
+ * second, etc. Most registers store one bit per GPIO and use a u8 value per
+ * port. The two interrupt mask registers store two bits per GPIO, so use u16
+ * values.
+ */
+static unsigned int realtek_gpio_port_offset_u8(unsigned int port)
+{
+ return port;
+}
+
+static unsigned int realtek_gpio_port_offset_u16(unsigned int port)
+{
+ return 2 * port;
+}
+
+/*
+ * Reversed port order register access
+ *
+ * For registers with one bit per GPIO, all ports are stored as u8-s in one
+ * register in reversed order. The two interrupt mask registers store two bits
+ * per GPIO, so use u16 values. The first register contains ports 1 and 0, the
+ * second ports 3 and 2.
+ */
+static unsigned int realtek_gpio_port_offset_u8_rev(unsigned int port)
+{
+ return 3 - port;
+}
+
+static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port)
+{
+ return 2 * (port ^ 1);
+}
+
+static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl,
+ unsigned int port, u16 irq_type, u16 irq_mask)
+{
+ iowrite16(irq_type & irq_mask,
+ ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port));
+}
+
+static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl,
+ unsigned int port, u8 mask)
+{
+ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
+}
+
+static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port)
+{
+ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
+}
+
+/* Set the rising and falling edge mask bits for a GPIO port pin */
+static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value)
+{
+ return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin;
+}
+
+static void realtek_gpio_irq_ack(struct irq_data *data)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ irq_hw_number_t line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+
+ realtek_gpio_clear_isr(ctrl, port, BIT(port_pin));
+}
+
+static void realtek_gpio_irq_unmask(struct irq_data *data)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ unsigned int line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+ unsigned long flags;
+ u16 m;
+
+ raw_spin_lock_irqsave(&ctrl->lock, flags);
+ m = ctrl->intr_mask[port];
+ m |= realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
+ ctrl->intr_mask[port] = m;
+ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m);
+ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
+}
+
+static void realtek_gpio_irq_mask(struct irq_data *data)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ unsigned int line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+ unsigned long flags;
+ u16 m;
+
+ raw_spin_lock_irqsave(&ctrl->lock, flags);
+ m = ctrl->intr_mask[port];
+ m &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
+ ctrl->intr_mask[port] = m;
+ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m);
+ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
+}
+
+static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ unsigned int line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+ unsigned long flags;
+ u16 type, t;
+
+ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_FALLING:
+ type = REALTEK_GPIO_IRQ_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ type = REALTEK_GPIO_IRQ_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ type = REALTEK_GPIO_IRQ_EDGE_BOTH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ irq_set_handler_locked(data, handle_edge_irq);
+
+ raw_spin_lock_irqsave(&ctrl->lock, flags);
+ t = ctrl->intr_type[port];
+ t &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
+ t |= realtek_gpio_imr_bits(port_pin, type);
+ ctrl->intr_type[port] = t;
+ realtek_gpio_write_imr(ctrl, port, t, ctrl->intr_mask[port]);
+ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return 0;
+}
+
+static void realtek_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ unsigned int lines_done;
+ unsigned int port_pin_count;
+ unsigned long status;
+ int offset;
+
+ chained_irq_enter(irq_chip, desc);
+
+ for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) {
+ 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 + lines_done);
+ }
+
+ chained_irq_exit(irq_chip, desc);
+}
+
+static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl,
+ unsigned int port, int cpu)
+{
+ return ctrl->cpumask_base + ctrl->port_offset_u8(port) +
+ REALTEK_GPIO_PORTS_PER_BANK * cpu;
+}
+
+static int realtek_gpio_irq_set_affinity(struct irq_data *data,
+ const struct cpumask *dest, bool force)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ unsigned int line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+ void __iomem *irq_cpu_mask;
+ unsigned long flags;
+ int cpu;
+ u8 v;
+
+ if (!ctrl->cpumask_base)
+ return -ENXIO;
+
+ raw_spin_lock_irqsave(&ctrl->lock, flags);
+
+ for_each_cpu(cpu, &ctrl->cpu_irq_maskable) {
+ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu);
+ v = ioread8(irq_cpu_mask);
+
+ if (cpumask_test_cpu(cpu, dest))
+ v |= BIT(port_pin);
+ else
+ v &= ~BIT(port_pin);
+
+ iowrite8(v, irq_cpu_mask);
+ }
+
+ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ irq_data_update_effective_affinity(data, dest);
+
+ return 0;
+}
+
+static int realtek_gpio_irq_init(struct gpio_chip *gc)
+{
+ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+ unsigned int port;
+ int cpu;
+
+ for (port = 0; (port * 8) < gc->ngpio; port++) {
+ realtek_gpio_write_imr(ctrl, port, 0, 0);
+ realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0));
+
+ for_each_cpu(cpu, &ctrl->cpu_irq_maskable)
+ iowrite8(GENMASK(7, 0), realtek_gpio_irq_cpu_mask(ctrl, port, cpu));
+ }
+
+ return 0;
+}
+
+static struct irq_chip realtek_gpio_irq_chip = {
+ .name = "realtek-otto-gpio",
+ .irq_ack = realtek_gpio_irq_ack,
+ .irq_mask = realtek_gpio_irq_mask,
+ .irq_unmask = realtek_gpio_irq_unmask,
+ .irq_set_type = realtek_gpio_irq_set_type,
+ .irq_set_affinity = realtek_gpio_irq_set_affinity,
+};
+
+static const struct of_device_id realtek_gpio_of_match[] = {
+ {
+ .compatible = "realtek,otto-gpio",
+ .data = (void *)GPIO_INTERRUPTS_DISABLED,
+ },
+ {
+ .compatible = "realtek,rtl8380-gpio",
+ },
+ {
+ .compatible = "realtek,rtl8390-gpio",
+ },
+ {
+ .compatible = "realtek,rtl9300-gpio",
+ .data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU)
+ },
+ {
+ .compatible = "realtek,rtl9310-gpio",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
+
+static int realtek_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned long bgpio_flags;
+ unsigned int dev_flags;
+ struct gpio_irq_chip *girq;
+ struct realtek_gpio_ctrl *ctrl;
+ struct resource *res;
+ u32 ngpios;
+ unsigned int nr_cpus;
+ int cpu, err, irq;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ dev_flags = (unsigned int) device_get_match_data(dev);
+
+ ngpios = REALTEK_GPIO_MAX;
+ device_property_read_u32(dev, "ngpios", &ngpios);
+
+ if (ngpios > REALTEK_GPIO_MAX) {
+ dev_err(&pdev->dev, "invalid ngpios (max. %d)\n",
+ REALTEK_GPIO_MAX);
+ return -EINVAL;
+ }
+
+ ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ctrl->base))
+ return PTR_ERR(ctrl->base);
+
+ raw_spin_lock_init(&ctrl->lock);
+
+ if (dev_flags & GPIO_PORTS_REVERSED) {
+ bgpio_flags = 0;
+ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8_rev;
+ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16_rev;
+ } else {
+ bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
+ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8;
+ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16;
+ }
+
+ err = bgpio_init(&ctrl->gc, dev, 4,
+ ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL,
+ ctrl->base + REALTEK_GPIO_REG_DIR, NULL,
+ bgpio_flags);
+ if (err) {
+ dev_err(dev, "unable to init generic GPIO");
+ return err;
+ }
+
+ ctrl->gc.ngpio = ngpios;
+ ctrl->gc.owner = THIS_MODULE;
+
+ irq = platform_get_irq_optional(pdev, 0);
+ if (!(dev_flags & GPIO_INTERRUPTS_DISABLED) && irq > 0) {
+ girq = &ctrl->gc.irq;
+ girq->chip = &realtek_gpio_irq_chip;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
+ girq->parent_handler = realtek_gpio_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(dev, girq->num_parents,
+ sizeof(*girq->parents), GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = irq;
+ girq->init_hw = realtek_gpio_irq_init;
+ }
+
+ cpumask_clear(&ctrl->cpu_irq_maskable);
+
+ if ((dev_flags & GPIO_INTERRUPTS_PER_CPU) && irq > 0) {
+ ctrl->cpumask_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
+ if (IS_ERR(ctrl->cpumask_base))
+ return dev_err_probe(dev, PTR_ERR(ctrl->cpumask_base),
+ "missing CPU IRQ mask registers");
+
+ nr_cpus = resource_size(res) / REALTEK_GPIO_PORTS_PER_BANK;
+ nr_cpus = min(nr_cpus, num_present_cpus());
+
+ for (cpu = 0; cpu < nr_cpus; cpu++)
+ cpumask_set_cpu(cpu, &ctrl->cpu_irq_maskable);
+ }
+
+ return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
+}
+
+static struct platform_driver realtek_gpio_driver = {
+ .driver = {
+ .name = "realtek-otto-gpio",
+ .of_match_table = realtek_gpio_of_match,
+ },
+ .probe = realtek_gpio_probe,
+};
+module_platform_driver(realtek_gpio_driver);
+
+MODULE_DESCRIPTION("Realtek Otto GPIO support");
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-reg.c b/drivers/gpio/gpio-reg.c
index fdc7a9d5b382..d35169bde25a 100644
--- a/drivers/gpio/gpio-reg.c
+++ b/drivers/gpio/gpio-reg.c
@@ -26,7 +26,8 @@ static int gpio_reg_get_direction(struct gpio_chip *gc, unsigned offset)
{
struct gpio_reg *r = to_gpio_reg(gc);
- return r->direction & BIT(offset) ? 1 : 0;
+ return r->direction & BIT(offset) ? GPIO_LINE_DIRECTION_IN :
+ GPIO_LINE_DIRECTION_OUT;
}
static int gpio_reg_direction_output(struct gpio_chip *gc, unsigned offset,
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
new file mode 100644
index 000000000000..6383136cbe59
--- /dev/null
+++ b/drivers/gpio/gpio-regmap.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * regmap based generic GPIO driver
+ *
+ * Copyright 2020 Michael Walle <michael@walle.cc>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+struct gpio_regmap {
+ struct device *parent;
+ struct regmap *regmap;
+ struct gpio_chip gpio_chip;
+
+ int reg_stride;
+ int ngpio_per_reg;
+ unsigned int reg_dat_base;
+ unsigned int reg_set_base;
+ unsigned int reg_clr_base;
+ unsigned int reg_dir_in_base;
+ unsigned int reg_dir_out_base;
+
+ int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
+ unsigned int offset, unsigned int *reg,
+ unsigned int *mask);
+
+ void *driver_data;
+};
+
+static unsigned int gpio_regmap_addr(unsigned int addr)
+{
+ if (addr == GPIO_REGMAP_ADDR_ZERO)
+ return 0;
+
+ return addr;
+}
+
+static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
+ unsigned int base, unsigned int offset,
+ unsigned int *reg, unsigned int *mask)
+{
+ unsigned int line = offset % gpio->ngpio_per_reg;
+ unsigned int stride = offset / gpio->ngpio_per_reg;
+
+ *reg = base + stride * gpio->reg_stride;
+ *mask = BIT(line);
+
+ return 0;
+}
+
+static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpio_regmap *gpio = gpiochip_get_data(chip);
+ unsigned int base, val, reg, mask;
+ int ret;
+
+ /* we might not have an output register if we are input only */
+ if (gpio->reg_dat_base)
+ base = gpio_regmap_addr(gpio->reg_dat_base);
+ else
+ base = gpio_regmap_addr(gpio->reg_set_base);
+
+ ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(gpio->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & mask);
+}
+
+static void gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
+ int val)
+{
+ struct gpio_regmap *gpio = gpiochip_get_data(chip);
+ unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
+ unsigned int reg, mask;
+
+ gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ if (val)
+ regmap_update_bits(gpio->regmap, reg, mask, mask);
+ else
+ regmap_update_bits(gpio->regmap, reg, mask, 0);
+}
+
+static void gpio_regmap_set_with_clear(struct gpio_chip *chip,
+ unsigned int offset, int val)
+{
+ struct gpio_regmap *gpio = gpiochip_get_data(chip);
+ unsigned int base, reg, mask;
+
+ if (val)
+ base = gpio_regmap_addr(gpio->reg_set_base);
+ else
+ base = gpio_regmap_addr(gpio->reg_clr_base);
+
+ gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ regmap_write(gpio->regmap, reg, mask);
+}
+
+static int gpio_regmap_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct gpio_regmap *gpio = gpiochip_get_data(chip);
+ unsigned int base, val, reg, mask;
+ int invert, ret;
+
+ if (gpio->reg_dir_out_base) {
+ base = gpio_regmap_addr(gpio->reg_dir_out_base);
+ invert = 0;
+ } else if (gpio->reg_dir_in_base) {
+ base = gpio_regmap_addr(gpio->reg_dir_in_base);
+ invert = 1;
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(gpio->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ if (!!(val & mask) ^ invert)
+ return GPIO_LINE_DIRECTION_OUT;
+ else
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int gpio_regmap_set_direction(struct gpio_chip *chip,
+ unsigned int offset, bool output)
+{
+ struct gpio_regmap *gpio = gpiochip_get_data(chip);
+ unsigned int base, val, reg, mask;
+ int invert, ret;
+
+ if (gpio->reg_dir_out_base) {
+ base = gpio_regmap_addr(gpio->reg_dir_out_base);
+ invert = 0;
+ } else if (gpio->reg_dir_in_base) {
+ base = gpio_regmap_addr(gpio->reg_dir_in_base);
+ invert = 1;
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ if (ret)
+ return ret;
+
+ if (invert)
+ val = output ? 0 : mask;
+ else
+ val = output ? mask : 0;
+
+ return regmap_update_bits(gpio->regmap, reg, mask, val);
+}
+
+static int gpio_regmap_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return gpio_regmap_set_direction(chip, offset, false);
+}
+
+static int gpio_regmap_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ gpio_regmap_set(chip, offset, value);
+
+ return gpio_regmap_set_direction(chip, offset, true);
+}
+
+void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
+{
+ return gpio->driver_data;
+}
+EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
+
+/**
+ * gpio_regmap_register() - Register a generic regmap GPIO controller
+ * @config: configuration for gpio_regmap
+ *
+ * Return: A pointer to the registered gpio_regmap or ERR_PTR error value.
+ */
+struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)
+{
+ struct gpio_regmap *gpio;
+ struct gpio_chip *chip;
+ int ret;
+
+ if (!config->parent)
+ return ERR_PTR(-EINVAL);
+
+ if (!config->ngpio)
+ return ERR_PTR(-EINVAL);
+
+ /* we need at least one */
+ if (!config->reg_dat_base && !config->reg_set_base)
+ return ERR_PTR(-EINVAL);
+
+ /* if we have a direction register we need both input and output */
+ if ((config->reg_dir_out_base || config->reg_dir_in_base) &&
+ (!config->reg_dat_base || !config->reg_set_base))
+ return ERR_PTR(-EINVAL);
+
+ /* we don't support having both registers simultaneously for now */
+ if (config->reg_dir_out_base && config->reg_dir_in_base)
+ return ERR_PTR(-EINVAL);
+
+ gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return ERR_PTR(-ENOMEM);
+
+ gpio->parent = config->parent;
+ gpio->driver_data = config->drvdata;
+ gpio->regmap = config->regmap;
+ gpio->ngpio_per_reg = config->ngpio_per_reg;
+ gpio->reg_stride = config->reg_stride;
+ gpio->reg_mask_xlate = config->reg_mask_xlate;
+ gpio->reg_dat_base = config->reg_dat_base;
+ gpio->reg_set_base = config->reg_set_base;
+ gpio->reg_clr_base = config->reg_clr_base;
+ gpio->reg_dir_in_base = config->reg_dir_in_base;
+ gpio->reg_dir_out_base = config->reg_dir_out_base;
+
+ /* if not set, assume there is only one register */
+ if (!gpio->ngpio_per_reg)
+ gpio->ngpio_per_reg = config->ngpio;
+
+ /* if not set, assume they are consecutive */
+ if (!gpio->reg_stride)
+ gpio->reg_stride = 1;
+
+ if (!gpio->reg_mask_xlate)
+ gpio->reg_mask_xlate = gpio_regmap_simple_xlate;
+
+ chip = &gpio->gpio_chip;
+ chip->parent = config->parent;
+ chip->fwnode = config->fwnode;
+ chip->base = -1;
+ chip->ngpio = config->ngpio;
+ chip->names = config->names;
+ chip->label = config->label ?: dev_name(config->parent);
+
+ /*
+ * If our regmap is fast_io we should probably set can_sleep to false.
+ * Right now, the regmap doesn't save this property, nor is there any
+ * access function for it.
+ * The only regmap type which uses fast_io is regmap-mmio. For now,
+ * assume a safe default of true here.
+ */
+ chip->can_sleep = true;
+
+ chip->get = gpio_regmap_get;
+ if (gpio->reg_set_base && gpio->reg_clr_base)
+ chip->set = gpio_regmap_set_with_clear;
+ else if (gpio->reg_set_base)
+ chip->set = gpio_regmap_set;
+
+ if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) {
+ chip->get_direction = gpio_regmap_get_direction;
+ chip->direction_input = gpio_regmap_direction_input;
+ chip->direction_output = gpio_regmap_direction_output;
+ }
+
+ ret = gpiochip_add_data(chip, gpio);
+ if (ret < 0)
+ goto err_free_gpio;
+
+ if (config->irq_domain) {
+ ret = gpiochip_irqchip_add_domain(chip, config->irq_domain);
+ if (ret)
+ goto err_remove_gpiochip;
+ }
+
+ return gpio;
+
+err_remove_gpiochip:
+ gpiochip_remove(chip);
+err_free_gpio:
+ kfree(gpio);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(gpio_regmap_register);
+
+/**
+ * gpio_regmap_unregister() - Unregister a generic regmap GPIO controller
+ * @gpio: gpio_regmap device to unregister
+ */
+void gpio_regmap_unregister(struct gpio_regmap *gpio)
+{
+ gpiochip_remove(&gpio->gpio_chip);
+ kfree(gpio);
+}
+EXPORT_SYMBOL_GPL(gpio_regmap_unregister);
+
+static void devm_gpio_regmap_unregister(void *res)
+{
+ gpio_regmap_unregister(res);
+}
+
+/**
+ * devm_gpio_regmap_register() - resource managed gpio_regmap_register()
+ * @dev: device that is registering this GPIO device
+ * @config: configuration for gpio_regmap
+ *
+ * Managed gpio_regmap_register(). For generic regmap GPIO device registered by
+ * this function, gpio_regmap_unregister() is automatically called on driver
+ * detach. See gpio_regmap_register() for more information.
+ *
+ * Return: A pointer to the registered gpio_regmap or ERR_PTR error value.
+ */
+struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
+ const struct gpio_regmap_config *config)
+{
+ struct gpio_regmap *gpio;
+ int ret;
+
+ gpio = gpio_regmap_register(config);
+
+ if (IS_ERR(gpio))
+ return gpio;
+
+ ret = devm_add_action_or_reset(dev, devm_gpio_regmap_unregister, gpio);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return gpio;
+}
+EXPORT_SYMBOL_GPL(devm_gpio_regmap_register);
+
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_DESCRIPTION("GPIO generic regmap driver core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
new file mode 100644
index 000000000000..e342a6dc4c6c
--- /dev/null
+++ b/drivers/gpio/gpio-rockchip.c
@@ -0,0 +1,823 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/regmap.h>
+
+#include "../pinctrl/core.h"
+#include "../pinctrl/pinctrl-rockchip.h"
+
+#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */
+#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */
+
+static const struct rockchip_gpio_regs gpio_regs_v1 = {
+ .port_dr = 0x00,
+ .port_ddr = 0x04,
+ .int_en = 0x30,
+ .int_mask = 0x34,
+ .int_type = 0x38,
+ .int_polarity = 0x3c,
+ .int_status = 0x40,
+ .int_rawstatus = 0x44,
+ .debounce = 0x48,
+ .port_eoi = 0x4c,
+ .ext_port = 0x50,
+};
+
+static const struct rockchip_gpio_regs gpio_regs_v2 = {
+ .port_dr = 0x00,
+ .port_ddr = 0x08,
+ .int_en = 0x10,
+ .int_mask = 0x18,
+ .int_type = 0x20,
+ .int_polarity = 0x28,
+ .int_bothedge = 0x30,
+ .int_status = 0x50,
+ .int_rawstatus = 0x58,
+ .debounce = 0x38,
+ .dbclk_div_en = 0x40,
+ .dbclk_div_con = 0x48,
+ .port_eoi = 0x60,
+ .ext_port = 0x70,
+ .version_id = 0x78,
+};
+
+static inline void gpio_writel_v2(u32 val, void __iomem *reg)
+{
+ writel((val & 0xffff) | 0xffff0000, reg);
+ writel((val >> 16) | 0xffff0000, reg + 0x4);
+}
+
+static inline u32 gpio_readl_v2(void __iomem *reg)
+{
+ return readl(reg + 0x4) << 16 | readl(reg);
+}
+
+static inline void rockchip_gpio_writel(struct rockchip_pin_bank *bank,
+ u32 value, unsigned int offset)
+{
+ void __iomem *reg = bank->reg_base + offset;
+
+ if (bank->gpio_type == GPIO_TYPE_V2)
+ gpio_writel_v2(value, reg);
+ else
+ writel(value, reg);
+}
+
+static inline u32 rockchip_gpio_readl(struct rockchip_pin_bank *bank,
+ unsigned int offset)
+{
+ void __iomem *reg = bank->reg_base + offset;
+ u32 value;
+
+ if (bank->gpio_type == GPIO_TYPE_V2)
+ value = gpio_readl_v2(reg);
+ else
+ value = readl(reg);
+
+ return value;
+}
+
+static inline void rockchip_gpio_writel_bit(struct rockchip_pin_bank *bank,
+ u32 bit, u32 value,
+ unsigned int offset)
+{
+ void __iomem *reg = bank->reg_base + offset;
+ u32 data;
+
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ if (value)
+ data = BIT(bit % 16) | BIT(bit % 16 + 16);
+ else
+ data = BIT(bit % 16 + 16);
+ writel(data, bit >= 16 ? reg + 0x4 : reg);
+ } else {
+ data = readl(reg);
+ data &= ~BIT(bit);
+ if (value)
+ data |= BIT(bit);
+ writel(data, reg);
+ }
+}
+
+static inline u32 rockchip_gpio_readl_bit(struct rockchip_pin_bank *bank,
+ u32 bit, unsigned int offset)
+{
+ void __iomem *reg = bank->reg_base + offset;
+ u32 data;
+
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ data = readl(bit >= 16 ? reg + 0x4 : reg);
+ data >>= bit % 16;
+ } else {
+ data = readl(reg);
+ data >>= bit;
+ }
+
+ return data & (0x1);
+}
+
+static int rockchip_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
+ u32 data;
+
+ data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr);
+ if (data)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int rockchip_gpio_set_direction(struct gpio_chip *chip,
+ unsigned int offset, bool input)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
+ unsigned long flags;
+ u32 data = input ? 0 : 1;
+
+ raw_spin_lock_irqsave(&bank->slock, flags);
+ rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr);
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ return 0;
+}
+
+static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&bank->slock, flags);
+ rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr);
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+}
+
+static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
+ u32 data;
+
+ data = readl(bank->reg_base + bank->gpio_regs->ext_port);
+ data >>= offset;
+ data &= 1;
+
+ return data;
+}
+
+static int rockchip_gpio_set_debounce(struct gpio_chip *gc,
+ unsigned int offset,
+ unsigned int debounce)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
+ const struct rockchip_gpio_regs *reg = bank->gpio_regs;
+ unsigned long flags, div_reg, freq, max_debounce;
+ bool div_debounce_support;
+ unsigned int cur_div_reg;
+ u64 div;
+
+ if (bank->gpio_type == GPIO_TYPE_V2 && !IS_ERR(bank->db_clk)) {
+ div_debounce_support = true;
+ freq = clk_get_rate(bank->db_clk);
+ max_debounce = (GENMASK(23, 0) + 1) * 2 * 1000000 / freq;
+ if (debounce > max_debounce)
+ return -EINVAL;
+
+ div = debounce * freq;
+ div_reg = DIV_ROUND_CLOSEST_ULL(div, 2 * USEC_PER_SEC) - 1;
+ } else {
+ div_debounce_support = false;
+ }
+
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ /* Only the v1 needs to configure div_en and div_con for dbclk */
+ if (debounce) {
+ if (div_debounce_support) {
+ /* Configure the max debounce from consumers */
+ cur_div_reg = readl(bank->reg_base +
+ reg->dbclk_div_con);
+ if (cur_div_reg < div_reg)
+ writel(div_reg, bank->reg_base +
+ reg->dbclk_div_con);
+ rockchip_gpio_writel_bit(bank, offset, 1,
+ reg->dbclk_div_en);
+ }
+
+ rockchip_gpio_writel_bit(bank, offset, 1, reg->debounce);
+ } else {
+ if (div_debounce_support)
+ rockchip_gpio_writel_bit(bank, offset, 0,
+ reg->dbclk_div_en);
+
+ rockchip_gpio_writel_bit(bank, offset, 0, reg->debounce);
+ }
+
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ /* Enable or disable dbclk at last */
+ if (div_debounce_support) {
+ if (debounce)
+ clk_prepare_enable(bank->db_clk);
+ else
+ clk_disable_unprepare(bank->db_clk);
+ }
+
+ return 0;
+}
+
+static int rockchip_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return rockchip_gpio_set_direction(gc, offset, true);
+}
+
+static int rockchip_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ rockchip_gpio_set(gc, offset, value);
+
+ return rockchip_gpio_set_direction(gc, offset, false);
+}
+
+/*
+ * gpiolib set_config callback function. The setting of the pin
+ * mux function as 'gpio output' will be handled by the pinctrl subsystem
+ * interface.
+ */
+static int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
+ unsigned long config)
+{
+ enum pin_config_param param = pinconf_to_config_param(config);
+
+ switch (param) {
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ rockchip_gpio_set_debounce(gc, offset, true);
+ /*
+ * Rockchip's gpio could only support up to one period
+ * of the debounce clock(pclk), which is far away from
+ * satisftying the requirement, as pclk is usually near
+ * 100MHz shared by all peripherals. So the fact is it
+ * has crippled debounce capability could only be useful
+ * to prevent any spurious glitches from waking up the system
+ * if the gpio is conguired as wakeup interrupt source. Let's
+ * still return -ENOTSUPP as before, to make sure the caller
+ * of gpiod_set_debounce won't change its behaviour.
+ */
+ return -ENOTSUPP;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+/*
+ * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin
+ * and a virtual IRQ, if not already present.
+ */
+static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
+ unsigned int virq;
+
+ if (!bank->domain)
+ return -ENXIO;
+
+ virq = irq_create_mapping(bank->domain, offset);
+
+ return (virq) ? : -ENXIO;
+}
+
+static const struct gpio_chip rockchip_gpiolib_chip = {
+ .request = gpiochip_generic_request,
+ .free = gpiochip_generic_free,
+ .set = rockchip_gpio_set,
+ .get = rockchip_gpio_get,
+ .get_direction = rockchip_gpio_get_direction,
+ .direction_input = rockchip_gpio_direction_input,
+ .direction_output = rockchip_gpio_direction_output,
+ .set_config = rockchip_gpio_set_config,
+ .to_irq = rockchip_gpio_to_irq,
+ .owner = THIS_MODULE,
+};
+
+static void rockchip_irq_demux(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc);
+ u32 pend;
+
+ dev_dbg(bank->dev, "got irq for bank %s\n", bank->name);
+
+ chained_irq_enter(chip, desc);
+
+ pend = readl_relaxed(bank->reg_base + bank->gpio_regs->int_status);
+
+ while (pend) {
+ unsigned int irq, virq;
+
+ irq = __ffs(pend);
+ pend &= ~BIT(irq);
+ virq = irq_find_mapping(bank->domain, irq);
+
+ if (!virq) {
+ dev_err(bank->dev, "unmapped irq %d\n", irq);
+ continue;
+ }
+
+ dev_dbg(bank->dev, "handling irq %d\n", irq);
+
+ /*
+ * Triggering IRQ on both rising and falling edge
+ * needs manual intervention.
+ */
+ if (bank->toggle_edge_mode & BIT(irq)) {
+ u32 data, data_old, polarity;
+ unsigned long flags;
+
+ data = readl_relaxed(bank->reg_base +
+ bank->gpio_regs->ext_port);
+ do {
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ polarity = readl_relaxed(bank->reg_base +
+ bank->gpio_regs->int_polarity);
+ if (data & BIT(irq))
+ polarity &= ~BIT(irq);
+ else
+ polarity |= BIT(irq);
+ writel(polarity,
+ bank->reg_base +
+ bank->gpio_regs->int_polarity);
+
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ data_old = data;
+ data = readl_relaxed(bank->reg_base +
+ bank->gpio_regs->ext_port);
+ } while ((data & BIT(irq)) != (data_old & BIT(irq)));
+ }
+
+ generic_handle_irq(virq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+ u32 mask = BIT(d->hwirq);
+ u32 polarity;
+ u32 level;
+ u32 data;
+ unsigned long flags;
+ int ret = 0;
+
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ rockchip_gpio_writel_bit(bank, d->hwirq, 0,
+ bank->gpio_regs->port_ddr);
+
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ irq_set_handler_locked(d, handle_edge_irq);
+ else
+ irq_set_handler_locked(d, handle_level_irq);
+
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ level = rockchip_gpio_readl(bank, bank->gpio_regs->int_type);
+ polarity = rockchip_gpio_readl(bank, bank->gpio_regs->int_polarity);
+
+ if (type == IRQ_TYPE_EDGE_BOTH) {
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ rockchip_gpio_writel_bit(bank, d->hwirq, 1,
+ bank->gpio_regs->int_bothedge);
+ goto out;
+ } else {
+ bank->toggle_edge_mode |= mask;
+ level |= mask;
+
+ /*
+ * Determine gpio state. If 1 next interrupt should be
+ * falling otherwise rising.
+ */
+ data = readl(bank->reg_base + bank->gpio_regs->ext_port);
+ if (data & mask)
+ polarity &= ~mask;
+ else
+ polarity |= mask;
+ }
+ } else {
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ rockchip_gpio_writel_bit(bank, d->hwirq, 0,
+ bank->gpio_regs->int_bothedge);
+ } else {
+ bank->toggle_edge_mode &= ~mask;
+ }
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ level |= mask;
+ polarity |= mask;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ level |= mask;
+ polarity &= ~mask;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ level &= ~mask;
+ polarity |= mask;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ level &= ~mask;
+ polarity &= ~mask;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ rockchip_gpio_writel(bank, level, bank->gpio_regs->int_type);
+ rockchip_gpio_writel(bank, polarity, bank->gpio_regs->int_polarity);
+out:
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ return ret;
+}
+
+static int rockchip_irq_reqres(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+
+ return gpiochip_reqres_irq(&bank->gpio_chip, d->hwirq);
+}
+
+static void rockchip_irq_relres(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+
+ gpiochip_relres_irq(&bank->gpio_chip, d->hwirq);
+}
+
+static void rockchip_irq_suspend(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+
+ bank->saved_masks = irq_reg_readl(gc, bank->gpio_regs->int_mask);
+ irq_reg_writel(gc, ~gc->wake_active, bank->gpio_regs->int_mask);
+}
+
+static void rockchip_irq_resume(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+
+ irq_reg_writel(gc, bank->saved_masks, bank->gpio_regs->int_mask);
+}
+
+static void rockchip_irq_enable(struct irq_data *d)
+{
+ irq_gc_mask_clr_bit(d);
+}
+
+static void rockchip_irq_disable(struct irq_data *d)
+{
+ irq_gc_mask_set_bit(d);
+}
+
+static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ struct irq_chip_generic *gc;
+ int ret;
+
+ bank->domain = irq_domain_add_linear(bank->of_node, 32,
+ &irq_generic_chip_ops, NULL);
+ if (!bank->domain) {
+ dev_warn(bank->dev, "could not init irq domain for bank %s\n",
+ bank->name);
+ return -EINVAL;
+ }
+
+ ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1,
+ "rockchip_gpio_irq",
+ handle_level_irq,
+ clr, 0, 0);
+ if (ret) {
+ dev_err(bank->dev, "could not alloc generic chips for bank %s\n",
+ bank->name);
+ irq_domain_remove(bank->domain);
+ return -EINVAL;
+ }
+
+ gc = irq_get_domain_generic_chip(bank->domain, 0);
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ gc->reg_writel = gpio_writel_v2;
+ gc->reg_readl = gpio_readl_v2;
+ }
+
+ gc->reg_base = bank->reg_base;
+ gc->private = bank;
+ gc->chip_types[0].regs.mask = bank->gpio_regs->int_mask;
+ gc->chip_types[0].regs.ack = bank->gpio_regs->port_eoi;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
+ gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
+ gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
+ gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
+ gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
+ gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
+ gc->chip_types[0].chip.irq_request_resources = rockchip_irq_reqres;
+ gc->chip_types[0].chip.irq_release_resources = rockchip_irq_relres;
+ gc->wake_enabled = IRQ_MSK(bank->nr_pins);
+
+ /*
+ * Linux assumes that all interrupts start out disabled/masked.
+ * Our driver only uses the concept of masked and always keeps
+ * things enabled, so for us that's all masked and all enabled.
+ */
+ rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_mask);
+ rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->port_eoi);
+ rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_en);
+ gc->mask_cache = 0xffffffff;
+
+ irq_set_chained_handler_and_data(bank->irq,
+ rockchip_irq_demux, bank);
+
+ return 0;
+}
+
+static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
+{
+ struct gpio_chip *gc;
+ int ret;
+
+ bank->gpio_chip = rockchip_gpiolib_chip;
+
+ gc = &bank->gpio_chip;
+ gc->base = bank->pin_base;
+ gc->ngpio = bank->nr_pins;
+ gc->label = bank->name;
+ gc->parent = bank->dev;
+
+ ret = gpiochip_add_data(gc, bank);
+ if (ret) {
+ dev_err(bank->dev, "failed to add gpiochip %s, %d\n",
+ gc->label, ret);
+ return ret;
+ }
+
+ /*
+ * For DeviceTree-supported systems, the gpio core checks the
+ * pinctrl's device node for the "gpio-ranges" property.
+ * If it is present, it takes care of adding the pin ranges
+ * for the driver. In this case the driver can skip ahead.
+ *
+ * In order to remain compatible with older, existing DeviceTree
+ * files which don't set the "gpio-ranges" property or systems that
+ * utilize ACPI the driver has to call gpiochip_add_pin_range().
+ */
+ if (!of_property_read_bool(bank->of_node, "gpio-ranges")) {
+ struct device_node *pctlnp = of_get_parent(bank->of_node);
+ struct pinctrl_dev *pctldev = NULL;
+
+ if (!pctlnp)
+ return -ENODATA;
+
+ pctldev = of_pinctrl_get(pctlnp);
+ if (!pctldev)
+ return -ENODEV;
+
+ ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0,
+ gc->base, gc->ngpio);
+ if (ret) {
+ dev_err(bank->dev, "Failed to add pin range\n");
+ goto fail;
+ }
+ }
+
+ ret = rockchip_interrupts_register(bank);
+ if (ret) {
+ dev_err(bank->dev, "failed to register interrupt, %d\n", ret);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ gpiochip_remove(&bank->gpio_chip);
+
+ return ret;
+}
+
+static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
+{
+ struct resource res;
+ int id = 0;
+
+ if (of_address_to_resource(bank->of_node, 0, &res)) {
+ dev_err(bank->dev, "cannot find IO resource for bank\n");
+ return -ENOENT;
+ }
+
+ bank->reg_base = devm_ioremap_resource(bank->dev, &res);
+ if (IS_ERR(bank->reg_base))
+ return PTR_ERR(bank->reg_base);
+
+ bank->irq = irq_of_parse_and_map(bank->of_node, 0);
+ if (!bank->irq)
+ return -EINVAL;
+
+ bank->clk = of_clk_get(bank->of_node, 0);
+ if (IS_ERR(bank->clk))
+ return PTR_ERR(bank->clk);
+
+ clk_prepare_enable(bank->clk);
+ id = readl(bank->reg_base + gpio_regs_v2.version_id);
+
+ /* If not gpio v2, that is default to v1. */
+ if (id == GPIO_TYPE_V2) {
+ bank->gpio_regs = &gpio_regs_v2;
+ bank->gpio_type = GPIO_TYPE_V2;
+ bank->db_clk = of_clk_get(bank->of_node, 1);
+ if (IS_ERR(bank->db_clk)) {
+ dev_err(bank->dev, "cannot find debounce clk\n");
+ clk_disable_unprepare(bank->clk);
+ return -EINVAL;
+ }
+ } else {
+ bank->gpio_regs = &gpio_regs_v1;
+ bank->gpio_type = GPIO_TYPE_V1;
+ }
+
+ return 0;
+}
+
+static struct rockchip_pin_bank *
+rockchip_gpio_find_bank(struct pinctrl_dev *pctldev, int id)
+{
+ struct rockchip_pinctrl *info;
+ struct rockchip_pin_bank *bank;
+ int i, found = 0;
+
+ info = pinctrl_dev_get_drvdata(pctldev);
+ bank = info->ctrl->pin_banks;
+ for (i = 0; i < info->ctrl->nr_banks; i++, bank++) {
+ if (bank->bank_num == id) {
+ found = 1;
+ break;
+ }
+ }
+
+ return found ? bank : NULL;
+}
+
+static int rockchip_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *pctlnp = of_get_parent(np);
+ struct pinctrl_dev *pctldev = NULL;
+ struct rockchip_pin_bank *bank = NULL;
+ struct rockchip_pin_deferred *cfg;
+ static int gpio;
+ int id, ret;
+
+ if (!np || !pctlnp)
+ return -ENODEV;
+
+ pctldev = of_pinctrl_get(pctlnp);
+ if (!pctldev)
+ return -EPROBE_DEFER;
+
+ id = of_alias_get_id(np, "gpio");
+ if (id < 0)
+ id = gpio++;
+
+ bank = rockchip_gpio_find_bank(pctldev, id);
+ if (!bank)
+ return -EINVAL;
+
+ bank->dev = dev;
+ bank->of_node = np;
+
+ raw_spin_lock_init(&bank->slock);
+
+ ret = rockchip_get_bank_data(bank);
+ if (ret)
+ return ret;
+
+ /*
+ * Prevent clashes with a deferred output setting
+ * being added right at this moment.
+ */
+ mutex_lock(&bank->deferred_lock);
+
+ ret = rockchip_gpiolib_register(bank);
+ if (ret) {
+ clk_disable_unprepare(bank->clk);
+ mutex_unlock(&bank->deferred_lock);
+ return ret;
+ }
+
+ while (!list_empty(&bank->deferred_pins)) {
+ cfg = list_first_entry(&bank->deferred_pins,
+ struct rockchip_pin_deferred, head);
+ list_del(&cfg->head);
+
+ switch (cfg->param) {
+ case PIN_CONFIG_OUTPUT:
+ ret = rockchip_gpio_direction_output(&bank->gpio_chip, cfg->pin, cfg->arg);
+ if (ret)
+ dev_warn(dev, "setting output pin %u to %u failed\n", cfg->pin,
+ cfg->arg);
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ ret = rockchip_gpio_direction_input(&bank->gpio_chip, cfg->pin);
+ if (ret)
+ dev_warn(dev, "setting input pin %u failed\n", cfg->pin);
+ break;
+ default:
+ dev_warn(dev, "unknown deferred config param %d\n", cfg->param);
+ break;
+ }
+ kfree(cfg);
+ }
+
+ mutex_unlock(&bank->deferred_lock);
+
+ platform_set_drvdata(pdev, bank);
+ dev_info(dev, "probed %pOF\n", np);
+
+ return 0;
+}
+
+static int rockchip_gpio_remove(struct platform_device *pdev)
+{
+ struct rockchip_pin_bank *bank = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(bank->clk);
+ gpiochip_remove(&bank->gpio_chip);
+
+ return 0;
+}
+
+static const struct of_device_id rockchip_gpio_match[] = {
+ { .compatible = "rockchip,gpio-bank", },
+ { .compatible = "rockchip,rk3188-gpio-bank0" },
+ { },
+};
+
+static struct platform_driver rockchip_gpio_driver = {
+ .probe = rockchip_gpio_probe,
+ .remove = rockchip_gpio_remove,
+ .driver = {
+ .name = "rockchip-gpio",
+ .of_match_table = rockchip_gpio_match,
+ },
+};
+
+static int __init rockchip_gpio_init(void)
+{
+ return platform_driver_register(&rockchip_gpio_driver);
+}
+postcore_initcall(rockchip_gpio_init);
+
+static void __exit rockchip_gpio_exit(void)
+{
+ platform_driver_unregister(&rockchip_gpio_driver);
+}
+module_exit(rockchip_gpio_exit);
+
+MODULE_DESCRIPTION("Rockchip gpio driver");
+MODULE_ALIAS("platform:rockchip-gpio");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, rockchip_gpio_match);
diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c
index 46b7cf23fb0f..edff5e81489f 100644
--- a/drivers/gpio/gpio-sa1100.c
+++ b/drivers/gpio/gpio-sa1100.c
@@ -53,7 +53,10 @@ static int sa1100_get_direction(struct gpio_chip *chip, unsigned offset)
{
void __iomem *gpdr = sa1100_gpio_chip(chip)->membase + R_GPDR;
- return !(readl_relaxed(gpdr) & BIT(offset));
+ if (readl_relaxed(gpdr) & BIT(offset))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset)
diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c
index 7d718557092e..3e95da717fc9 100644
--- a/drivers/gpio/gpio-sama5d2-piobu.c
+++ b/drivers/gpio/gpio-sama5d2-piobu.c
@@ -49,7 +49,7 @@ struct sama5d2_piobu {
struct regmap *regmap;
};
-/**
+/*
* sama5d2_piobu_setup_pin() - prepares a pin for set_direction call
*
* Do not consider pin for tamper detection (normal and backup modes)
@@ -73,7 +73,7 @@ static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin)
return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0);
}
-/**
+/*
* sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register
*/
static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin,
@@ -88,7 +88,7 @@ static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin,
return regmap_update_bits(piobu->regmap, reg, mask, value);
}
-/**
+/*
* sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU
* register
*/
@@ -108,7 +108,7 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin,
return val & mask;
}
-/**
+/*
* sama5d2_piobu_get_direction() - gpiochip get_direction
*/
static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
@@ -119,10 +119,11 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
if (ret < 0)
return ret;
- return (ret == PIOBU_IN) ? 1 : 0;
+ return (ret == PIOBU_IN) ? GPIO_LINE_DIRECTION_IN :
+ GPIO_LINE_DIRECTION_OUT;
}
-/**
+/*
* sama5d2_piobu_direction_input() - gpiochip direction_input
*/
static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
@@ -131,7 +132,7 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN);
}
-/**
+/*
* sama5d2_piobu_direction_output() - gpiochip direction_output
*/
static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
@@ -146,7 +147,7 @@ static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
val);
}
-/**
+/*
* sama5d2_piobu_get() - gpiochip get
*/
static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
@@ -154,9 +155,9 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
/* if pin is input, read value from PDS else read from SOD */
int ret = sama5d2_piobu_get_direction(chip, pin);
- if (ret == 1)
+ if (ret == GPIO_LINE_DIRECTION_IN)
ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS);
- else if (!ret)
+ else if (ret == GPIO_LINE_DIRECTION_OUT)
ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD);
if (ret < 0)
@@ -165,7 +166,7 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
return !!ret;
}
-/**
+/*
* sama5d2_piobu_set() - gpiochip set
*/
static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin,
@@ -191,7 +192,6 @@ static int sama5d2_piobu_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, piobu);
piobu->chip.label = pdev->name;
piobu->chip.parent = &pdev->dev;
- piobu->chip.of_node = pdev->dev.of_node;
piobu->chip.owner = THIS_MODULE,
piobu->chip.get_direction = sama5d2_piobu_get_direction,
piobu->chip.direction_input = sama5d2_piobu_direction_input,
@@ -243,7 +243,6 @@ static struct platform_driver sama5d2_piobu_driver = {
module_platform_driver(sama5d2_piobu_driver);
-MODULE_VERSION("1.0");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver");
MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>");
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index fb143f28c386..8a83f7bf4382 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -7,46 +7,67 @@
*/
#include <linux/acpi.h>
+#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci_ids.h>
#include <linux/platform_device.h>
+#include <linux/types.h>
#define GEN 0x00
#define GIO 0x04
#define GLV 0x08
+#define GTPE 0x0c
+#define GTNE 0x10
+#define GGPE 0x14
+#define GSMI 0x18
+#define GTS 0x1c
+
+#define CORE_BANK_OFFSET 0x00
+#define RESUME_BANK_OFFSET 0x20
+
+/*
+ * iLB datasheet describes GPE0BLK registers, in particular GPE0E.GPIO bit.
+ * Document Number: 328195-001
+ */
+#define GPE0E_GPIO 14
struct sch_gpio {
struct gpio_chip chip;
spinlock_t lock;
unsigned short iobase;
unsigned short resume_base;
+
+ /* GPE handling */
+ u32 gpe;
+ acpi_gpe_handler gpe_handler;
};
-static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio,
- unsigned reg)
+static unsigned int sch_gpio_offset(struct sch_gpio *sch, unsigned int gpio,
+ unsigned int reg)
{
- unsigned base = 0;
+ unsigned int base = CORE_BANK_OFFSET;
if (gpio >= sch->resume_base) {
gpio -= sch->resume_base;
- base += 0x20;
+ base = RESUME_BANK_OFFSET;
}
return base + reg + gpio / 8;
}
-static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
+static unsigned int sch_gpio_bit(struct sch_gpio *sch, unsigned int gpio)
{
if (gpio >= sch->resume_base)
gpio -= sch->resume_base;
return gpio % 8;
}
-static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned gpio, unsigned reg)
+static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned int gpio, unsigned int reg)
{
unsigned short offset, bit;
u8 reg_val;
@@ -59,7 +80,7 @@ static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned gpio, unsigned reg)
return reg_val;
}
-static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned gpio, unsigned reg,
+static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned int gpio, unsigned int reg,
int val)
{
unsigned short offset, bit;
@@ -76,44 +97,48 @@ static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned gpio, unsigned reg,
outb((reg_val & ~BIT(bit)), sch->iobase + offset);
}
-static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
+static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num)
{
struct sch_gpio *sch = gpiochip_get_data(gc);
+ unsigned long flags;
- spin_lock(&sch->lock);
+ spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GIO, 1);
- spin_unlock(&sch->lock);
+ spin_unlock_irqrestore(&sch->lock, flags);
return 0;
}
-static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
+static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num)
{
struct sch_gpio *sch = gpiochip_get_data(gc);
+
return sch_gpio_reg_get(sch, gpio_num, GLV);
}
-static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
+static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val)
{
struct sch_gpio *sch = gpiochip_get_data(gc);
+ unsigned long flags;
- spin_lock(&sch->lock);
+ spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GLV, val);
- spin_unlock(&sch->lock);
+ spin_unlock_irqrestore(&sch->lock, flags);
}
-static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
+static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num,
int val)
{
struct sch_gpio *sch = gpiochip_get_data(gc);
+ unsigned long flags;
- spin_lock(&sch->lock);
+ spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GIO, 0);
- spin_unlock(&sch->lock);
+ spin_unlock_irqrestore(&sch->lock, flags);
/*
* according to the datasheet, writing to the level register has no
* effect when GPIO is programmed as input.
- * Actually the the level register is read-only when configured as input.
+ * Actually the level register is read-only when configured as input.
* Thus presetting the output level before switching to output is _NOT_ possible.
* Hence we set the level after configuring the GPIO as output.
* But we cannot prevent a short low pulse if direction is set to high
@@ -123,11 +148,14 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
return 0;
}
-static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned gpio_num)
+static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num)
{
struct sch_gpio *sch = gpiochip_get_data(gc);
- return sch_gpio_reg_get(sch, gpio_num, GIO);
+ if (sch_gpio_reg_get(sch, gpio_num, GIO))
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static const struct gpio_chip sch_gpio_chip = {
@@ -140,10 +168,161 @@ static const struct gpio_chip sch_gpio_chip = {
.get_direction = sch_gpio_get_direction,
};
+static int sch_irq_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct sch_gpio *sch = gpiochip_get_data(gc);
+ irq_hw_number_t gpio_num = irqd_to_hwirq(d);
+ unsigned long flags;
+ int rising, falling;
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ rising = 1;
+ falling = 0;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ rising = 0;
+ falling = 1;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ rising = 1;
+ falling = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&sch->lock, flags);
+
+ sch_gpio_reg_set(sch, gpio_num, GTPE, rising);
+ sch_gpio_reg_set(sch, gpio_num, GTNE, falling);
+
+ irq_set_handler_locked(d, handle_edge_irq);
+
+ spin_unlock_irqrestore(&sch->lock, flags);
+
+ return 0;
+}
+
+static void sch_irq_ack(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct sch_gpio *sch = gpiochip_get_data(gc);
+ irq_hw_number_t gpio_num = irqd_to_hwirq(d);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sch->lock, flags);
+ sch_gpio_reg_set(sch, gpio_num, GTS, 1);
+ spin_unlock_irqrestore(&sch->lock, flags);
+}
+
+static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, int val)
+{
+ struct sch_gpio *sch = gpiochip_get_data(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sch->lock, flags);
+ sch_gpio_reg_set(sch, gpio_num, GGPE, val);
+ spin_unlock_irqrestore(&sch->lock, flags);
+}
+
+static void sch_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t gpio_num = irqd_to_hwirq(d);
+
+ sch_irq_mask_unmask(gc, gpio_num, 0);
+ gpiochip_disable_irq(gc, gpio_num);
+}
+
+static void sch_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t gpio_num = irqd_to_hwirq(d);
+
+ gpiochip_enable_irq(gc, gpio_num);
+ sch_irq_mask_unmask(gc, gpio_num, 1);
+}
+
+static const struct irq_chip sch_irqchip = {
+ .name = "sch_gpio",
+ .irq_ack = sch_irq_ack,
+ .irq_mask = sch_irq_mask,
+ .irq_unmask = sch_irq_unmask,
+ .irq_set_type = sch_irq_type,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
+{
+ struct sch_gpio *sch = context;
+ struct gpio_chip *gc = &sch->chip;
+ unsigned long core_status, resume_status;
+ unsigned long pending;
+ unsigned long flags;
+ int offset;
+ u32 ret;
+
+ spin_lock_irqsave(&sch->lock, flags);
+
+ core_status = inl(sch->iobase + CORE_BANK_OFFSET + GTS);
+ resume_status = inl(sch->iobase + RESUME_BANK_OFFSET + GTS);
+
+ spin_unlock_irqrestore(&sch->lock, flags);
+
+ pending = (resume_status << sch->resume_base) | core_status;
+ for_each_set_bit(offset, &pending, sch->chip.ngpio)
+ generic_handle_domain_irq(gc->irq.domain, offset);
+
+ /* Set returning value depending on whether we handled an interrupt */
+ ret = pending ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
+
+ /* Acknowledge GPE to ACPICA */
+ ret |= ACPI_REENABLE_GPE;
+
+ return ret;
+}
+
+static void sch_gpio_remove_gpe_handler(void *data)
+{
+ struct sch_gpio *sch = data;
+
+ acpi_disable_gpe(NULL, sch->gpe);
+ acpi_remove_gpe_handler(NULL, sch->gpe, sch->gpe_handler);
+}
+
+static int sch_gpio_install_gpe_handler(struct sch_gpio *sch)
+{
+ struct device *dev = sch->chip.parent;
+ acpi_status status;
+
+ status = acpi_install_gpe_handler(NULL, sch->gpe, ACPI_GPE_LEVEL_TRIGGERED,
+ sch->gpe_handler, sch);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "Failed to install GPE handler for %u: %s\n",
+ sch->gpe, acpi_format_exception(status));
+ return -ENODEV;
+ }
+
+ status = acpi_enable_gpe(NULL, sch->gpe);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "Failed to enable GPE handler for %u: %s\n",
+ sch->gpe, acpi_format_exception(status));
+ acpi_remove_gpe_handler(NULL, sch->gpe, sch->gpe_handler);
+ return -ENODEV;
+ }
+
+ return devm_add_action_or_reset(dev, sch_gpio_remove_gpe_handler, sch);
+}
+
static int sch_gpio_probe(struct platform_device *pdev)
{
+ struct gpio_irq_chip *girq;
struct sch_gpio *sch;
struct resource *res;
+ int ret;
sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL);
if (!sch)
@@ -203,6 +382,22 @@ static int sch_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sch);
+ girq = &sch->chip.irq;
+ gpio_irq_chip_set_chip(girq, &sch_irqchip);
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->parent_handler = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
+
+ /* GPE setup is optional */
+ sch->gpe = GPE0E_GPIO;
+ sch->gpe_handler = sch_gpio_gpe_handler;
+
+ ret = sch_gpio_install_gpe_handler(sch);
+ if (ret)
+ dev_warn(&pdev->dev, "Can't setup GPE, no IRQ support\n");
+
return devm_gpiochip_add_data(&pdev->dev, &sch->chip, sch);
}
diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c
index 8ecf336c9af4..da01e1cad7cb 100644
--- a/drivers/gpio/gpio-sch311x.c
+++ b/drivers/gpio/gpio-sch311x.c
@@ -228,7 +228,10 @@ static int sch311x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
data = inb(block->runtime_reg + block->config_regs[offset]);
spin_unlock(&block->lock);
- return !!(data & SCH311X_GPIO_CONF_DIR);
+ if (data & SCH311X_GPIO_CONF_DIR)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int sch311x_gpio_set_config(struct gpio_chip *chip, unsigned offset,
diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
new file mode 100644
index 000000000000..238f3210970c
--- /dev/null
+++ b/drivers/gpio/gpio-sifive.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 SiFive
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/of_irq.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/regmap.h>
+
+#define SIFIVE_GPIO_INPUT_VAL 0x00
+#define SIFIVE_GPIO_INPUT_EN 0x04
+#define SIFIVE_GPIO_OUTPUT_EN 0x08
+#define SIFIVE_GPIO_OUTPUT_VAL 0x0C
+#define SIFIVE_GPIO_RISE_IE 0x18
+#define SIFIVE_GPIO_RISE_IP 0x1C
+#define SIFIVE_GPIO_FALL_IE 0x20
+#define SIFIVE_GPIO_FALL_IP 0x24
+#define SIFIVE_GPIO_HIGH_IE 0x28
+#define SIFIVE_GPIO_HIGH_IP 0x2C
+#define SIFIVE_GPIO_LOW_IE 0x30
+#define SIFIVE_GPIO_LOW_IP 0x34
+#define SIFIVE_GPIO_OUTPUT_XOR 0x40
+
+#define SIFIVE_GPIO_MAX 32
+
+struct sifive_gpio {
+ void __iomem *base;
+ struct gpio_chip gc;
+ struct regmap *regs;
+ unsigned long irq_state;
+ unsigned int trigger[SIFIVE_GPIO_MAX];
+ unsigned int irq_number[SIFIVE_GPIO_MAX];
+};
+
+static void sifive_gpio_set_ie(struct sifive_gpio *chip, unsigned int offset)
+{
+ unsigned long flags;
+ unsigned int trigger;
+
+ raw_spin_lock_irqsave(&chip->gc.bgpio_lock, flags);
+ trigger = (chip->irq_state & BIT(offset)) ? chip->trigger[offset] : 0;
+ regmap_update_bits(chip->regs, SIFIVE_GPIO_RISE_IE, BIT(offset),
+ (trigger & IRQ_TYPE_EDGE_RISING) ? BIT(offset) : 0);
+ regmap_update_bits(chip->regs, SIFIVE_GPIO_FALL_IE, BIT(offset),
+ (trigger & IRQ_TYPE_EDGE_FALLING) ? BIT(offset) : 0);
+ regmap_update_bits(chip->regs, SIFIVE_GPIO_HIGH_IE, BIT(offset),
+ (trigger & IRQ_TYPE_LEVEL_HIGH) ? BIT(offset) : 0);
+ regmap_update_bits(chip->regs, SIFIVE_GPIO_LOW_IE, BIT(offset),
+ (trigger & IRQ_TYPE_LEVEL_LOW) ? BIT(offset) : 0);
+ raw_spin_unlock_irqrestore(&chip->gc.bgpio_lock, flags);
+}
+
+static int sifive_gpio_irq_set_type(struct irq_data *d, unsigned int trigger)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct sifive_gpio *chip = gpiochip_get_data(gc);
+ int offset = irqd_to_hwirq(d);
+
+ if (offset < 0 || offset >= gc->ngpio)
+ return -EINVAL;
+
+ chip->trigger[offset] = trigger;
+ sifive_gpio_set_ie(chip, offset);
+ return 0;
+}
+
+static void sifive_gpio_irq_enable(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct sifive_gpio *chip = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ int offset = hwirq % SIFIVE_GPIO_MAX;
+ u32 bit = BIT(offset);
+ unsigned long flags;
+
+ gpiochip_enable_irq(gc, hwirq);
+ irq_chip_enable_parent(d);
+
+ /* Switch to input */
+ gc->direction_input(gc, offset);
+
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+ /* Clear any sticky pending interrupts */
+ regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit);
+ regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit);
+ regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit);
+ regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+
+ /* Enable interrupts */
+ assign_bit(offset, &chip->irq_state, 1);
+ sifive_gpio_set_ie(chip, offset);
+}
+
+static void sifive_gpio_irq_disable(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct sifive_gpio *chip = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ int offset = hwirq % SIFIVE_GPIO_MAX;
+
+ assign_bit(offset, &chip->irq_state, 0);
+ sifive_gpio_set_ie(chip, offset);
+ irq_chip_disable_parent(d);
+ gpiochip_disable_irq(gc, hwirq);
+}
+
+static void sifive_gpio_irq_eoi(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct sifive_gpio *chip = gpiochip_get_data(gc);
+ int offset = irqd_to_hwirq(d) % SIFIVE_GPIO_MAX;
+ u32 bit = BIT(offset);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
+ /* Clear all pending interrupts */
+ regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit);
+ regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit);
+ regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit);
+ regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit);
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+
+ irq_chip_eoi_parent(d);
+}
+
+static int sifive_gpio_irq_set_affinity(struct irq_data *data,
+ const struct cpumask *dest,
+ bool force)
+{
+ if (data->parent_data)
+ return irq_chip_set_affinity_parent(data, dest, force);
+
+ return -EINVAL;
+}
+
+static const struct irq_chip sifive_gpio_irqchip = {
+ .name = "sifive-gpio",
+ .irq_set_type = sifive_gpio_irq_set_type,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_enable = sifive_gpio_irq_enable,
+ .irq_disable = sifive_gpio_irq_disable,
+ .irq_eoi = sifive_gpio_irq_eoi,
+ .irq_set_affinity = sifive_gpio_irq_set_affinity,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static int sifive_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+ unsigned int child,
+ unsigned int child_type,
+ unsigned int *parent,
+ unsigned int *parent_type)
+{
+ struct sifive_gpio *chip = gpiochip_get_data(gc);
+ struct irq_data *d = irq_get_irq_data(chip->irq_number[child]);
+
+ *parent_type = IRQ_TYPE_NONE;
+ *parent = irqd_to_hwirq(d);
+
+ return 0;
+}
+
+static const struct regmap_config sifive_gpio_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+ .disable_locking = true,
+};
+
+static int sifive_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *irq_parent;
+ struct irq_domain *parent;
+ struct gpio_irq_chip *girq;
+ struct sifive_gpio *chip;
+ int ret, ngpio, i;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(chip->base)) {
+ dev_err(dev, "failed to allocate device memory\n");
+ return PTR_ERR(chip->base);
+ }
+
+ chip->regs = devm_regmap_init_mmio(dev, chip->base,
+ &sifive_gpio_regmap_config);
+ if (IS_ERR(chip->regs))
+ return PTR_ERR(chip->regs);
+
+ ngpio = of_irq_count(node);
+ if (ngpio > SIFIVE_GPIO_MAX) {
+ dev_err(dev, "Too many GPIO interrupts (max=%d)\n",
+ SIFIVE_GPIO_MAX);
+ return -ENXIO;
+ }
+
+ irq_parent = of_irq_find_parent(node);
+ if (!irq_parent) {
+ dev_err(dev, "no IRQ parent node\n");
+ return -ENODEV;
+ }
+ parent = irq_find_host(irq_parent);
+ if (!parent) {
+ dev_err(dev, "no IRQ parent domain\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ngpio; i++)
+ chip->irq_number[i] = platform_get_irq(pdev, i);
+
+ ret = bgpio_init(&chip->gc, dev, 4,
+ chip->base + SIFIVE_GPIO_INPUT_VAL,
+ chip->base + SIFIVE_GPIO_OUTPUT_VAL,
+ NULL,
+ chip->base + SIFIVE_GPIO_OUTPUT_EN,
+ chip->base + SIFIVE_GPIO_INPUT_EN,
+ BGPIOF_READ_OUTPUT_REG_SET);
+ if (ret) {
+ dev_err(dev, "unable to init generic GPIO\n");
+ return ret;
+ }
+
+ /* Disable all GPIO interrupts before enabling parent interrupts */
+ regmap_write(chip->regs, SIFIVE_GPIO_RISE_IE, 0);
+ regmap_write(chip->regs, SIFIVE_GPIO_FALL_IE, 0);
+ regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IE, 0);
+ regmap_write(chip->regs, SIFIVE_GPIO_LOW_IE, 0);
+ chip->irq_state = 0;
+
+ chip->gc.base = -1;
+ chip->gc.ngpio = ngpio;
+ chip->gc.label = dev_name(dev);
+ chip->gc.parent = dev;
+ chip->gc.owner = THIS_MODULE;
+ girq = &chip->gc.irq;
+ gpio_irq_chip_set_chip(girq, &sifive_gpio_irqchip);
+ girq->fwnode = of_node_to_fwnode(node);
+ girq->parent_domain = parent;
+ girq->child_to_parent_hwirq = sifive_gpio_child_to_parent_hwirq;
+ girq->handler = handle_bad_irq;
+ girq->default_type = IRQ_TYPE_NONE;
+
+ platform_set_drvdata(pdev, chip);
+ return gpiochip_add_data(&chip->gc, chip);
+}
+
+static const struct of_device_id sifive_gpio_match[] = {
+ { .compatible = "sifive,gpio0" },
+ { .compatible = "sifive,fu540-c000-gpio" },
+ { },
+};
+
+static struct platform_driver sifive_gpio_driver = {
+ .probe = sifive_gpio_probe,
+ .driver = {
+ .name = "sifive_gpio",
+ .of_match_table = of_match_ptr(sifive_gpio_match),
+ },
+};
+builtin_platform_driver(sifive_gpio_driver)
diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c
new file mode 100644
index 000000000000..98109839102f
--- /dev/null
+++ b/drivers/gpio/gpio-sim.c
@@ -0,0 +1,1603 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * GPIO testing driver based on configfs.
+ *
+ * Copyright (C) 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitmap.h>
+#include <linux/completion.h>
+#include <linux/configfs.h>
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irq_sim.h>
+#include <linux/list.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/string_helpers.h>
+#include <linux/sysfs.h>
+
+#include "gpiolib.h"
+
+#define GPIO_SIM_PROP_MAX 4 /* Max 3 properties + sentinel. */
+#define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */
+
+static DEFINE_IDA(gpio_sim_ida);
+
+struct gpio_sim_chip {
+ struct gpio_chip gc;
+ unsigned long *direction_map;
+ unsigned long *value_map;
+ unsigned long *pull_map;
+ struct irq_domain *irq_sim;
+ struct mutex lock;
+ const struct attribute_group **attr_groups;
+};
+
+struct gpio_sim_attribute {
+ struct device_attribute dev_attr;
+ unsigned int offset;
+};
+
+static struct gpio_sim_attribute *
+to_gpio_sim_attr(struct device_attribute *dev_attr)
+{
+ return container_of(dev_attr, struct gpio_sim_attribute, dev_attr);
+}
+
+static int gpio_sim_apply_pull(struct gpio_sim_chip *chip,
+ unsigned int offset, int value)
+{
+ int irq, irq_type, ret;
+ struct gpio_desc *desc;
+ struct gpio_chip *gc;
+
+ gc = &chip->gc;
+ desc = &gc->gpiodev->descs[offset];
+
+ mutex_lock(&chip->lock);
+
+ if (test_bit(FLAG_REQUESTED, &desc->flags) &&
+ !test_bit(FLAG_IS_OUT, &desc->flags)) {
+ if (value == !!test_bit(offset, chip->value_map))
+ goto set_pull;
+
+ /*
+ * This is fine - it just means, nobody is listening
+ * for interrupts on this line, otherwise
+ * irq_create_mapping() would have been called from
+ * the to_irq() callback.
+ */
+ irq = irq_find_mapping(chip->irq_sim, offset);
+ if (!irq)
+ goto set_value;
+
+ irq_type = irq_get_trigger_type(irq);
+
+ if ((value && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
+ (!value && (irq_type & IRQ_TYPE_EDGE_FALLING))) {
+ ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING,
+ true);
+ if (ret)
+ goto set_pull;
+ }
+ }
+
+set_value:
+ /* Change the value unless we're actively driving the line. */
+ if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+ !test_bit(FLAG_IS_OUT, &desc->flags))
+ __assign_bit(offset, chip->value_map, value);
+
+set_pull:
+ __assign_bit(offset, chip->pull_map, value);
+ mutex_unlock(&chip->lock);
+ return 0;
+}
+
+static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = !!test_bit(offset, chip->value_map);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static void gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+
+ mutex_lock(&chip->lock);
+ __assign_bit(offset, chip->value_map, value);
+ mutex_unlock(&chip->lock);
+}
+
+static int gpio_sim_get_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+
+ mutex_lock(&chip->lock);
+ bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio);
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static void gpio_sim_set_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+
+ mutex_lock(&chip->lock);
+ bitmap_replace(chip->value_map, chip->value_map, bits, mask, gc->ngpio);
+ mutex_unlock(&chip->lock);
+}
+
+static int gpio_sim_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+
+ mutex_lock(&chip->lock);
+ __clear_bit(offset, chip->direction_map);
+ __assign_bit(offset, chip->value_map, value);
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static int gpio_sim_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+
+ mutex_lock(&chip->lock);
+ __set_bit(offset, chip->direction_map);
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static int gpio_sim_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+ int direction;
+
+ mutex_lock(&chip->lock);
+ direction = !!test_bit(offset, chip->direction_map);
+ mutex_unlock(&chip->lock);
+
+ return direction ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
+}
+
+static int gpio_sim_set_config(struct gpio_chip *gc,
+ unsigned int offset, unsigned long config)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_BIAS_PULL_UP:
+ return gpio_sim_apply_pull(chip, offset, 1);
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ return gpio_sim_apply_pull(chip, offset, 0);
+ default:
+ break;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int gpio_sim_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+
+ return irq_create_mapping(chip->irq_sim, offset);
+}
+
+static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_sim_chip *chip = gpiochip_get_data(gc);
+
+ mutex_lock(&chip->lock);
+ __assign_bit(offset, chip->value_map, !!test_bit(offset, chip->pull_map));
+ mutex_unlock(&chip->lock);
+}
+
+static ssize_t gpio_sim_sysfs_val_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr);
+ struct gpio_sim_chip *chip = dev_get_drvdata(dev);
+ int val;
+
+ mutex_lock(&chip->lock);
+ val = !!test_bit(line_attr->offset, chip->value_map);
+ mutex_unlock(&chip->lock);
+
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t gpio_sim_sysfs_val_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ /*
+ * Not assigning this function will result in write() returning -EIO
+ * which is confusing. Return -EPERM explicitly.
+ */
+ return -EPERM;
+}
+
+static const char *const gpio_sim_sysfs_pull_strings[] = {
+ [0] = "pull-down",
+ [1] = "pull-up",
+};
+
+static ssize_t gpio_sim_sysfs_pull_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr);
+ struct gpio_sim_chip *chip = dev_get_drvdata(dev);
+ int pull;
+
+ mutex_lock(&chip->lock);
+ pull = !!test_bit(line_attr->offset, chip->pull_map);
+ mutex_unlock(&chip->lock);
+
+ return sysfs_emit(buf, "%s\n", gpio_sim_sysfs_pull_strings[pull]);
+}
+
+static ssize_t gpio_sim_sysfs_pull_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr);
+ struct gpio_sim_chip *chip = dev_get_drvdata(dev);
+ int ret, pull;
+
+ pull = sysfs_match_string(gpio_sim_sysfs_pull_strings, buf);
+ if (pull < 0)
+ return pull;
+
+ ret = gpio_sim_apply_pull(chip, line_attr->offset, pull);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static void gpio_sim_mutex_destroy(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_destroy(lock);
+}
+
+static void gpio_sim_sysfs_remove(void *data)
+{
+ struct gpio_sim_chip *chip = data;
+
+ sysfs_remove_groups(&chip->gc.gpiodev->dev.kobj, chip->attr_groups);
+}
+
+static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip)
+{
+ struct device_attribute *val_dev_attr, *pull_dev_attr;
+ struct gpio_sim_attribute *val_attr, *pull_attr;
+ unsigned int num_lines = chip->gc.ngpio;
+ struct device *dev = chip->gc.parent;
+ struct attribute_group *attr_group;
+ struct attribute **attrs;
+ int i, ret;
+
+ chip->attr_groups = devm_kcalloc(dev, sizeof(*chip->attr_groups),
+ num_lines + 1, GFP_KERNEL);
+ if (!chip->attr_groups)
+ return -ENOMEM;
+
+ for (i = 0; i < num_lines; i++) {
+ attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL);
+ attrs = devm_kcalloc(dev, GPIO_SIM_NUM_ATTRS, sizeof(*attrs),
+ GFP_KERNEL);
+ val_attr = devm_kzalloc(dev, sizeof(*val_attr), GFP_KERNEL);
+ pull_attr = devm_kzalloc(dev, sizeof(*pull_attr), GFP_KERNEL);
+ if (!attr_group || !attrs || !val_attr || !pull_attr)
+ return -ENOMEM;
+
+ attr_group->name = devm_kasprintf(dev, GFP_KERNEL,
+ "sim_gpio%u", i);
+ if (!attr_group->name)
+ return -ENOMEM;
+
+ val_attr->offset = pull_attr->offset = i;
+
+ val_dev_attr = &val_attr->dev_attr;
+ pull_dev_attr = &pull_attr->dev_attr;
+
+ sysfs_attr_init(&val_dev_attr->attr);
+ sysfs_attr_init(&pull_dev_attr->attr);
+
+ val_dev_attr->attr.name = "value";
+ pull_dev_attr->attr.name = "pull";
+
+ val_dev_attr->attr.mode = pull_dev_attr->attr.mode = 0644;
+
+ val_dev_attr->show = gpio_sim_sysfs_val_show;
+ val_dev_attr->store = gpio_sim_sysfs_val_store;
+ pull_dev_attr->show = gpio_sim_sysfs_pull_show;
+ pull_dev_attr->store = gpio_sim_sysfs_pull_store;
+
+ attrs[0] = &val_dev_attr->attr;
+ attrs[1] = &pull_dev_attr->attr;
+
+ attr_group->attrs = attrs;
+ chip->attr_groups[i] = attr_group;
+ }
+
+ ret = sysfs_create_groups(&chip->gc.gpiodev->dev.kobj,
+ chip->attr_groups);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip);
+}
+
+static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
+{
+ struct gpio_sim_chip *chip;
+ struct gpio_chip *gc;
+ const char *label;
+ u32 num_lines;
+ int ret;
+
+ ret = fwnode_property_read_u32(swnode, "ngpios", &num_lines);
+ if (ret)
+ return ret;
+
+ ret = fwnode_property_read_string(swnode, "gpio-sim,label", &label);
+ if (ret) {
+ label = devm_kasprintf(dev, GFP_KERNEL, "%s-%s",
+ dev_name(dev), fwnode_get_name(swnode));
+ if (!label)
+ return -ENOMEM;
+ }
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->direction_map = devm_bitmap_alloc(dev, num_lines, GFP_KERNEL);
+ if (!chip->direction_map)
+ return -ENOMEM;
+
+ /* Default to input mode. */
+ bitmap_fill(chip->direction_map, num_lines);
+
+ chip->value_map = devm_bitmap_zalloc(dev, num_lines, GFP_KERNEL);
+ if (!chip->value_map)
+ return -ENOMEM;
+
+ chip->pull_map = devm_bitmap_zalloc(dev, num_lines, GFP_KERNEL);
+ if (!chip->pull_map)
+ return -ENOMEM;
+
+ chip->irq_sim = devm_irq_domain_create_sim(dev, NULL, num_lines);
+ if (IS_ERR(chip->irq_sim))
+ return PTR_ERR(chip->irq_sim);
+
+ mutex_init(&chip->lock);
+ ret = devm_add_action_or_reset(dev, gpio_sim_mutex_destroy,
+ &chip->lock);
+ if (ret)
+ return ret;
+
+ gc = &chip->gc;
+ gc->base = -1;
+ gc->ngpio = num_lines;
+ gc->label = label;
+ gc->owner = THIS_MODULE;
+ gc->parent = dev;
+ gc->fwnode = swnode;
+ gc->get = gpio_sim_get;
+ gc->set = gpio_sim_set;
+ gc->get_multiple = gpio_sim_get_multiple;
+ gc->set_multiple = gpio_sim_set_multiple;
+ gc->direction_output = gpio_sim_direction_output;
+ gc->direction_input = gpio_sim_direction_input;
+ gc->get_direction = gpio_sim_get_direction;
+ gc->set_config = gpio_sim_set_config;
+ gc->to_irq = gpio_sim_to_irq;
+ gc->free = gpio_sim_free;
+
+ ret = devm_gpiochip_add_data(dev, gc, chip);
+ if (ret)
+ return ret;
+
+ /* Used by sysfs and configfs callbacks. */
+ dev_set_drvdata(&gc->gpiodev->dev, chip);
+
+ return gpio_sim_setup_sysfs(chip);
+}
+
+static int gpio_sim_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fwnode_handle *swnode;
+ int ret;
+
+ device_for_each_child_node(dev, swnode) {
+ ret = gpio_sim_add_bank(swnode, dev);
+ if (ret) {
+ fwnode_handle_put(swnode);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id gpio_sim_of_match[] = {
+ { .compatible = "gpio-simulator" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gpio_sim_of_match);
+
+static struct platform_driver gpio_sim_driver = {
+ .driver = {
+ .name = "gpio-sim",
+ .of_match_table = gpio_sim_of_match,
+ },
+ .probe = gpio_sim_probe,
+};
+
+struct gpio_sim_device {
+ struct config_group group;
+
+ /*
+ * If pdev is NULL, the device is 'pending' (waiting for configuration).
+ * Once the pointer is assigned, the device has been created and the
+ * item is 'live'.
+ */
+ struct platform_device *pdev;
+ int id;
+
+ /*
+ * Each configfs filesystem operation is protected with the subsystem
+ * mutex. Each separate attribute is protected with the buffer mutex.
+ * This structure however can be modified by callbacks of different
+ * attributes so we need another lock.
+ *
+ * We use this lock fo protecting all data structures owned by this
+ * object too.
+ */
+ struct mutex lock;
+
+ /*
+ * This is used to synchronously wait for the driver's probe to complete
+ * and notify the user-space about any errors.
+ */
+ struct notifier_block bus_notifier;
+ struct completion probe_completion;
+ bool driver_bound;
+
+ struct gpiod_hog *hogs;
+
+ struct list_head bank_list;
+};
+
+/* This is called with dev->lock already taken. */
+static int gpio_sim_bus_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct gpio_sim_device *simdev = container_of(nb,
+ struct gpio_sim_device,
+ bus_notifier);
+ struct device *dev = data;
+ char devname[32];
+
+ snprintf(devname, sizeof(devname), "gpio-sim.%u", simdev->id);
+
+ if (strcmp(dev_name(dev), devname) == 0) {
+ if (action == BUS_NOTIFY_BOUND_DRIVER)
+ simdev->driver_bound = true;
+ else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND)
+ simdev->driver_bound = false;
+ else
+ return NOTIFY_DONE;
+
+ complete(&simdev->probe_completion);
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct gpio_sim_device *to_gpio_sim_device(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+
+ return container_of(group, struct gpio_sim_device, group);
+}
+
+struct gpio_sim_bank {
+ struct config_group group;
+
+ /*
+ * We could have used the ci_parent field of the config_item but
+ * configfs is stupid and calls the item's release callback after
+ * already having cleared the parent pointer even though the parent
+ * is guaranteed to survive the child...
+ *
+ * So we need to store the pointer to the parent struct here. We can
+ * dereference it anywhere we need with no checks and no locking as
+ * it's guaranteed to survive the children and protected by configfs
+ * locks.
+ *
+ * Same for other structures.
+ */
+ struct gpio_sim_device *parent;
+ struct list_head siblings;
+
+ char *label;
+ unsigned int num_lines;
+
+ struct list_head line_list;
+
+ struct fwnode_handle *swnode;
+};
+
+static struct gpio_sim_bank *to_gpio_sim_bank(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+
+ return container_of(group, struct gpio_sim_bank, group);
+}
+
+static bool gpio_sim_bank_has_label(struct gpio_sim_bank *bank)
+{
+ return bank->label && *bank->label;
+}
+
+static struct gpio_sim_device *
+gpio_sim_bank_get_device(struct gpio_sim_bank *bank)
+{
+ return bank->parent;
+}
+
+struct gpio_sim_hog;
+
+struct gpio_sim_line {
+ struct config_group group;
+
+ struct gpio_sim_bank *parent;
+ struct list_head siblings;
+
+ unsigned int offset;
+ char *name;
+
+ /* There can only be one hog per line. */
+ struct gpio_sim_hog *hog;
+};
+
+static struct gpio_sim_line *to_gpio_sim_line(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+
+ return container_of(group, struct gpio_sim_line, group);
+}
+
+static struct gpio_sim_device *
+gpio_sim_line_get_device(struct gpio_sim_line *line)
+{
+ struct gpio_sim_bank *bank = line->parent;
+
+ return gpio_sim_bank_get_device(bank);
+}
+
+struct gpio_sim_hog {
+ struct config_item item;
+ struct gpio_sim_line *parent;
+
+ char *name;
+ int dir;
+};
+
+static struct gpio_sim_hog *to_gpio_sim_hog(struct config_item *item)
+{
+ return container_of(item, struct gpio_sim_hog, item);
+}
+
+static struct gpio_sim_device *gpio_sim_hog_get_device(struct gpio_sim_hog *hog)
+{
+ struct gpio_sim_line *line = hog->parent;
+
+ return gpio_sim_line_get_device(line);
+}
+
+static bool gpio_sim_device_is_live_unlocked(struct gpio_sim_device *dev)
+{
+ return !!dev->pdev;
+}
+
+static char *gpio_sim_strdup_trimmed(const char *str, size_t count)
+{
+ char *dup, *trimmed;
+
+ dup = kstrndup(str, count, GFP_KERNEL);
+ if (!dup)
+ return NULL;
+
+ trimmed = strstrip(dup);
+ memmove(dup, trimmed, strlen(trimmed) + 1);
+
+ return dup;
+}
+
+static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item,
+ char *page)
+{
+ struct gpio_sim_device *dev = to_gpio_sim_device(item);
+ struct platform_device *pdev;
+ int ret;
+
+ mutex_lock(&dev->lock);
+ pdev = dev->pdev;
+ if (pdev)
+ ret = sprintf(page, "%s\n", dev_name(&pdev->dev));
+ else
+ ret = sprintf(page, "gpio-sim.%d\n", dev->id);
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name);
+
+static ssize_t
+gpio_sim_device_config_live_show(struct config_item *item, char *page)
+{
+ struct gpio_sim_device *dev = to_gpio_sim_device(item);
+ bool live;
+
+ mutex_lock(&dev->lock);
+ live = gpio_sim_device_is_live_unlocked(dev);
+ mutex_unlock(&dev->lock);
+
+ return sprintf(page, "%c\n", live ? '1' : '0');
+}
+
+static char **gpio_sim_make_line_names(struct gpio_sim_bank *bank,
+ unsigned int *line_names_size)
+{
+ unsigned int max_offset = 0;
+ bool has_line_names = false;
+ struct gpio_sim_line *line;
+ char **line_names;
+
+ list_for_each_entry(line, &bank->line_list, siblings) {
+ if (line->name) {
+ if (line->offset > max_offset)
+ max_offset = line->offset;
+
+ /*
+ * max_offset can stay at 0 so it's not an indicator
+ * of whether line names were configured at all.
+ */
+ has_line_names = true;
+ }
+ }
+
+ if (!has_line_names)
+ /*
+ * This is not an error - NULL means, there are no line
+ * names configured.
+ */
+ return NULL;
+
+ *line_names_size = max_offset + 1;
+
+ line_names = kcalloc(*line_names_size, sizeof(*line_names), GFP_KERNEL);
+ if (!line_names)
+ return ERR_PTR(-ENOMEM);
+
+ list_for_each_entry(line, &bank->line_list, siblings)
+ line_names[line->offset] = line->name;
+
+ return line_names;
+}
+
+static void gpio_sim_remove_hogs(struct gpio_sim_device *dev)
+{
+ struct gpiod_hog *hog;
+
+ if (!dev->hogs)
+ return;
+
+ gpiod_remove_hogs(dev->hogs);
+
+ for (hog = dev->hogs; !hog->chip_label; hog++) {
+ kfree(hog->chip_label);
+ kfree(hog->line_name);
+ }
+
+ kfree(dev->hogs);
+ dev->hogs = NULL;
+}
+
+static int gpio_sim_add_hogs(struct gpio_sim_device *dev)
+{
+ unsigned int num_hogs = 0, idx = 0;
+ struct gpio_sim_bank *bank;
+ struct gpio_sim_line *line;
+ struct gpiod_hog *hog;
+
+ list_for_each_entry(bank, &dev->bank_list, siblings) {
+ list_for_each_entry(line, &bank->line_list, siblings) {
+ if (line->hog)
+ num_hogs++;
+ }
+ }
+
+ if (!num_hogs)
+ return 0;
+
+ /* Allocate one more for the sentinel. */
+ dev->hogs = kcalloc(num_hogs + 1, sizeof(*dev->hogs), GFP_KERNEL);
+ if (!dev->hogs)
+ return -ENOMEM;
+
+ list_for_each_entry(bank, &dev->bank_list, siblings) {
+ list_for_each_entry(line, &bank->line_list, siblings) {
+ if (!line->hog)
+ continue;
+
+ hog = &dev->hogs[idx++];
+
+ /*
+ * We need to make this string manually because at this
+ * point the device doesn't exist yet and so dev_name()
+ * is not available.
+ */
+ if (gpio_sim_bank_has_label(bank))
+ hog->chip_label = kstrdup(bank->label,
+ GFP_KERNEL);
+ else
+ hog->chip_label = kasprintf(GFP_KERNEL,
+ "gpio-sim.%u-%s",
+ dev->id,
+ fwnode_get_name(
+ bank->swnode));
+ if (!hog->chip_label) {
+ gpio_sim_remove_hogs(dev);
+ return -ENOMEM;
+ }
+
+ /*
+ * We need to duplicate this because the hog config
+ * item can be removed at any time (and we can't block
+ * it) and gpiolib doesn't make a deep copy of the hog
+ * data.
+ */
+ if (line->hog->name) {
+ hog->line_name = kstrdup(line->hog->name,
+ GFP_KERNEL);
+ if (!hog->line_name) {
+ gpio_sim_remove_hogs(dev);
+ return -ENOMEM;
+ }
+ }
+
+ hog->chip_hwnum = line->offset;
+ hog->dflags = line->hog->dir;
+ }
+ }
+
+ gpiod_add_hogs(dev->hogs);
+
+ return 0;
+}
+
+static struct fwnode_handle *
+gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
+ struct fwnode_handle *parent)
+{
+ struct property_entry properties[GPIO_SIM_PROP_MAX];
+ unsigned int prop_idx = 0, line_names_size = 0;
+ struct fwnode_handle *swnode;
+ char **line_names;
+
+ memset(properties, 0, sizeof(properties));
+
+ properties[prop_idx++] = PROPERTY_ENTRY_U32("ngpios", bank->num_lines);
+
+ if (gpio_sim_bank_has_label(bank))
+ properties[prop_idx++] = PROPERTY_ENTRY_STRING("gpio-sim,label",
+ bank->label);
+
+ line_names = gpio_sim_make_line_names(bank, &line_names_size);
+ if (IS_ERR(line_names))
+ return ERR_CAST(line_names);
+
+ if (line_names)
+ properties[prop_idx++] = PROPERTY_ENTRY_STRING_ARRAY_LEN(
+ "gpio-line-names",
+ line_names, line_names_size);
+
+ swnode = fwnode_create_software_node(properties, parent);
+ kfree(line_names);
+ return swnode;
+}
+
+static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode)
+{
+ struct fwnode_handle *child;
+
+ fwnode_for_each_child_node(swnode, child)
+ fwnode_remove_software_node(child);
+
+ fwnode_remove_software_node(swnode);
+}
+
+static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev)
+{
+ struct gpio_sim_bank *this, *pos;
+
+ list_for_each_entry(this, &dev->bank_list, siblings) {
+ list_for_each_entry(pos, &dev->bank_list, siblings) {
+ if (this == pos || (!this->label || !pos->label))
+ continue;
+
+ if (strcmp(this->label, pos->label) == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev)
+{
+ struct platform_device_info pdevinfo;
+ struct fwnode_handle *swnode;
+ struct platform_device *pdev;
+ struct gpio_sim_bank *bank;
+ int ret;
+
+ if (list_empty(&dev->bank_list))
+ return -ENODATA;
+
+ /*
+ * Non-unique GPIO device labels are a corner-case we don't support
+ * as it would interfere with machine hogging mechanism and has little
+ * use in real life.
+ */
+ if (gpio_sim_bank_labels_non_unique(dev))
+ return -EINVAL;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+
+ swnode = fwnode_create_software_node(NULL, NULL);
+ if (IS_ERR(swnode))
+ return PTR_ERR(swnode);
+
+ list_for_each_entry(bank, &dev->bank_list, siblings) {
+ bank->swnode = gpio_sim_make_bank_swnode(bank, swnode);
+ if (IS_ERR(bank->swnode)) {
+ ret = PTR_ERR(bank->swnode);
+ gpio_sim_remove_swnode_recursive(swnode);
+ return ret;
+ }
+ }
+
+ ret = gpio_sim_add_hogs(dev);
+ if (ret) {
+ gpio_sim_remove_swnode_recursive(swnode);
+ return ret;
+ }
+
+ pdevinfo.name = "gpio-sim";
+ pdevinfo.fwnode = swnode;
+ pdevinfo.id = dev->id;
+
+ reinit_completion(&dev->probe_completion);
+ dev->driver_bound = false;
+ bus_register_notifier(&platform_bus_type, &dev->bus_notifier);
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier);
+ gpio_sim_remove_hogs(dev);
+ gpio_sim_remove_swnode_recursive(swnode);
+ return PTR_ERR(pdev);
+ }
+
+ wait_for_completion(&dev->probe_completion);
+ bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier);
+
+ if (!dev->driver_bound) {
+ /* Probe failed, check kernel log. */
+ platform_device_unregister(pdev);
+ gpio_sim_remove_hogs(dev);
+ gpio_sim_remove_swnode_recursive(swnode);
+ return -ENXIO;
+ }
+
+ dev->pdev = pdev;
+
+ return 0;
+}
+
+static void gpio_sim_device_deactivate_unlocked(struct gpio_sim_device *dev)
+{
+ struct fwnode_handle *swnode;
+
+ swnode = dev_fwnode(&dev->pdev->dev);
+ platform_device_unregister(dev->pdev);
+ gpio_sim_remove_swnode_recursive(swnode);
+ dev->pdev = NULL;
+ gpio_sim_remove_hogs(dev);
+}
+
+static ssize_t
+gpio_sim_device_config_live_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_sim_device *dev = to_gpio_sim_device(item);
+ bool live;
+ int ret;
+
+ ret = kstrtobool(page, &live);
+ if (ret)
+ return ret;
+
+ mutex_lock(&dev->lock);
+
+ if ((!live && !gpio_sim_device_is_live_unlocked(dev)) ||
+ (live && gpio_sim_device_is_live_unlocked(dev)))
+ ret = -EPERM;
+ else if (live)
+ ret = gpio_sim_device_activate_unlocked(dev);
+ else
+ gpio_sim_device_deactivate_unlocked(dev);
+
+ mutex_unlock(&dev->lock);
+
+ return ret ?: count;
+}
+
+CONFIGFS_ATTR(gpio_sim_device_config_, live);
+
+static struct configfs_attribute *gpio_sim_device_config_attrs[] = {
+ &gpio_sim_device_config_attr_dev_name,
+ &gpio_sim_device_config_attr_live,
+ NULL
+};
+
+struct gpio_sim_chip_name_ctx {
+ struct gpio_sim_device *dev;
+ char *page;
+};
+
+static int gpio_sim_emit_chip_name(struct device *dev, void *data)
+{
+ struct gpio_sim_chip_name_ctx *ctx = data;
+ struct fwnode_handle *swnode;
+ struct gpio_sim_bank *bank;
+
+ /* This would be the sysfs device exported in /sys/class/gpio. */
+ if (dev->class)
+ return 0;
+
+ swnode = dev_fwnode(dev);
+
+ list_for_each_entry(bank, &ctx->dev->bank_list, siblings) {
+ if (bank->swnode == swnode)
+ return sprintf(ctx->page, "%s\n", dev_name(dev));
+ }
+
+ return -ENODATA;
+}
+
+static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item,
+ char *page)
+{
+ struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
+ struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
+ struct gpio_sim_chip_name_ctx ctx = { dev, page };
+ int ret;
+
+ mutex_lock(&dev->lock);
+ if (gpio_sim_device_is_live_unlocked(dev))
+ ret = device_for_each_child(&dev->pdev->dev, &ctx,
+ gpio_sim_emit_chip_name);
+ else
+ ret = sprintf(page, "none\n");
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name);
+
+static ssize_t
+gpio_sim_bank_config_label_show(struct config_item *item, char *page)
+{
+ struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
+ struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
+ int ret;
+
+ mutex_lock(&dev->lock);
+ ret = sprintf(page, "%s\n", bank->label ?: "");
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+static ssize_t gpio_sim_bank_config_label_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
+ struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
+ char *trimmed;
+
+ mutex_lock(&dev->lock);
+
+ if (gpio_sim_device_is_live_unlocked(dev)) {
+ mutex_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
+ trimmed = gpio_sim_strdup_trimmed(page, count);
+ if (!trimmed) {
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
+ }
+
+ kfree(bank->label);
+ bank->label = trimmed;
+
+ mutex_unlock(&dev->lock);
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_sim_bank_config_, label);
+
+static ssize_t
+gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page)
+{
+ struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
+ struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
+ int ret;
+
+ mutex_lock(&dev->lock);
+ ret = sprintf(page, "%u\n", bank->num_lines);
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+static ssize_t
+gpio_sim_bank_config_num_lines_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
+ struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
+ unsigned int num_lines;
+ int ret;
+
+ ret = kstrtouint(page, 0, &num_lines);
+ if (ret)
+ return ret;
+
+ if (num_lines == 0)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ if (gpio_sim_device_is_live_unlocked(dev)) {
+ mutex_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
+ bank->num_lines = num_lines;
+
+ mutex_unlock(&dev->lock);
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_sim_bank_config_, num_lines);
+
+static struct configfs_attribute *gpio_sim_bank_config_attrs[] = {
+ &gpio_sim_bank_config_attr_chip_name,
+ &gpio_sim_bank_config_attr_label,
+ &gpio_sim_bank_config_attr_num_lines,
+ NULL
+};
+
+static ssize_t
+gpio_sim_line_config_name_show(struct config_item *item, char *page)
+{
+ struct gpio_sim_line *line = to_gpio_sim_line(item);
+ struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
+ int ret;
+
+ mutex_lock(&dev->lock);
+ ret = sprintf(page, "%s\n", line->name ?: "");
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+static ssize_t gpio_sim_line_config_name_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_sim_line *line = to_gpio_sim_line(item);
+ struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
+ char *trimmed;
+
+ mutex_lock(&dev->lock);
+
+ if (gpio_sim_device_is_live_unlocked(dev)) {
+ mutex_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
+ trimmed = gpio_sim_strdup_trimmed(page, count);
+ if (!trimmed) {
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
+ }
+
+ kfree(line->name);
+ line->name = trimmed;
+
+ mutex_unlock(&dev->lock);
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_sim_line_config_, name);
+
+static struct configfs_attribute *gpio_sim_line_config_attrs[] = {
+ &gpio_sim_line_config_attr_name,
+ NULL
+};
+
+static ssize_t gpio_sim_hog_config_name_show(struct config_item *item,
+ char *page)
+{
+ struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
+ struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
+ int ret;
+
+ mutex_lock(&dev->lock);
+ ret = sprintf(page, "%s\n", hog->name ?: "");
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+static ssize_t gpio_sim_hog_config_name_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
+ struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
+ char *trimmed;
+
+ mutex_lock(&dev->lock);
+
+ if (gpio_sim_device_is_live_unlocked(dev)) {
+ mutex_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
+ trimmed = gpio_sim_strdup_trimmed(page, count);
+ if (!trimmed) {
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
+ }
+
+ kfree(hog->name);
+ hog->name = trimmed;
+
+ mutex_unlock(&dev->lock);
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_sim_hog_config_, name);
+
+static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item,
+ char *page)
+{
+ struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
+ struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
+ char *repr;
+ int dir;
+
+ mutex_lock(&dev->lock);
+ dir = hog->dir;
+ mutex_unlock(&dev->lock);
+
+ switch (dir) {
+ case GPIOD_IN:
+ repr = "input";
+ break;
+ case GPIOD_OUT_HIGH:
+ repr = "output-high";
+ break;
+ case GPIOD_OUT_LOW:
+ repr = "output-low";
+ break;
+ default:
+ /* This would be a programmer bug. */
+ WARN(1, "Unexpected hog direction value: %d", dir);
+ return -EINVAL;
+ }
+
+ return sprintf(page, "%s\n", repr);
+}
+
+static ssize_t
+gpio_sim_hog_config_direction_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
+ struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
+ char *trimmed;
+ int dir;
+
+ mutex_lock(&dev->lock);
+
+ if (gpio_sim_device_is_live_unlocked(dev)) {
+ mutex_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
+ trimmed = gpio_sim_strdup_trimmed(page, count);
+ if (!trimmed) {
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
+ }
+
+ if (strcmp(trimmed, "input") == 0)
+ dir = GPIOD_IN;
+ else if (strcmp(trimmed, "output-high") == 0)
+ dir = GPIOD_OUT_HIGH;
+ else if (strcmp(trimmed, "output-low") == 0)
+ dir = GPIOD_OUT_LOW;
+ else
+ dir = -EINVAL;
+
+ kfree(trimmed);
+
+ if (dir < 0) {
+ mutex_unlock(&dev->lock);
+ return dir;
+ }
+
+ hog->dir = dir;
+
+ mutex_unlock(&dev->lock);
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_sim_hog_config_, direction);
+
+static struct configfs_attribute *gpio_sim_hog_config_attrs[] = {
+ &gpio_sim_hog_config_attr_name,
+ &gpio_sim_hog_config_attr_direction,
+ NULL
+};
+
+static void gpio_sim_hog_config_item_release(struct config_item *item)
+{
+ struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
+ struct gpio_sim_line *line = hog->parent;
+ struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
+
+ mutex_lock(&dev->lock);
+ line->hog = NULL;
+ mutex_unlock(&dev->lock);
+
+ kfree(hog->name);
+ kfree(hog);
+}
+
+static struct configfs_item_operations gpio_sim_hog_config_item_ops = {
+ .release = gpio_sim_hog_config_item_release,
+};
+
+static const struct config_item_type gpio_sim_hog_config_type = {
+ .ct_item_ops = &gpio_sim_hog_config_item_ops,
+ .ct_attrs = gpio_sim_hog_config_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *
+gpio_sim_line_config_make_hog_item(struct config_group *group, const char *name)
+{
+ struct gpio_sim_line *line = to_gpio_sim_line(&group->cg_item);
+ struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
+ struct gpio_sim_hog *hog;
+
+ if (strcmp(name, "hog") != 0)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&dev->lock);
+
+ hog = kzalloc(sizeof(*hog), GFP_KERNEL);
+ if (!hog) {
+ mutex_unlock(&dev->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ config_item_init_type_name(&hog->item, name,
+ &gpio_sim_hog_config_type);
+
+ hog->dir = GPIOD_IN;
+ hog->name = NULL;
+ hog->parent = line;
+ line->hog = hog;
+
+ mutex_unlock(&dev->lock);
+
+ return &hog->item;
+}
+
+static void gpio_sim_line_config_group_release(struct config_item *item)
+{
+ struct gpio_sim_line *line = to_gpio_sim_line(item);
+ struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
+
+ mutex_lock(&dev->lock);
+ list_del(&line->siblings);
+ mutex_unlock(&dev->lock);
+
+ kfree(line->name);
+ kfree(line);
+}
+
+static struct configfs_item_operations gpio_sim_line_config_item_ops = {
+ .release = gpio_sim_line_config_group_release,
+};
+
+static struct configfs_group_operations gpio_sim_line_config_group_ops = {
+ .make_item = gpio_sim_line_config_make_hog_item,
+};
+
+static const struct config_item_type gpio_sim_line_config_type = {
+ .ct_item_ops = &gpio_sim_line_config_item_ops,
+ .ct_group_ops = &gpio_sim_line_config_group_ops,
+ .ct_attrs = gpio_sim_line_config_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *
+gpio_sim_bank_config_make_line_group(struct config_group *group,
+ const char *name)
+{
+ struct gpio_sim_bank *bank = to_gpio_sim_bank(&group->cg_item);
+ struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
+ struct gpio_sim_line *line;
+ unsigned int offset;
+ int ret, nchar;
+
+ ret = sscanf(name, "line%u%n", &offset, &nchar);
+ if (ret != 1 || nchar != strlen(name))
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&dev->lock);
+
+ if (gpio_sim_device_is_live_unlocked(dev)) {
+ mutex_unlock(&dev->lock);
+ return ERR_PTR(-EBUSY);
+ }
+
+ line = kzalloc(sizeof(*line), GFP_KERNEL);
+ if (!line) {
+ mutex_unlock(&dev->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ config_group_init_type_name(&line->group, name,
+ &gpio_sim_line_config_type);
+
+ line->parent = bank;
+ line->offset = offset;
+ list_add_tail(&line->siblings, &bank->line_list);
+
+ mutex_unlock(&dev->lock);
+
+ return &line->group;
+}
+
+static void gpio_sim_bank_config_group_release(struct config_item *item)
+{
+ struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
+ struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
+
+ mutex_lock(&dev->lock);
+ list_del(&bank->siblings);
+ mutex_unlock(&dev->lock);
+
+ kfree(bank->label);
+ kfree(bank);
+}
+
+static struct configfs_item_operations gpio_sim_bank_config_item_ops = {
+ .release = gpio_sim_bank_config_group_release,
+};
+
+static struct configfs_group_operations gpio_sim_bank_config_group_ops = {
+ .make_group = gpio_sim_bank_config_make_line_group,
+};
+
+static const struct config_item_type gpio_sim_bank_config_group_type = {
+ .ct_item_ops = &gpio_sim_bank_config_item_ops,
+ .ct_group_ops = &gpio_sim_bank_config_group_ops,
+ .ct_attrs = gpio_sim_bank_config_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *
+gpio_sim_device_config_make_bank_group(struct config_group *group,
+ const char *name)
+{
+ struct gpio_sim_device *dev = to_gpio_sim_device(&group->cg_item);
+ struct gpio_sim_bank *bank;
+
+ mutex_lock(&dev->lock);
+
+ if (gpio_sim_device_is_live_unlocked(dev)) {
+ mutex_unlock(&dev->lock);
+ return ERR_PTR(-EBUSY);
+ }
+
+ bank = kzalloc(sizeof(*bank), GFP_KERNEL);
+ if (!bank) {
+ mutex_unlock(&dev->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ config_group_init_type_name(&bank->group, name,
+ &gpio_sim_bank_config_group_type);
+ bank->num_lines = 1;
+ bank->parent = dev;
+ INIT_LIST_HEAD(&bank->line_list);
+ list_add_tail(&bank->siblings, &dev->bank_list);
+
+ mutex_unlock(&dev->lock);
+
+ return &bank->group;
+}
+
+static void gpio_sim_device_config_group_release(struct config_item *item)
+{
+ struct gpio_sim_device *dev = to_gpio_sim_device(item);
+
+ mutex_lock(&dev->lock);
+ if (gpio_sim_device_is_live_unlocked(dev))
+ gpio_sim_device_deactivate_unlocked(dev);
+ mutex_unlock(&dev->lock);
+
+ mutex_destroy(&dev->lock);
+ ida_free(&gpio_sim_ida, dev->id);
+ kfree(dev);
+}
+
+static struct configfs_item_operations gpio_sim_device_config_item_ops = {
+ .release = gpio_sim_device_config_group_release,
+};
+
+static struct configfs_group_operations gpio_sim_device_config_group_ops = {
+ .make_group = gpio_sim_device_config_make_bank_group,
+};
+
+static const struct config_item_type gpio_sim_device_config_group_type = {
+ .ct_item_ops = &gpio_sim_device_config_item_ops,
+ .ct_group_ops = &gpio_sim_device_config_group_ops,
+ .ct_attrs = gpio_sim_device_config_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *
+gpio_sim_config_make_device_group(struct config_group *group, const char *name)
+{
+ struct gpio_sim_device *dev;
+ int id;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ id = ida_alloc(&gpio_sim_ida, GFP_KERNEL);
+ if (id < 0) {
+ kfree(dev);
+ return ERR_PTR(id);
+ }
+
+ config_group_init_type_name(&dev->group, name,
+ &gpio_sim_device_config_group_type);
+ dev->id = id;
+ mutex_init(&dev->lock);
+ INIT_LIST_HEAD(&dev->bank_list);
+
+ dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call;
+ init_completion(&dev->probe_completion);
+
+ return &dev->group;
+}
+
+static struct configfs_group_operations gpio_sim_config_group_ops = {
+ .make_group = gpio_sim_config_make_device_group,
+};
+
+static const struct config_item_type gpio_sim_config_type = {
+ .ct_group_ops = &gpio_sim_config_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem gpio_sim_config_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "gpio-sim",
+ .ci_type = &gpio_sim_config_type,
+ },
+ },
+};
+
+static int __init gpio_sim_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&gpio_sim_driver);
+ if (ret) {
+ pr_err("Error %d while registering the platform driver\n", ret);
+ return ret;
+ }
+
+ config_group_init(&gpio_sim_config_subsys.su_group);
+ mutex_init(&gpio_sim_config_subsys.su_mutex);
+ ret = configfs_register_subsystem(&gpio_sim_config_subsys);
+ if (ret) {
+ pr_err("Error %d while registering the configfs subsystem %s\n",
+ ret, gpio_sim_config_subsys.su_group.cg_item.ci_namebuf);
+ mutex_destroy(&gpio_sim_config_subsys.su_mutex);
+ platform_driver_unregister(&gpio_sim_driver);
+ return ret;
+ }
+
+ return 0;
+}
+module_init(gpio_sim_init);
+
+static void __exit gpio_sim_exit(void)
+{
+ configfs_unregister_subsystem(&gpio_sim_config_subsys);
+ mutex_destroy(&gpio_sim_config_subsys.su_mutex);
+ platform_driver_unregister(&gpio_sim_driver);
+}
+module_exit(gpio_sim_exit);
+
+MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl");
+MODULE_DESCRIPTION("GPIO Simulator Module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c
index 006a7e6a75f2..f8c5e9fc4bac 100644
--- a/drivers/gpio/gpio-siox.c
+++ b/drivers/gpio/gpio-siox.c
@@ -15,7 +15,7 @@ struct gpio_siox_ddata {
u8 setdata[1];
u8 getdata[3];
- spinlock_t irqlock;
+ raw_spinlock_t irqlock;
u32 irq_enable;
u32 irq_status;
u32 irq_type[20];
@@ -44,7 +44,7 @@ static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
mutex_lock(&ddata->lock);
- spin_lock_irq(&ddata->irqlock);
+ raw_spin_lock_irq(&ddata->irqlock);
for (offset = 0; offset < 12; ++offset) {
unsigned int bitpos = 11 - offset;
@@ -66,7 +66,7 @@ static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
trigger = ddata->irq_status & ddata->irq_enable;
- spin_unlock_irq(&ddata->irqlock);
+ raw_spin_unlock_irq(&ddata->irqlock);
ddata->getdata[0] = buf[0];
ddata->getdata[1] = buf[1];
@@ -84,9 +84,9 @@ static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
* handler of the irq chip. But it doesn't, so we have
* to clean the irq_status here.
*/
- spin_lock_irq(&ddata->irqlock);
+ raw_spin_lock_irq(&ddata->irqlock);
ddata->irq_status &= ~(1 << offset);
- spin_unlock_irq(&ddata->irqlock);
+ raw_spin_unlock_irq(&ddata->irqlock);
handle_nested_irq(irq);
}
@@ -101,9 +101,9 @@ static void gpio_siox_irq_ack(struct irq_data *d)
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
- spin_lock_irq(&ddata->irqlock);
+ raw_spin_lock(&ddata->irqlock);
ddata->irq_status &= ~(1 << d->hwirq);
- spin_unlock_irq(&ddata->irqlock);
+ raw_spin_unlock(&ddata->irqlock);
}
static void gpio_siox_irq_mask(struct irq_data *d)
@@ -112,9 +112,9 @@ static void gpio_siox_irq_mask(struct irq_data *d)
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
- spin_lock_irq(&ddata->irqlock);
+ raw_spin_lock(&ddata->irqlock);
ddata->irq_enable &= ~(1 << d->hwirq);
- spin_unlock_irq(&ddata->irqlock);
+ raw_spin_unlock(&ddata->irqlock);
}
static void gpio_siox_irq_unmask(struct irq_data *d)
@@ -123,9 +123,9 @@ static void gpio_siox_irq_unmask(struct irq_data *d)
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
- spin_lock_irq(&ddata->irqlock);
+ raw_spin_lock(&ddata->irqlock);
ddata->irq_enable |= 1 << d->hwirq;
- spin_unlock_irq(&ddata->irqlock);
+ raw_spin_unlock(&ddata->irqlock);
}
static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
@@ -134,9 +134,9 @@ static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
- spin_lock_irq(&ddata->irqlock);
+ raw_spin_lock(&ddata->irqlock);
ddata->irq_type[d->hwirq] = type;
- spin_unlock_irq(&ddata->irqlock);
+ raw_spin_unlock(&ddata->irqlock);
return 0;
}
@@ -203,9 +203,9 @@ static int gpio_siox_direction_output(struct gpio_chip *chip,
static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset)
{
if (offset < 12)
- return 1; /* input */
+ return GPIO_LINE_DIRECTION_IN;
else
- return 0; /* output */
+ return GPIO_LINE_DIRECTION_OUT;
}
static int gpio_siox_probe(struct siox_device *sdevice)
@@ -222,7 +222,7 @@ static int gpio_siox_probe(struct siox_device *sdevice)
dev_set_drvdata(dev, ddata);
mutex_init(&ddata->lock);
- spin_lock_init(&ddata->irqlock);
+ raw_spin_lock_init(&ddata->irqlock);
ddata->gchip.base = -1;
ddata->gchip.can_sleep = 1;
@@ -245,6 +245,7 @@ static int gpio_siox_probe(struct siox_device *sdevice)
girq->chip = &ddata->ichip;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_level_irq;
+ girq->threaded = true;
ret = devm_gpiochip_add_data(dev, &ddata->gchip, NULL);
if (ret)
diff --git a/drivers/gpio/gpio-sl28cpld.c b/drivers/gpio/gpio-sl28cpld.c
new file mode 100644
index 000000000000..52404736ac86
--- /dev/null
+++ b/drivers/gpio/gpio-sl28cpld.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sl28cpld GPIO driver
+ *
+ * Copyright 2020 Michael Walle <michael@walle.cc>
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* GPIO flavor */
+#define GPIO_REG_DIR 0x00
+#define GPIO_REG_OUT 0x01
+#define GPIO_REG_IN 0x02
+#define GPIO_REG_IE 0x03
+#define GPIO_REG_IP 0x04
+
+/* input-only flavor */
+#define GPI_REG_IN 0x00
+
+/* output-only flavor */
+#define GPO_REG_OUT 0x00
+
+enum sl28cpld_gpio_type {
+ SL28CPLD_GPIO = 1,
+ SL28CPLD_GPI,
+ SL28CPLD_GPO,
+};
+
+static const struct regmap_irq sl28cpld_gpio_irqs[] = {
+ REGMAP_IRQ_REG_LINE(0, 8),
+ REGMAP_IRQ_REG_LINE(1, 8),
+ REGMAP_IRQ_REG_LINE(2, 8),
+ REGMAP_IRQ_REG_LINE(3, 8),
+ REGMAP_IRQ_REG_LINE(4, 8),
+ REGMAP_IRQ_REG_LINE(5, 8),
+ REGMAP_IRQ_REG_LINE(6, 8),
+ REGMAP_IRQ_REG_LINE(7, 8),
+};
+
+static int sl28cpld_gpio_irq_init(struct platform_device *pdev,
+ unsigned int base,
+ struct gpio_regmap_config *config)
+{
+ struct regmap_irq_chip_data *irq_data;
+ struct regmap_irq_chip *irq_chip;
+ struct device *dev = &pdev->dev;
+ int irq, ret;
+
+ if (!device_property_read_bool(dev, "interrupt-controller"))
+ return 0;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL);
+ if (!irq_chip)
+ return -ENOMEM;
+
+ irq_chip->name = "sl28cpld-gpio-irq";
+ irq_chip->irqs = sl28cpld_gpio_irqs;
+ irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs);
+ irq_chip->num_regs = 1;
+ irq_chip->status_base = base + GPIO_REG_IP;
+ irq_chip->mask_base = base + GPIO_REG_IE;
+ irq_chip->mask_invert = true;
+ irq_chip->ack_base = base + GPIO_REG_IP;
+
+ ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
+ config->regmap, irq,
+ IRQF_SHARED | IRQF_ONESHOT,
+ 0, irq_chip, &irq_data);
+ if (ret)
+ return ret;
+
+ config->irq_domain = regmap_irq_get_domain(irq_data);
+
+ return 0;
+}
+
+static int sl28cpld_gpio_probe(struct platform_device *pdev)
+{
+ struct gpio_regmap_config config = {0};
+ enum sl28cpld_gpio_type type;
+ struct regmap *regmap;
+ u32 base;
+ int ret;
+
+ if (!pdev->dev.parent)
+ return -ENODEV;
+
+ type = (uintptr_t)device_get_match_data(&pdev->dev);
+ if (!type)
+ return -ENODEV;
+
+ ret = device_property_read_u32(&pdev->dev, "reg", &base);
+ if (ret)
+ return -EINVAL;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ config.regmap = regmap;
+ config.parent = &pdev->dev;
+ config.ngpio = 8;
+
+ switch (type) {
+ case SL28CPLD_GPIO:
+ config.reg_dat_base = base + GPIO_REG_IN;
+ config.reg_set_base = base + GPIO_REG_OUT;
+ /* reg_dir_out_base might be zero */
+ config.reg_dir_out_base = GPIO_REGMAP_ADDR(base + GPIO_REG_DIR);
+
+ /* This type supports interrupts */
+ ret = sl28cpld_gpio_irq_init(pdev, base, &config);
+ if (ret)
+ return ret;
+ break;
+ case SL28CPLD_GPO:
+ config.reg_set_base = base + GPO_REG_OUT;
+ break;
+ case SL28CPLD_GPI:
+ config.reg_dat_base = base + GPI_REG_IN;
+ break;
+ default:
+ dev_err(&pdev->dev, "unknown type %d\n", type);
+ return -ENODEV;
+ }
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
+}
+
+static const struct of_device_id sl28cpld_gpio_of_match[] = {
+ { .compatible = "kontron,sl28cpld-gpio", .data = (void *)SL28CPLD_GPIO },
+ { .compatible = "kontron,sl28cpld-gpi", .data = (void *)SL28CPLD_GPI },
+ { .compatible = "kontron,sl28cpld-gpo", .data = (void *)SL28CPLD_GPO },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sl28cpld_gpio_of_match);
+
+static struct platform_driver sl28cpld_gpio_driver = {
+ .probe = sl28cpld_gpio_probe,
+ .driver = {
+ .name = "sl28cpld-gpio",
+ .of_match_table = sl28cpld_gpio_of_match,
+ },
+};
+module_platform_driver(sl28cpld_gpio_driver);
+
+MODULE_DESCRIPTION("sl28cpld GPIO Driver");
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c
index aed988e78251..c2a2c76c1652 100644
--- a/drivers/gpio/gpio-sodaville.c
+++ b/drivers/gpio/gpio-sodaville.c
@@ -84,7 +84,7 @@ static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data)
return IRQ_NONE;
for_each_set_bit(irq_bit, &irq_stat, 32)
- generic_handle_irq(irq_find_mapping(sd->id, irq_bit));
+ generic_handle_domain_irq(sd->id, irq_bit);
return IRQ_HANDLED;
}
diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c
index 6eca531b7d96..49aac2bb8d2c 100644
--- a/drivers/gpio/gpio-spear-spics.c
+++ b/drivers/gpio/gpio-spear-spics.c
@@ -122,7 +122,6 @@ static int spics_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct spear_spics *spics;
- int ret;
spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL);
if (!spics)
@@ -148,8 +147,6 @@ static int spics_gpio_probe(struct platform_device *pdev)
&spics->cs_enable_shift))
goto err_dt_data;
- platform_set_drvdata(pdev, spics);
-
spics->chip.ngpio = NUM_OF_GPIO;
spics->chip.base = -1;
spics->chip.request = spics_request;
@@ -163,14 +160,7 @@ static int spics_gpio_probe(struct platform_device *pdev)
spics->chip.owner = THIS_MODULE;
spics->last_off = -1;
- ret = devm_gpiochip_add_data(&pdev->dev, &spics->chip, spics);
- if (ret) {
- dev_err(&pdev->dev, "unable to add gpio chip\n");
- return ret;
- }
-
- dev_info(&pdev->dev, "spear spics registered\n");
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &spics->chip, spics);
err_dt_data:
dev_err(&pdev->dev, "DT probe failed\n");
diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c
index f5c8b3a351d5..9bff63990eee 100644
--- a/drivers/gpio/gpio-sprd.c
+++ b/drivers/gpio/gpio-sprd.c
@@ -149,17 +149,20 @@ static int sprd_gpio_irq_set_type(struct irq_data *data,
sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 1);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IC, 1);
irq_set_handler_locked(data, handle_edge_irq);
break;
case IRQ_TYPE_EDGE_FALLING:
sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IC, 1);
irq_set_handler_locked(data, handle_edge_irq);
break;
case IRQ_TYPE_EDGE_BOTH:
sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 1);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IC, 1);
irq_set_handler_locked(data, handle_edge_irq);
break;
case IRQ_TYPE_LEVEL_HIGH:
@@ -186,7 +189,7 @@ static void sprd_gpio_irq_handler(struct irq_desc *desc)
struct gpio_chip *chip = irq_desc_get_handler_data(desc);
struct irq_chip *ic = irq_desc_get_chip(desc);
struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
- u32 bank, n, girq;
+ u32 bank, n;
chained_irq_enter(ic, desc);
@@ -195,13 +198,9 @@ static void sprd_gpio_irq_handler(struct irq_desc *desc)
unsigned long reg = readl_relaxed(base + SPRD_GPIO_MIS) &
SPRD_GPIO_BANK_MASK;
- for_each_set_bit(n, &reg, SPRD_GPIO_BANK_NR) {
- girq = irq_find_mapping(chip->irq.domain,
- bank * SPRD_GPIO_BANK_NR + n);
-
- generic_handle_irq(girq);
- }
-
+ for_each_set_bit(n, &reg, SPRD_GPIO_BANK_NR)
+ generic_handle_domain_irq(chip->irq.domain,
+ bank * SPRD_GPIO_BANK_NR + n);
}
chained_irq_exit(ic, desc);
}
@@ -219,17 +218,14 @@ static int sprd_gpio_probe(struct platform_device *pdev)
{
struct gpio_irq_chip *irq;
struct sprd_gpio *sprd_gpio;
- int ret;
sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
if (!sprd_gpio)
return -ENOMEM;
sprd_gpio->irq = platform_get_irq(pdev, 0);
- if (sprd_gpio->irq < 0) {
- dev_err(&pdev->dev, "Failed to get GPIO interrupt.\n");
+ if (sprd_gpio->irq < 0)
return sprd_gpio->irq;
- }
sprd_gpio->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(sprd_gpio->base))
@@ -241,7 +237,6 @@ static int sprd_gpio_probe(struct platform_device *pdev)
sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
sprd_gpio->chip.base = -1;
sprd_gpio->chip.parent = &pdev->dev;
- sprd_gpio->chip.of_node = pdev->dev.of_node;
sprd_gpio->chip.request = sprd_gpio_request;
sprd_gpio->chip.free = sprd_gpio_free;
sprd_gpio->chip.get = sprd_gpio_get;
@@ -258,14 +253,7 @@ static int sprd_gpio_probe(struct platform_device *pdev)
irq->num_parents = 1;
irq->parents = &sprd_gpio->irq;
- ret = devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, sprd_gpio);
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
}
static const struct of_device_id sprd_gpio_of_match[] = {
diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c
index a74bb97a41e2..e07cca0f8d35 100644
--- a/drivers/gpio/gpio-sta2x11.c
+++ b/drivers/gpio/gpio-sta2x11.c
@@ -324,7 +324,7 @@ static int gsta_alloc_irq_chip(struct gsta_gpio *chip)
if (rv)
return rv;
- /* Set up all all 128 interrupts: code from setup_generic_chip */
+ /* Set up all 128 interrupts: code from setup_generic_chip */
{
struct irq_chip_type *ct = gc->chip_types;
int i, j;
@@ -398,15 +398,7 @@ static int gsta_probe(struct platform_device *dev)
return err;
}
- err = devm_gpiochip_add_data(&dev->dev, &chip->gpio, chip);
- if (err < 0) {
- dev_err(&dev->dev, "sta2x11 gpio: Can't register (%i)\n",
- -err);
- return err;
- }
-
- platform_set_drvdata(dev, chip);
- return 0;
+ return devm_gpiochip_add_data(&dev->dev, &chip->gpio, chip);
}
static struct platform_driver sta2x11_gpio_platform_driver = {
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index dbf9cbe36b2b..0fa4f0a93378 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -84,7 +84,10 @@ static int stmpe_gpio_get_direction(struct gpio_chip *chip,
if (ret < 0)
return ret;
- return !(ret & mask);
+ if (ret & mask)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int stmpe_gpio_direction_output(struct gpio_chip *chip,
@@ -305,7 +308,7 @@ static void stmpe_dbg_show_one(struct seq_file *s,
if (ret < 0)
return;
edge_det = !!(ret & mask);
- /* fall through */
+ fallthrough;
case STMPE1801:
rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB + bank];
fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB + bank];
@@ -318,7 +321,7 @@ static void stmpe_dbg_show_one(struct seq_file *s,
if (ret < 0)
return;
fall = !!(ret & mask);
- /* fall through */
+ fallthrough;
case STMPE801:
case STMPE1600:
irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB + bank];
@@ -429,6 +432,28 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
return IRQ_HANDLED;
}
+static void stmpe_init_irq_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc);
+ int i;
+
+ if (!stmpe_gpio->norequest_mask)
+ return;
+
+ /* Forbid unused lines to be mapped as IRQs */
+ for (i = 0; i < sizeof(u32); i++) {
+ if (stmpe_gpio->norequest_mask & BIT(i))
+ clear_bit(i, valid_mask);
+ }
+}
+
+static void stmpe_gpio_disable(void *stmpe)
+{
+ stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
+}
+
static int stmpe_gpio_probe(struct platform_device *pdev)
{
struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
@@ -441,7 +466,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
return -EINVAL;
}
- stmpe_gpio = kzalloc(sizeof(*stmpe_gpio), GFP_KERNEL);
+ stmpe_gpio = devm_kzalloc(&pdev->dev, sizeof(*stmpe_gpio), GFP_KERNEL);
if (!stmpe_gpio)
return -ENOMEM;
@@ -452,7 +477,6 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
stmpe_gpio->chip = template_chip;
stmpe_gpio->chip.ngpio = stmpe->num_gpios;
stmpe_gpio->chip.parent = &pdev->dev;
- stmpe_gpio->chip.of_node = np;
stmpe_gpio->chip.base = -1;
if (IS_ENABLED(CONFIG_DEBUG_FS))
@@ -460,8 +484,6 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
of_property_read_u32(np, "st,norequest-mask",
&stmpe_gpio->norequest_mask);
- if (stmpe_gpio->norequest_mask)
- stmpe_gpio->chip.irq.need_valid_mask = true;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -471,56 +493,36 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
if (ret)
- goto out_free;
+ return ret;
- ret = gpiochip_add_data(&stmpe_gpio->chip, stmpe_gpio);
- if (ret) {
- dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
- goto out_disable;
- }
+ ret = devm_add_action_or_reset(&pdev->dev, stmpe_gpio_disable, stmpe);
+ if (ret)
+ return ret;
if (irq > 0) {
+ struct gpio_irq_chip *girq;
+
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
stmpe_gpio_irq, IRQF_ONESHOT,
"stmpe-gpio", stmpe_gpio);
if (ret) {
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
- goto out_disable;
+ return ret;
}
- if (stmpe_gpio->norequest_mask) {
- int i;
- /* Forbid unused lines to be mapped as IRQs */
- for (i = 0; i < sizeof(u32); i++)
- if (stmpe_gpio->norequest_mask & BIT(i))
- clear_bit(i, stmpe_gpio->chip.irq.valid_mask);
- }
- ret = gpiochip_irqchip_add_nested(&stmpe_gpio->chip,
- &stmpe_gpio_irq_chip,
- 0,
- handle_simple_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(&pdev->dev,
- "could not connect irqchip to gpiochip\n");
- goto out_disable;
- }
-
- gpiochip_set_nested_irqchip(&stmpe_gpio->chip,
- &stmpe_gpio_irq_chip,
- irq);
+ girq = &stmpe_gpio->chip.irq;
+ girq->chip = &stmpe_gpio_irq_chip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->threaded = true;
+ girq->init_valid_mask = stmpe_init_irq_valid_mask;
}
- platform_set_drvdata(pdev, stmpe_gpio);
-
- return 0;
-
-out_disable:
- stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
- gpiochip_remove(&stmpe_gpio->chip);
-out_free:
- kfree(stmpe_gpio);
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &stmpe_gpio->chip, stmpe_gpio);
}
static struct platform_driver stmpe_gpio_driver = {
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
index 9e23a5ae8108..0ce1543426a4 100644
--- a/drivers/gpio/gpio-stp-xway.c
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -41,7 +41,10 @@
#define XWAY_STP_4HZ BIT(23)
#define XWAY_STP_8HZ BIT(24)
#define XWAY_STP_10HZ (BIT(24) | BIT(23))
-#define XWAY_STP_SPEED_MASK (0xf << 23)
+#define XWAY_STP_SPEED_MASK (BIT(23) | BIT(24) | BIT(25) | BIT(26) | BIT(27))
+
+#define XWAY_STP_FPIS_VALUE BIT(21)
+#define XWAY_STP_FPIS_MASK (BIT(20) | BIT(21))
/* clock source for automatic update */
#define XWAY_STP_UPD_FPI BIT(31)
@@ -54,7 +57,9 @@
/* 2 groups of 3 bits can be driven by the phys */
#define XWAY_STP_PHY_MASK 0x7
#define XWAY_STP_PHY1_SHIFT 27
-#define XWAY_STP_PHY2_SHIFT 15
+#define XWAY_STP_PHY2_SHIFT 3
+#define XWAY_STP_PHY3_SHIFT 6
+#define XWAY_STP_PHY4_SHIFT 15
/* STP has 3 groups of 8 bits */
#define XWAY_STP_GROUP0 BIT(0)
@@ -80,6 +85,8 @@ struct xway_stp {
u8 dsl; /* the 2 LSBs can be driven by the dsl core */
u8 phy1; /* 3 bits can be driven by phy1 */
u8 phy2; /* 3 bits can be driven by phy2 */
+ u8 phy3; /* 3 bits can be driven by phy3 */
+ u8 phy4; /* 3 bits can be driven by phy4 */
u8 reserved; /* mask out the hw driven bits in gpio_request */
};
@@ -114,7 +121,8 @@ static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val)
else
chip->shadow &= ~BIT(gpio);
xway_stp_w32(chip->virt, chip->shadow, XWAY_STP_CPU0);
- xway_stp_w32_mask(chip->virt, 0, XWAY_STP_CON_SWU, XWAY_STP_CON0);
+ if (!chip->reserved)
+ xway_stp_w32_mask(chip->virt, 0, XWAY_STP_CON_SWU, XWAY_STP_CON0);
}
/**
@@ -188,16 +196,37 @@ static void xway_stp_hw_init(struct xway_stp *chip)
chip->phy2 << XWAY_STP_PHY2_SHIFT,
XWAY_STP_CON1);
+ if (of_machine_is_compatible("lantiq,grx390")
+ || of_machine_is_compatible("lantiq,ar10")) {
+ xway_stp_w32_mask(chip->virt,
+ XWAY_STP_PHY_MASK << XWAY_STP_PHY3_SHIFT,
+ chip->phy3 << XWAY_STP_PHY3_SHIFT,
+ XWAY_STP_CON1);
+ }
+
+ if (of_machine_is_compatible("lantiq,grx390")) {
+ xway_stp_w32_mask(chip->virt,
+ XWAY_STP_PHY_MASK << XWAY_STP_PHY4_SHIFT,
+ chip->phy4 << XWAY_STP_PHY4_SHIFT,
+ XWAY_STP_CON1);
+ }
+
/* mask out the hw driven bits in gpio_request */
- chip->reserved = (chip->phy2 << 5) | (chip->phy1 << 2) | chip->dsl;
+ chip->reserved = (chip->phy4 << 11) | (chip->phy3 << 8) | (chip->phy2 << 5)
+ | (chip->phy1 << 2) | chip->dsl;
/*
* if we have pins that are driven by hw, we need to tell the stp what
* clock to use as a timer.
*/
- if (chip->reserved)
+ if (chip->reserved) {
xway_stp_w32_mask(chip->virt, XWAY_STP_UPD_MASK,
XWAY_STP_UPD_FPI, XWAY_STP_CON1);
+ xway_stp_w32_mask(chip->virt, XWAY_STP_SPEED_MASK,
+ XWAY_STP_10HZ, XWAY_STP_CON1);
+ xway_stp_w32_mask(chip->virt, XWAY_STP_FPIS_MASK,
+ XWAY_STP_FPIS_VALUE, XWAY_STP_CON1);
+ }
}
static int xway_stp_probe(struct platform_device *pdev)
@@ -242,13 +271,26 @@ static int xway_stp_probe(struct platform_device *pdev)
/* find out which gpios are controlled by the phys */
if (of_machine_is_compatible("lantiq,ar9") ||
of_machine_is_compatible("lantiq,gr9") ||
- of_machine_is_compatible("lantiq,vr9")) {
+ of_machine_is_compatible("lantiq,vr9") ||
+ of_machine_is_compatible("lantiq,ar10") ||
+ of_machine_is_compatible("lantiq,grx390")) {
if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy1", &phy))
chip->phy1 = phy & XWAY_STP_PHY_MASK;
if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy2", &phy))
chip->phy2 = phy & XWAY_STP_PHY_MASK;
}
+ if (of_machine_is_compatible("lantiq,ar10") ||
+ of_machine_is_compatible("lantiq,grx390")) {
+ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy3", &phy))
+ chip->phy3 = phy & XWAY_STP_PHY_MASK;
+ }
+
+ if (of_machine_is_compatible("lantiq,grx390")) {
+ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy4", &phy))
+ chip->phy4 = phy & XWAY_STP_PHY_MASK;
+ }
+
/* check which edge trigger we should use, default to a falling edge */
if (!of_find_property(pdev->dev.of_node, "lantiq,rising", NULL))
chip->edge = XWAY_STP_FALLING;
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
index 31f332074d7d..6076937b18e7 100644
--- a/drivers/gpio/gpio-syscon.c
+++ b/drivers/gpio/gpio-syscon.c
@@ -24,21 +24,20 @@
/**
* struct syscon_gpio_data - Configuration for the device.
- * compatible: SYSCON driver compatible string.
- * flags: Set of GPIO_SYSCON_FEAT_ flags:
+ * @compatible: SYSCON driver compatible string.
+ * @flags: Set of GPIO_SYSCON_FEAT_ flags:
* GPIO_SYSCON_FEAT_IN: GPIOs supports input,
* GPIO_SYSCON_FEAT_OUT: GPIOs supports output,
* GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction.
- * bit_count: Number of bits used as GPIOs.
- * dat_bit_offset: Offset (in bits) to the first GPIO bit.
- * dir_bit_offset: Optional offset (in bits) to the first bit to switch
+ * @bit_count: Number of bits used as GPIOs.
+ * @dat_bit_offset: Offset (in bits) to the first GPIO bit.
+ * @dir_bit_offset: Optional offset (in bits) to the first bit to switch
* GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
- * set: HW specific callback to assigns output value
+ * @set: HW specific callback to assigns output value
* for signal "offset"
*/
struct syscon_gpio_data {
- const char *compatible;
unsigned int flags;
unsigned int bit_count;
unsigned int dat_bit_offset;
@@ -125,7 +124,6 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
static const struct syscon_gpio_data clps711x_mctrl_gpio = {
/* ARM CLPS711X SYSFLG1 Bits 8-10 */
- .compatible = "cirrus,ep7209-syscon1",
.flags = GPIO_SYSCON_FEAT_IN,
.bit_count = 3,
.dat_bit_offset = 0x40 * 8 + 8,
@@ -182,7 +180,6 @@ static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
static const struct syscon_gpio_data keystone_dsp_gpio = {
/* ARM Keystone 2 */
- .compatible = NULL,
.flags = GPIO_SYSCON_FEAT_OUT,
.bit_count = 28,
.dat_bit_offset = 4,
@@ -219,33 +216,25 @@ static int syscon_gpio_probe(struct platform_device *pdev)
priv->data = of_device_get_match_data(dev);
- if (priv->data->compatible) {
- priv->syscon = syscon_regmap_lookup_by_compatible(
- priv->data->compatible);
- if (IS_ERR(priv->syscon))
- return PTR_ERR(priv->syscon);
- } else {
- priv->syscon =
- syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev");
- if (IS_ERR(priv->syscon) && np->parent)
- priv->syscon = syscon_node_to_regmap(np->parent);
- if (IS_ERR(priv->syscon))
- return PTR_ERR(priv->syscon);
-
- ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
- &priv->dreg_offset);
- if (ret)
- dev_err(dev, "can't read the data register offset!\n");
-
- priv->dreg_offset <<= 3;
-
- ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
- &priv->dir_reg_offset);
- if (ret)
- dev_dbg(dev, "can't read the dir register offset!\n");
-
- priv->dir_reg_offset <<= 3;
- }
+ priv->syscon = syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev");
+ if (IS_ERR(priv->syscon) && np->parent)
+ priv->syscon = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(priv->syscon))
+ return PTR_ERR(priv->syscon);
+
+ ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
+ &priv->dreg_offset);
+ if (ret)
+ dev_err(dev, "can't read the data register offset!\n");
+
+ priv->dreg_offset <<= 3;
+
+ ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
+ &priv->dir_reg_offset);
+ if (ret)
+ dev_dbg(dev, "can't read the dir register offset!\n");
+
+ priv->dir_reg_offset <<= 3;
priv->chip.parent = dev;
priv->chip.owner = THIS_MODULE;
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
index bd1f3f775ce9..de6afa3f9716 100644
--- a/drivers/gpio/gpio-tb10x.c
+++ b/drivers/gpio/gpio-tb10x.c
@@ -62,14 +62,14 @@ static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
u32 r;
unsigned long flags;
- spin_lock_irqsave(&gpio->gc.bgpio_lock, flags);
+ raw_spin_lock_irqsave(&gpio->gc.bgpio_lock, flags);
r = tb10x_reg_read(gpio, offs);
r = (r & ~mask) | (val & mask);
tb10x_reg_write(gpio, offs, r);
- spin_unlock_irqrestore(&gpio->gc.bgpio_lock, flags);
+ raw_spin_unlock_irqrestore(&gpio->gc.bgpio_lock, flags);
}
static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -100,7 +100,7 @@ static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
int i;
for_each_set_bit(i, &bits, 32)
- generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i));
+ generic_handle_domain_irq(tb10x_gpio->domain, i);
return IRQ_HANDLED;
}
@@ -171,10 +171,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
struct irq_chip_generic *gc;
ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(dev, "No interrupt specified.\n");
+ if (ret < 0)
return ret;
- }
tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
tb10x_gpio->irq = ret;
@@ -245,4 +243,3 @@ static struct platform_driver tb10x_gpio_driver = {
module_platform_driver(tb10x_gpio_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("tb10x gpio.");
-MODULE_VERSION("0.0.1");
diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c
index 75b1135b383a..443fe975bf13 100644
--- a/drivers/gpio/gpio-tc3589x.c
+++ b/drivers/gpio/gpio-tc3589x.c
@@ -19,9 +19,9 @@
* These registers are modified under the irq bus lock and cached to avoid
* unnecessary writes in bus_sync_unlock.
*/
-enum { REG_IBE, REG_IEV, REG_IS, REG_IE };
+enum { REG_IBE, REG_IEV, REG_IS, REG_IE, REG_DIRECT };
-#define CACHE_NR_REGS 4
+#define CACHE_NR_REGS 5
#define CACHE_NR_BANKS 3
struct tc3589x_gpio {
@@ -97,7 +97,10 @@ static int tc3589x_gpio_get_direction(struct gpio_chip *chip,
if (ret < 0)
return ret;
- return !(ret & BIT(pos));
+ if (ret & BIT(pos))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int tc3589x_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
@@ -197,6 +200,7 @@ static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)
[REG_IEV] = TC3589x_GPIOIEV0,
[REG_IS] = TC3589x_GPIOIS0,
[REG_IE] = TC3589x_GPIOIE0,
+ [REG_DIRECT] = TC3589x_DIRECT0,
};
int i, j;
@@ -209,7 +213,7 @@ static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)
continue;
tc3589x_gpio->oldregs[i][j] = new;
- tc3589x_reg_write(tc3589x, regmap[i] + j * 8, new);
+ tc3589x_reg_write(tc3589x, regmap[i] + j, new);
}
}
@@ -225,6 +229,7 @@ static void tc3589x_gpio_irq_mask(struct irq_data *d)
int mask = BIT(offset % 8);
tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask;
+ tc3589x_gpio->regs[REG_DIRECT][regoffset] |= mask;
}
static void tc3589x_gpio_irq_unmask(struct irq_data *d)
@@ -236,6 +241,7 @@ static void tc3589x_gpio_irq_unmask(struct irq_data *d)
int mask = BIT(offset % 8);
tc3589x_gpio->regs[REG_IE][regoffset] |= mask;
+ tc3589x_gpio->regs[REG_DIRECT][regoffset] &= ~mask;
}
static struct irq_chip tc3589x_gpio_irq_chip = {
@@ -286,6 +292,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
struct device_node *np = pdev->dev.of_node;
struct tc3589x_gpio *tc3589x_gpio;
+ struct gpio_irq_chip *girq;
int ret;
int irq;
@@ -312,7 +319,16 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
tc3589x_gpio->chip.parent = &pdev->dev;
tc3589x_gpio->chip.base = -1;
- tc3589x_gpio->chip.of_node = np;
+
+ girq = &tc3589x_gpio->chip.irq;
+ girq->chip = &tc3589x_gpio_irq_chip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->threaded = true;
/* Bring the GPIO module out of reset */
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
@@ -320,6 +336,17 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
+ /* For tc35894, have to disable Direct KBD interrupts,
+ * else IRQST will always be 0x20, IRQN low level, can't
+ * clear the irq status.
+ * TODO: need more test on other tc3589x chip.
+ *
+ */
+ ret = tc3589x_reg_write(tc3589x, TC3589x_DKBDMSK,
+ TC3589x_DKBDMSK_ELINT | TC3589x_DKBDMSK_EINT);
+ if (ret < 0)
+ return ret;
+
ret = devm_request_threaded_irq(&pdev->dev,
irq, NULL, tc3589x_gpio_irq,
IRQF_ONESHOT, "tc3589x-gpio",
@@ -329,31 +356,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
return ret;
}
- ret = devm_gpiochip_add_data(&pdev->dev, &tc3589x_gpio->chip,
- tc3589x_gpio);
- if (ret) {
- dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
- return ret;
- }
-
- ret = gpiochip_irqchip_add_nested(&tc3589x_gpio->chip,
- &tc3589x_gpio_irq_chip,
- 0,
- handle_simple_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(&pdev->dev,
- "could not connect irqchip to gpiochip\n");
- return ret;
- }
-
- gpiochip_set_nested_irqchip(&tc3589x_gpio->chip,
- &tc3589x_gpio_irq_chip,
- irq);
-
- platform_set_drvdata(pdev, tc3589x_gpio);
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &tc3589x_gpio->chip, tc3589x_gpio);
}
static struct platform_driver tc3589x_gpio_driver = {
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 0f59161a4701..ff2d2a1f9c73 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -60,9 +60,16 @@ struct tegra_gpio_info;
struct tegra_gpio_bank {
unsigned int bank;
- unsigned int irq;
- spinlock_t lvl_lock[4];
- spinlock_t dbc_lock[4]; /* Lock for updating debounce count register */
+
+ /*
+ * IRQ-core code uses raw locking, and thus, nested locking also
+ * should be raw in order not to trip spinlock debug warnings.
+ */
+ raw_spinlock_t lvl_lock[4];
+
+ /* Lock for updating debounce count register */
+ spinlock_t dbc_lock[4];
+
#ifdef CONFIG_PM_SLEEP
u32 cnf[4];
u32 out[4];
@@ -73,7 +80,6 @@ struct tegra_gpio_bank {
u32 dbc_enb[4];
#endif
u32 dbc_cnt[4];
- struct tegra_gpio_info *tgi;
};
struct tegra_gpio_soc_config {
@@ -85,23 +91,23 @@ struct tegra_gpio_soc_config {
struct tegra_gpio_info {
struct device *dev;
void __iomem *regs;
- struct irq_domain *irq_domain;
struct tegra_gpio_bank *bank_info;
const struct tegra_gpio_soc_config *soc;
struct gpio_chip gc;
struct irq_chip ic;
u32 bank_count;
+ unsigned int *irqs;
};
static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi,
u32 val, u32 reg)
{
- __raw_writel(val, tgi->regs + reg);
+ writel_relaxed(val, tgi->regs + reg);
}
static inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg)
{
- return __raw_readl(tgi->regs + reg);
+ return readl_relaxed(tgi->regs + reg);
}
static unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port,
@@ -215,7 +221,10 @@ static int tegra_gpio_get_direction(struct gpio_chip *chip,
oe = tegra_gpio_readl(tgi, GPIO_OE(tgi, offset));
- return !(oe & pin_mask);
+ if (oe & pin_mask)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int tegra_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
@@ -263,17 +272,10 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
return tegra_gpio_set_debounce(chip, offset, debounce);
}
-static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
-{
- struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
-
- return irq_find_mapping(tgi->irq_domain, offset);
-}
-
static void tegra_gpio_irq_ack(struct irq_data *d)
{
- struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
- struct tegra_gpio_info *tgi = bank->tgi;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
tegra_gpio_writel(tgi, 1 << GPIO_BIT(gpio), GPIO_INT_CLR(tgi, gpio));
@@ -281,8 +283,8 @@ static void tegra_gpio_irq_ack(struct irq_data *d)
static void tegra_gpio_irq_mask(struct irq_data *d)
{
- struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
- struct tegra_gpio_info *tgi = bank->tgi;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0);
@@ -290,8 +292,8 @@ static void tegra_gpio_irq_mask(struct irq_data *d)
static void tegra_gpio_irq_unmask(struct irq_data *d)
{
- struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
- struct tegra_gpio_info *tgi = bank->tgi;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1);
@@ -300,11 +302,14 @@ static void tegra_gpio_irq_unmask(struct irq_data *d)
static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
unsigned int gpio = d->hwirq, port = GPIO_PORT(gpio), lvl_type;
- struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
- struct tegra_gpio_info *tgi = bank->tgi;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+ struct tegra_gpio_bank *bank;
unsigned long flags;
- u32 val;
int ret;
+ u32 val;
+
+ bank = &tgi->bank_info[GPIO_BANK(d->hwirq)];
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_RISING:
@@ -331,14 +336,14 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- spin_lock_irqsave(&bank->lvl_lock[port], flags);
+ raw_spin_lock_irqsave(&bank->lvl_lock[port], flags);
val = tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio));
val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio));
val |= lvl_type << GPIO_BIT(gpio);
tegra_gpio_writel(tgi, val, GPIO_INT_LVL(tgi, gpio));
- spin_unlock_irqrestore(&bank->lvl_lock[port], flags);
+ raw_spin_unlock_irqrestore(&bank->lvl_lock[port], flags);
tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, gpio), gpio, 0);
tegra_gpio_enable(tgi, gpio);
@@ -356,27 +361,43 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
irq_set_handler_locked(d, handle_edge_irq);
- return 0;
+ if (d->parent_data)
+ ret = irq_chip_set_type_parent(d, type);
+
+ return ret;
}
static void tegra_gpio_irq_shutdown(struct irq_data *d)
{
- struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
- struct tegra_gpio_info *tgi = bank->tgi;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
+ tegra_gpio_irq_mask(d);
gpiochip_unlock_as_irq(&tgi->gc, gpio);
}
static void tegra_gpio_irq_handler(struct irq_desc *desc)
{
- unsigned int port, pin, gpio;
+ struct tegra_gpio_info *tgi = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_domain *domain = tgi->gc.irq.domain;
+ unsigned int irq = irq_desc_get_irq(desc);
+ struct tegra_gpio_bank *bank = NULL;
+ unsigned int port, pin, gpio, i;
bool unmasked = false;
- u32 lvl;
unsigned long sta;
- struct irq_chip *chip = irq_desc_get_chip(desc);
- struct tegra_gpio_bank *bank = irq_desc_get_handler_data(desc);
- struct tegra_gpio_info *tgi = bank->tgi;
+ u32 lvl;
+
+ for (i = 0; i < tgi->bank_count; i++) {
+ if (tgi->irqs[i] == irq) {
+ bank = &tgi->bank_info[i];
+ break;
+ }
+ }
+
+ if (WARN_ON(bank == NULL))
+ return;
chained_irq_enter(chip, desc);
@@ -387,6 +408,8 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc)
lvl = tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio));
for_each_set_bit(pin, &sta, 8) {
+ int ret;
+
tegra_gpio_writel(tgi, 1 << pin,
GPIO_INT_CLR(tgi, gpio));
@@ -399,25 +422,52 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
- generic_handle_irq(irq_find_mapping(tgi->irq_domain,
- gpio + pin));
+ ret = generic_handle_domain_irq(domain, gpio + pin);
+ WARN_RATELIMIT(ret, "hwirq = %d", gpio + pin);
}
}
if (!unmasked)
chained_irq_exit(chip, desc);
+}
+
+static int tegra_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
+ unsigned int hwirq,
+ unsigned int type,
+ unsigned int *parent_hwirq,
+ unsigned int *parent_type)
+{
+ *parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq);
+ *parent_type = type;
+
+ return 0;
+}
+static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip,
+ unsigned int parent_hwirq,
+ unsigned int parent_type)
+{
+ struct irq_fwspec *fwspec;
+
+ fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
+ if (!fwspec)
+ return NULL;
+
+ fwspec->fwnode = chip->irq.parent_domain->fwnode;
+ fwspec->param_count = 3;
+ fwspec->param[0] = 0;
+ fwspec->param[1] = parent_hwirq;
+ fwspec->param[2] = parent_type;
+
+ return fwspec;
}
#ifdef CONFIG_PM_SLEEP
static int tegra_gpio_resume(struct device *dev)
{
struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
- unsigned long flags;
unsigned int b, p;
- local_irq_save(flags);
-
for (b = 0; b < tgi->bank_count; b++) {
struct tegra_gpio_bank *bank = &tgi->bank_info[b];
@@ -445,17 +495,14 @@ static int tegra_gpio_resume(struct device *dev)
}
}
- local_irq_restore(flags);
return 0;
}
static int tegra_gpio_suspend(struct device *dev)
{
struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
- unsigned long flags;
unsigned int b, p;
- local_irq_save(flags);
for (b = 0; b < tgi->bank_count; b++) {
struct tegra_gpio_bank *bank = &tgi->bank_info[b];
@@ -485,29 +532,75 @@ static int tegra_gpio_suspend(struct device *dev)
GPIO_INT_ENB(tgi, gpio));
}
}
- local_irq_restore(flags);
+
return 0;
}
static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
{
- struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+ struct tegra_gpio_bank *bank;
unsigned int gpio = d->hwirq;
u32 port, bit, mask;
+ int err;
+
+ bank = &tgi->bank_info[GPIO_BANK(d->hwirq)];
port = GPIO_PORT(gpio);
bit = GPIO_BIT(gpio);
mask = BIT(bit);
+ err = irq_set_irq_wake(tgi->irqs[bank->bank], enable);
+ if (err)
+ return err;
+
+ if (d->parent_data) {
+ err = irq_chip_set_wake_parent(d, enable);
+ if (err) {
+ irq_set_irq_wake(tgi->irqs[bank->bank], !enable);
+ return err;
+ }
+ }
+
if (enable)
bank->wake_enb[port] |= mask;
else
bank->wake_enb[port] &= ~mask;
- return irq_set_irq_wake(bank->irq, enable);
+ return 0;
}
#endif
+static int tegra_gpio_irq_set_affinity(struct irq_data *data,
+ const struct cpumask *dest,
+ bool force)
+{
+ if (data->parent_data)
+ return irq_chip_set_affinity_parent(data, dest, force);
+
+ return -EINVAL;
+}
+
+static int tegra_gpio_irq_request_resources(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+
+ tegra_gpio_enable(tgi, d->hwirq);
+
+ return gpiochip_reqres_irq(chip, d->hwirq);
+}
+
+static void tegra_gpio_irq_release_resources(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+
+ gpiochip_relres_irq(chip, d->hwirq);
+ tegra_gpio_enable(tgi, d->hwirq);
+}
+
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
@@ -515,7 +608,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
{
- struct tegra_gpio_info *tgi = s->private;
+ struct tegra_gpio_info *tgi = dev_get_drvdata(s->private);
unsigned int i, j;
for (i = 0; i < tgi->bank_count; i++) {
@@ -537,12 +630,10 @@ static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
return 0;
}
-DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio);
-
static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
{
- debugfs_create_file("tegra_gpio", 0444, NULL, tgi,
- &tegra_dbg_gpio_fops);
+ debugfs_create_devm_seqfile(tgi->dev, "tegra_gpio", NULL,
+ tegra_dbg_gpio_show);
}
#else
@@ -554,14 +645,21 @@ static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
#endif
static const struct dev_pm_ops tegra_gpio_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
+};
+
+static const struct of_device_id tegra_pmc_of_match[] = {
+ { .compatible = "nvidia,tegra210-pmc", },
+ { /* sentinel */ },
};
static int tegra_gpio_probe(struct platform_device *pdev)
{
- struct tegra_gpio_info *tgi;
struct tegra_gpio_bank *bank;
- unsigned int gpio, i, j;
+ struct tegra_gpio_info *tgi;
+ struct gpio_irq_chip *irq;
+ struct device_node *np;
+ unsigned int i, j;
int ret;
tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL);
@@ -590,11 +688,9 @@ static int tegra_gpio_probe(struct platform_device *pdev)
tgi->gc.direction_output = tegra_gpio_direction_output;
tgi->gc.set = tegra_gpio_set;
tgi->gc.get_direction = tegra_gpio_get_direction;
- tgi->gc.to_irq = tegra_gpio_to_irq;
tgi->gc.base = 0;
tgi->gc.ngpio = tgi->bank_count * 32;
tgi->gc.parent = &pdev->dev;
- tgi->gc.of_node = pdev->dev.of_node;
tgi->ic.name = "GPIO";
tgi->ic.irq_ack = tegra_gpio_irq_ack;
@@ -605,6 +701,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake;
#endif
+ tgi->ic.irq_request_resources = tegra_gpio_irq_request_resources;
+ tgi->ic.irq_release_resources = tegra_gpio_irq_release_resources;
platform_set_drvdata(pdev, tgi);
@@ -616,23 +714,48 @@ static int tegra_gpio_probe(struct platform_device *pdev)
if (!tgi->bank_info)
return -ENOMEM;
- tgi->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
- tgi->gc.ngpio,
- &irq_domain_simple_ops, NULL);
- if (!tgi->irq_domain)
- return -ENODEV;
+ tgi->irqs = devm_kcalloc(&pdev->dev, tgi->bank_count,
+ sizeof(*tgi->irqs), GFP_KERNEL);
+ if (!tgi->irqs)
+ return -ENOMEM;
for (i = 0; i < tgi->bank_count; i++) {
ret = platform_get_irq(pdev, i);
- if (ret < 0) {
- dev_err(&pdev->dev, "Missing IRQ resource: %d\n", ret);
+ if (ret < 0)
return ret;
- }
bank = &tgi->bank_info[i];
bank->bank = i;
- bank->irq = ret;
- bank->tgi = tgi;
+
+ tgi->irqs[i] = ret;
+
+ for (j = 0; j < 4; j++) {
+ raw_spin_lock_init(&bank->lvl_lock[j]);
+ spin_lock_init(&bank->dbc_lock[j]);
+ }
+ }
+
+ irq = &tgi->gc.irq;
+ irq->chip = &tgi->ic;
+ irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
+ irq->child_to_parent_hwirq = tegra_gpio_child_to_parent_hwirq;
+ irq->populate_parent_alloc_arg = tegra_gpio_populate_parent_fwspec;
+ irq->handler = handle_simple_irq;
+ irq->default_type = IRQ_TYPE_NONE;
+ irq->parent_handler = tegra_gpio_irq_handler;
+ irq->parent_handler_data = tgi;
+ irq->num_parents = tgi->bank_count;
+ irq->parents = tgi->irqs;
+
+ np = of_find_matching_node(NULL, tegra_pmc_of_match);
+ if (np) {
+ irq->parent_domain = irq_find_host(np);
+ of_node_put(np);
+
+ if (!irq->parent_domain)
+ return -EPROBE_DEFER;
+
+ tgi->ic.irq_set_affinity = tegra_gpio_irq_set_affinity;
}
tgi->regs = devm_platform_ioremap_resource(pdev, 0);
@@ -648,32 +771,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
}
ret = devm_gpiochip_add_data(&pdev->dev, &tgi->gc, tgi);
- if (ret < 0) {
- irq_domain_remove(tgi->irq_domain);
+ if (ret < 0)
return ret;
- }
-
- for (gpio = 0; gpio < tgi->gc.ngpio; gpio++) {
- int irq = irq_create_mapping(tgi->irq_domain, gpio);
- /* No validity check; all Tegra GPIOs are valid IRQs */
-
- bank = &tgi->bank_info[GPIO_BANK(gpio)];
-
- irq_set_chip_data(irq, bank);
- irq_set_chip_and_handler(irq, &tgi->ic, handle_simple_irq);
- }
-
- for (i = 0; i < tgi->bank_count; i++) {
- bank = &tgi->bank_info[i];
-
- irq_set_chained_handler_and_data(bank->irq,
- tegra_gpio_irq_handler, bank);
-
- for (j = 0; j < 4; j++) {
- spin_lock_init(&bank->lvl_lock[j]);
- spin_lock_init(&bank->dbc_lock[j]);
- }
- }
tegra_gpio_debuginit(tgi);
@@ -702,18 +801,21 @@ static const struct of_device_id tegra_gpio_of_match[] = {
{ .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config },
{ },
};
+MODULE_DEVICE_TABLE(of, tegra_gpio_of_match);
static struct platform_driver tegra_gpio_driver = {
- .driver = {
- .name = "tegra-gpio",
- .pm = &tegra_gpio_pm_ops,
+ .driver = {
+ .name = "tegra-gpio",
+ .pm = &tegra_gpio_pm_ops,
.of_match_table = tegra_gpio_of_match,
},
- .probe = tegra_gpio_probe,
+ .probe = tegra_gpio_probe,
};
-
-static int __init tegra_gpio_init(void)
-{
- return platform_driver_register(&tegra_gpio_driver);
-}
-subsys_initcall(tegra_gpio_init);
+module_platform_driver(tegra_gpio_driver);
+
+MODULE_DESCRIPTION("NVIDIA Tegra GPIO controller driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_AUTHOR("Erik Gilling <konkers@google.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index a9058fda187e..de28a68daea0 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2017 NVIDIA Corporation
+ * Copyright (c) 2016-2022 NVIDIA Corporation
*
* Author: Thierry Reding <treding@nvidia.com>
+ * Dipen Patel <dpatel@nvidia.com>
*/
#include <linux/gpio/driver.h>
@@ -11,10 +12,21 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/hte.h>
#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/tegra241-gpio.h>
+/* security registers */
+#define TEGRA186_GPIO_CTL_SCR 0x0c
+#define TEGRA186_GPIO_CTL_SCR_SEC_WEN BIT(28)
+#define TEGRA186_GPIO_CTL_SCR_SEC_REN BIT(27)
+
+#define TEGRA186_GPIO_INT_ROUTE_MAPPING(p, x) (0x14 + (p) * 0x20 + (x) * 4)
+
+/* control registers */
#define TEGRA186_GPIO_ENABLE_CONFIG 0x00
#define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0)
#define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1)
@@ -24,7 +36,9 @@
#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE (0x3 << 2)
#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK (0x3 << 2)
#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL BIT(4)
+#define TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE BIT(5)
#define TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT BIT(6)
+#define TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC BIT(7)
#define TEGRA186_GPIO_DEBOUNCE_CONTROL 0x04
#define TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(x) ((x) & 0xff)
@@ -44,25 +58,40 @@
struct tegra_gpio_port {
const char *name;
- unsigned int offset;
+ unsigned int bank;
+ unsigned int port;
unsigned int pins;
- unsigned int irq;
+};
+
+struct tegra186_pin_range {
+ unsigned int offset;
+ const char *group;
};
struct tegra_gpio_soc {
const struct tegra_gpio_port *ports;
unsigned int num_ports;
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;
+ bool has_gte;
};
struct tegra_gpio {
struct gpio_chip gpio;
- struct irq_chip intc;
unsigned int num_irq;
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;
};
@@ -89,12 +118,15 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio,
unsigned int pin)
{
const struct tegra_gpio_port *port;
+ unsigned int offset;
port = tegra186_gpio_get_port(gpio, &pin);
if (!port)
return NULL;
- return gpio->base + port->offset + pin * 0x20;
+ offset = port->bank * 0x1000 + port->port * 0x200;
+
+ return gpio->base + offset + pin * 0x20;
}
static int tegra186_gpio_get_direction(struct gpio_chip *chip,
@@ -110,9 +142,9 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip,
value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG);
if (value & TEGRA186_GPIO_ENABLE_CONFIG_OUT)
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
}
static int tegra186_gpio_direction_input(struct gpio_chip *chip,
@@ -165,6 +197,76 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip,
return 0;
}
+#define HTE_BOTH_EDGES (HTE_RISING_EDGE_TS | HTE_FALLING_EDGE_TS)
+
+static int tegra186_gpio_en_hw_ts(struct gpio_chip *gc, u32 offset,
+ unsigned long flags)
+{
+ struct tegra_gpio *gpio;
+ void __iomem *base;
+ int value;
+
+ if (!gc)
+ return -EINVAL;
+
+ gpio = gpiochip_get_data(gc);
+ if (!gpio)
+ return -ENODEV;
+
+ base = tegra186_gpio_get_base(gpio, offset);
+ if (WARN_ON(base == NULL))
+ return -EINVAL;
+
+ value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG);
+ value |= TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC;
+
+ if (flags == HTE_BOTH_EDGES) {
+ value |= TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE;
+ } else if (flags == HTE_RISING_EDGE_TS) {
+ value |= TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE;
+ value |= TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL;
+ } else if (flags == HTE_FALLING_EDGE_TS) {
+ value |= TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE;
+ }
+
+ writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
+
+ return 0;
+}
+
+static int tegra186_gpio_dis_hw_ts(struct gpio_chip *gc, u32 offset,
+ unsigned long flags)
+{
+ struct tegra_gpio *gpio;
+ void __iomem *base;
+ int value;
+
+ if (!gc)
+ return -EINVAL;
+
+ gpio = gpiochip_get_data(gc);
+ if (!gpio)
+ return -ENODEV;
+
+ base = tegra186_gpio_get_base(gpio, offset);
+ if (WARN_ON(base == NULL))
+ return -EINVAL;
+
+ value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG);
+ value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC;
+ if (flags == HTE_BOTH_EDGES) {
+ value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE;
+ } else if (flags == HTE_RISING_EDGE_TS) {
+ value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE;
+ value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL;
+ } else if (flags == HTE_FALLING_EDGE_TS) {
+ value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE;
+ }
+ writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
+
+ return 0;
+}
+
static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct tegra_gpio *gpio = gpiochip_get_data(chip);
@@ -204,6 +306,86 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset,
writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE);
}
+static int tegra186_gpio_set_config(struct gpio_chip *chip,
+ unsigned int offset,
+ unsigned long config)
+{
+ struct tegra_gpio *gpio = gpiochip_get_data(chip);
+ u32 debounce, value;
+ void __iomem *base;
+
+ base = tegra186_gpio_get_base(gpio, offset);
+ if (base == NULL)
+ return -ENXIO;
+
+ if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
+ return -ENOTSUPP;
+
+ debounce = pinconf_to_config_argument(config);
+
+ /*
+ * The Tegra186 GPIO controller supports a maximum of 255 ms debounce
+ * time.
+ */
+ if (debounce > 255000)
+ return -EINVAL;
+
+ debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC);
+
+ value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce);
+ writel(value, base + TEGRA186_GPIO_DEBOUNCE_CONTROL);
+
+ value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG);
+ value |= TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE;
+ writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
+
+ return 0;
+}
+
+static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip)
+{
+ struct tegra_gpio *gpio = gpiochip_get_data(chip);
+ struct pinctrl_dev *pctldev;
+ struct device_node *np;
+ unsigned int i, j;
+ int err;
+
+ if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0)
+ return 0;
+
+ np = of_find_compatible_node(NULL, NULL, gpio->soc->pinmux);
+ if (!np)
+ return -ENODEV;
+
+ pctldev = of_pinctrl_get(np);
+ of_node_put(np);
+ if (!pctldev)
+ return -EPROBE_DEFER;
+
+ for (i = 0; i < gpio->soc->num_pin_ranges; i++) {
+ unsigned int pin = gpio->soc->pin_ranges[i].offset, port;
+ const char *group = gpio->soc->pin_ranges[i].group;
+
+ port = pin / 8;
+ pin = pin % 8;
+
+ if (port >= gpio->soc->num_ports) {
+ dev_warn(chip->parent, "invalid port %u for %s\n",
+ port, group);
+ continue;
+ }
+
+ for (j = 0; j < port; j++)
+ pin += gpio->soc->ports[j].pins;
+
+ err = gpiochip_add_pingroup_range(chip, pctldev, pin, group);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
static int tegra186_gpio_of_xlate(struct gpio_chip *chip,
const struct of_phandle_args *spec,
u32 *flags)
@@ -234,9 +416,12 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip,
return offset + pin;
}
+#define to_tegra_gpio(x) container_of((x), struct tegra_gpio, gpio)
+
static void tegra186_irq_ack(struct irq_data *data)
{
- struct tegra_gpio *gpio = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ struct tegra_gpio *gpio = to_tegra_gpio(gc);
void __iomem *base;
base = tegra186_gpio_get_base(gpio, data->hwirq);
@@ -248,7 +433,8 @@ static void tegra186_irq_ack(struct irq_data *data)
static void tegra186_irq_mask(struct irq_data *data)
{
- struct tegra_gpio *gpio = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ struct tegra_gpio *gpio = to_tegra_gpio(gc);
void __iomem *base;
u32 value;
@@ -259,11 +445,14 @@ static void tegra186_irq_mask(struct irq_data *data)
value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG);
value &= ~TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT;
writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
+
+ gpiochip_disable_irq(&gpio->gpio, data->hwirq);
}
static void tegra186_irq_unmask(struct irq_data *data)
{
- struct tegra_gpio *gpio = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ struct tegra_gpio *gpio = to_tegra_gpio(gc);
void __iomem *base;
u32 value;
@@ -271,6 +460,8 @@ static void tegra186_irq_unmask(struct irq_data *data)
if (WARN_ON(base == NULL))
return;
+ gpiochip_enable_irq(&gpio->gpio, data->hwirq);
+
value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG);
value |= TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT;
writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
@@ -278,7 +469,8 @@ static void tegra186_irq_unmask(struct irq_data *data)
static int tegra186_irq_set_type(struct irq_data *data, unsigned int type)
{
- struct tegra_gpio *gpio = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ struct tegra_gpio *gpio = to_tegra_gpio(gc);
void __iomem *base;
u32 value;
@@ -327,37 +519,70 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type)
else
irq_set_handler_locked(data, handle_edge_irq);
+ if (data->parent_data)
+ return irq_chip_set_type_parent(data, type);
+
return 0;
}
+static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ if (data->parent_data)
+ return irq_chip_set_wake_parent(data, on);
+
+ return 0;
+}
+
+static void tegra186_irq_print_chip(struct irq_data *data, struct seq_file *p)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+
+ seq_printf(p, dev_name(gc->parent));
+}
+
+static const struct irq_chip tegra186_gpio_irq_chip = {
+ .irq_ack = tegra186_irq_ack,
+ .irq_mask = tegra186_irq_mask,
+ .irq_unmask = tegra186_irq_unmask,
+ .irq_set_type = tegra186_irq_set_type,
+ .irq_set_wake = tegra186_irq_set_wake,
+ .irq_print_chip = tegra186_irq_print_chip,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
static void tegra186_gpio_irq(struct irq_desc *desc)
{
struct tegra_gpio *gpio = irq_desc_get_handler_data(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);
for (i = 0; i < gpio->soc->num_ports; i++) {
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
- void __iomem *base = gpio->base + port->offset;
- unsigned int pin, irq;
+ unsigned int pin;
unsigned long value;
+ void __iomem *base;
+
+ base = gpio->base + port->bank * 0x1000 + port->port * 0x200;
+
+ /* skip ports that are not associated with this bank */
+ for (j = 0; j < gpio->num_irqs_per_bank; j++) {
+ if (parent == gpio->irq[port->bank * gpio->num_irqs_per_bank + j])
+ break;
+ }
- /* skip ports that are not associated with this controller */
- if (parent != gpio->irq[port->irq])
+ if (j == gpio->num_irqs_per_bank)
goto skip;
value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1));
for_each_set_bit(pin, &value, port->pins) {
- irq = irq_find_mapping(domain, offset + pin);
- if (WARN_ON(irq == 0))
- continue;
-
- generic_handle_irq(irq);
+ int ret = generic_handle_domain_irq(domain, offset + pin);
+ WARN_RATELIMIT(ret, "hwirq = %d", offset + pin);
}
skip:
@@ -367,47 +592,170 @@ skip:
chained_irq_exit(chip, desc);
}
-static int tegra186_gpio_irq_domain_xlate(struct irq_domain *domain,
- struct device_node *np,
- const u32 *spec, unsigned int size,
- unsigned long *hwirq,
- unsigned int *type)
+static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
{
struct tegra_gpio *gpio = gpiochip_get_data(domain->host_data);
unsigned int port, pin, i, offset = 0;
- if (size < 2)
+ if (WARN_ON(gpio->gpio.of_gpio_n_cells < 2))
return -EINVAL;
- port = spec[0] / 8;
- pin = spec[0] % 8;
+ if (WARN_ON(fwspec->param_count < gpio->gpio.of_gpio_n_cells))
+ return -EINVAL;
- if (port >= gpio->soc->num_ports) {
- dev_err(gpio->gpio.parent, "invalid port number: %u\n", port);
+ port = fwspec->param[0] / 8;
+ pin = fwspec->param[0] % 8;
+
+ if (port >= gpio->soc->num_ports)
return -EINVAL;
- }
for (i = 0; i < port; i++)
offset += gpio->soc->ports[i].pins;
- *type = spec[1] & IRQ_TYPE_SENSE_MASK;
+ *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
*hwirq = offset + pin;
return 0;
}
-static const struct irq_domain_ops tegra186_gpio_irq_domain_ops = {
- .map = gpiochip_irq_map,
- .unmap = gpiochip_irq_unmap,
- .xlate = tegra186_gpio_irq_domain_xlate,
+static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
+ unsigned int parent_hwirq,
+ unsigned int parent_type)
+{
+ struct tegra_gpio *gpio = gpiochip_get_data(chip);
+ struct irq_fwspec *fwspec;
+
+ fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
+ if (!fwspec)
+ return NULL;
+
+ fwspec->fwnode = chip->irq.parent_domain->fwnode;
+ fwspec->param_count = 3;
+ fwspec->param[0] = gpio->soc->instance;
+ fwspec->param[1] = parent_hwirq;
+ fwspec->param[2] = parent_type;
+
+ return fwspec;
+}
+
+static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
+ unsigned int hwirq,
+ unsigned int type,
+ unsigned int *parent_hwirq,
+ unsigned int *parent_type)
+{
+ *parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq);
+ *parent_type = type;
+
+ return 0;
+}
+
+static unsigned int tegra186_gpio_child_offset_to_irq(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct tegra_gpio *gpio = gpiochip_get_data(chip);
+ unsigned int i;
+
+ for (i = 0; i < gpio->soc->num_ports; i++) {
+ if (offset < gpio->soc->ports[i].pins)
+ break;
+
+ offset -= gpio->soc->ports[i].pins;
+ }
+
+ return offset + i * 8;
+}
+
+static const struct of_device_id tegra186_pmc_of_match[] = {
+ { .compatible = "nvidia,tegra186-pmc" },
+ { .compatible = "nvidia,tegra194-pmc" },
+ { /* sentinel */ }
};
+static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio)
+{
+ struct device *dev = gpio->gpio.parent;
+ unsigned int i, j;
+ u32 value;
+
+ for (i = 0; i < gpio->soc->num_ports; i++) {
+ const struct tegra_gpio_port *port = &gpio->soc->ports[i];
+ unsigned int offset, p = port->port;
+ void __iomem *base;
+
+ base = gpio->secure + port->bank * 0x1000 + 0x800;
+
+ value = readl(base + TEGRA186_GPIO_CTL_SCR);
+
+ /*
+ * For controllers that haven't been locked down yet, make
+ * sure to program the default interrupt route mapping.
+ */
+ if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 &&
+ (value & TEGRA186_GPIO_CTL_SCR_SEC_WEN) == 0) {
+ /*
+ * 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);
+
+ /*
+ * 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;
struct gpio_irq_chip *irq;
struct tegra_gpio *gpio;
- struct resource *res;
+ struct device_node *np;
char **names;
int err;
@@ -415,12 +763,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
if (!gpio)
return -ENOMEM;
- gpio->soc = of_device_get_match_data(&pdev->dev);
+ gpio->soc = device_get_match_data(&pdev->dev);
+ gpio->gpio.label = gpio->soc->name;
+ gpio->gpio.parent = &pdev->dev;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpio");
- gpio->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(gpio->base))
- return PTR_ERR(gpio->base);
+ /* 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);
+ if (IS_ERR(gpio->secure))
+ return PTR_ERR(gpio->secure);
+ }
+
+ gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio");
+ if (IS_ERR(gpio->base)) {
+ gpio->base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(gpio->base))
+ return PTR_ERR(gpio->base);
+ }
err = platform_irq_count(pdev);
if (err < 0)
@@ -428,6 +795,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)
@@ -441,14 +812,19 @@ 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;
gpio->gpio.direction_input = tegra186_gpio_direction_input;
gpio->gpio.direction_output = tegra186_gpio_direction_output;
- gpio->gpio.get = tegra186_gpio_get,
+ gpio->gpio.get = tegra186_gpio_get;
gpio->gpio.set = tegra186_gpio_set;
+ gpio->gpio.set_config = tegra186_gpio_set_config;
+ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges;
+ if (gpio->soc->has_gte) {
+ gpio->gpio.en_hw_timestamp = tegra186_gpio_en_hw_ts;
+ gpio->gpio.dis_hw_timestamp = tegra186_gpio_dis_hw_ts;
+ }
gpio->gpio.base = -1;
@@ -478,25 +854,57 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
gpio->gpio.names = (const char * const *)names;
- gpio->gpio.of_node = pdev->dev.of_node;
+#if defined(CONFIG_OF_GPIO)
gpio->gpio.of_gpio_n_cells = 2;
gpio->gpio.of_xlate = tegra186_gpio_of_xlate;
-
- gpio->intc.name = pdev->dev.of_node->name;
- gpio->intc.irq_ack = tegra186_irq_ack;
- gpio->intc.irq_mask = tegra186_irq_mask;
- gpio->intc.irq_unmask = tegra186_irq_unmask;
- gpio->intc.irq_set_type = tegra186_irq_set_type;
+#endif /* CONFIG_OF_GPIO */
irq = &gpio->gpio.irq;
- irq->chip = &gpio->intc;
- irq->domain_ops = &tegra186_gpio_irq_domain_ops;
+ gpio_irq_chip_set_chip(irq, &tegra186_gpio_irq_chip);
+ irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
+ irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq;
+ irq->populate_parent_alloc_arg = tegra186_gpio_populate_parent_fwspec;
+ irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq;
+ irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate;
irq->handler = handle_simple_irq;
irq->default_type = IRQ_TYPE_NONE;
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) {
+ irq->parent_domain = irq_find_host(np);
+ of_node_put(np);
+
+ if (!irq->parent_domain)
+ return -EPROBE_DEFER;
+ }
irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio,
sizeof(*irq->map), GFP_KERNEL);
@@ -507,155 +915,281 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
for (j = 0; j < port->pins; j++)
- irq->map[offset + j] = irq->parents[port->irq];
+ irq->map[offset + j] = irq->parents[port->bank];
offset += port->pins;
}
- platform_set_drvdata(pdev, gpio);
-
- err = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio);
- if (err < 0)
- return err;
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio);
}
-static int tegra186_gpio_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-#define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \
- [TEGRA186_MAIN_GPIO_PORT_##port] = { \
- .name = #port, \
- .offset = base, \
- .pins = count, \
- .irq = controller, \
+#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ [TEGRA186_MAIN_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
}
static const struct tegra_gpio_port tegra186_main_ports[] = {
- TEGRA186_MAIN_GPIO_PORT( A, 0x2000, 7, 2),
- TEGRA186_MAIN_GPIO_PORT( B, 0x3000, 7, 3),
- TEGRA186_MAIN_GPIO_PORT( C, 0x3200, 7, 3),
- TEGRA186_MAIN_GPIO_PORT( D, 0x3400, 6, 3),
- TEGRA186_MAIN_GPIO_PORT( E, 0x2200, 8, 2),
- TEGRA186_MAIN_GPIO_PORT( F, 0x2400, 6, 2),
- TEGRA186_MAIN_GPIO_PORT( G, 0x4200, 6, 4),
- TEGRA186_MAIN_GPIO_PORT( H, 0x1000, 7, 1),
- TEGRA186_MAIN_GPIO_PORT( I, 0x0800, 8, 0),
- TEGRA186_MAIN_GPIO_PORT( J, 0x5000, 8, 5),
- TEGRA186_MAIN_GPIO_PORT( K, 0x5200, 1, 5),
- TEGRA186_MAIN_GPIO_PORT( L, 0x1200, 8, 1),
- TEGRA186_MAIN_GPIO_PORT( M, 0x5600, 6, 5),
- TEGRA186_MAIN_GPIO_PORT( N, 0x0000, 7, 0),
- TEGRA186_MAIN_GPIO_PORT( O, 0x0200, 4, 0),
- TEGRA186_MAIN_GPIO_PORT( P, 0x4000, 7, 4),
- TEGRA186_MAIN_GPIO_PORT( Q, 0x0400, 6, 0),
- TEGRA186_MAIN_GPIO_PORT( R, 0x0a00, 6, 0),
- TEGRA186_MAIN_GPIO_PORT( T, 0x0600, 4, 0),
- TEGRA186_MAIN_GPIO_PORT( X, 0x1400, 8, 1),
- TEGRA186_MAIN_GPIO_PORT( Y, 0x1600, 7, 1),
- TEGRA186_MAIN_GPIO_PORT(BB, 0x2600, 2, 2),
- TEGRA186_MAIN_GPIO_PORT(CC, 0x5400, 4, 5),
+ TEGRA186_MAIN_GPIO_PORT( A, 2, 0, 7),
+ TEGRA186_MAIN_GPIO_PORT( B, 3, 0, 7),
+ TEGRA186_MAIN_GPIO_PORT( C, 3, 1, 7),
+ TEGRA186_MAIN_GPIO_PORT( D, 3, 2, 6),
+ TEGRA186_MAIN_GPIO_PORT( E, 2, 1, 8),
+ TEGRA186_MAIN_GPIO_PORT( F, 2, 2, 6),
+ TEGRA186_MAIN_GPIO_PORT( G, 4, 1, 6),
+ TEGRA186_MAIN_GPIO_PORT( H, 1, 0, 7),
+ TEGRA186_MAIN_GPIO_PORT( I, 0, 4, 8),
+ TEGRA186_MAIN_GPIO_PORT( J, 5, 0, 8),
+ TEGRA186_MAIN_GPIO_PORT( K, 5, 1, 1),
+ TEGRA186_MAIN_GPIO_PORT( L, 1, 1, 8),
+ TEGRA186_MAIN_GPIO_PORT( M, 5, 3, 6),
+ TEGRA186_MAIN_GPIO_PORT( N, 0, 0, 7),
+ TEGRA186_MAIN_GPIO_PORT( O, 0, 1, 4),
+ TEGRA186_MAIN_GPIO_PORT( P, 4, 0, 7),
+ TEGRA186_MAIN_GPIO_PORT( Q, 0, 2, 6),
+ TEGRA186_MAIN_GPIO_PORT( R, 0, 5, 6),
+ TEGRA186_MAIN_GPIO_PORT( T, 0, 3, 4),
+ TEGRA186_MAIN_GPIO_PORT( X, 1, 2, 8),
+ TEGRA186_MAIN_GPIO_PORT( Y, 1, 3, 7),
+ TEGRA186_MAIN_GPIO_PORT(BB, 2, 3, 2),
+ TEGRA186_MAIN_GPIO_PORT(CC, 5, 2, 4),
};
static const struct tegra_gpio_soc tegra186_main_soc = {
.num_ports = ARRAY_SIZE(tegra186_main_ports),
.ports = tegra186_main_ports,
.name = "tegra186-gpio",
+ .instance = 0,
+ .num_irqs_per_bank = 1,
};
-#define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \
- [TEGRA186_AON_GPIO_PORT_##port] = { \
- .name = #port, \
- .offset = base, \
- .pins = count, \
- .irq = controller, \
+#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ [TEGRA186_AON_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
}
static const struct tegra_gpio_port tegra186_aon_ports[] = {
- TEGRA186_AON_GPIO_PORT( S, 0x0200, 5, 0),
- TEGRA186_AON_GPIO_PORT( U, 0x0400, 6, 0),
- TEGRA186_AON_GPIO_PORT( V, 0x0800, 8, 0),
- TEGRA186_AON_GPIO_PORT( W, 0x0a00, 8, 0),
- TEGRA186_AON_GPIO_PORT( Z, 0x0e00, 4, 0),
- TEGRA186_AON_GPIO_PORT(AA, 0x0c00, 8, 0),
- TEGRA186_AON_GPIO_PORT(EE, 0x0600, 3, 0),
- TEGRA186_AON_GPIO_PORT(FF, 0x0000, 5, 0),
+ TEGRA186_AON_GPIO_PORT( S, 0, 1, 5),
+ TEGRA186_AON_GPIO_PORT( U, 0, 2, 6),
+ TEGRA186_AON_GPIO_PORT( V, 0, 4, 8),
+ TEGRA186_AON_GPIO_PORT( W, 0, 5, 8),
+ TEGRA186_AON_GPIO_PORT( Z, 0, 7, 4),
+ TEGRA186_AON_GPIO_PORT(AA, 0, 6, 8),
+ TEGRA186_AON_GPIO_PORT(EE, 0, 3, 3),
+ TEGRA186_AON_GPIO_PORT(FF, 0, 0, 5),
};
static const struct tegra_gpio_soc tegra186_aon_soc = {
.num_ports = ARRAY_SIZE(tegra186_aon_ports),
.ports = tegra186_aon_ports,
.name = "tegra186-gpio-aon",
+ .instance = 1,
+ .num_irqs_per_bank = 1,
};
-#define TEGRA194_MAIN_GPIO_PORT(port, base, count, controller) \
- [TEGRA194_MAIN_GPIO_PORT_##port] = { \
- .name = #port, \
- .offset = base, \
- .pins = count, \
- .irq = controller, \
+#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ [TEGRA194_MAIN_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
}
static const struct tegra_gpio_port tegra194_main_ports[] = {
- TEGRA194_MAIN_GPIO_PORT( A, 0x1400, 8, 1),
- TEGRA194_MAIN_GPIO_PORT( B, 0x4e00, 2, 4),
- TEGRA194_MAIN_GPIO_PORT( C, 0x4600, 8, 4),
- TEGRA194_MAIN_GPIO_PORT( D, 0x4800, 4, 4),
- TEGRA194_MAIN_GPIO_PORT( E, 0x4a00, 8, 4),
- TEGRA194_MAIN_GPIO_PORT( F, 0x4c00, 6, 4),
- TEGRA194_MAIN_GPIO_PORT( G, 0x4000, 8, 4),
- TEGRA194_MAIN_GPIO_PORT( H, 0x4200, 8, 4),
- TEGRA194_MAIN_GPIO_PORT( I, 0x4400, 5, 4),
- TEGRA194_MAIN_GPIO_PORT( J, 0x5200, 6, 5),
- TEGRA194_MAIN_GPIO_PORT( K, 0x3000, 8, 3),
- TEGRA194_MAIN_GPIO_PORT( L, 0x3200, 4, 3),
- TEGRA194_MAIN_GPIO_PORT( M, 0x2600, 8, 2),
- TEGRA194_MAIN_GPIO_PORT( N, 0x2800, 3, 2),
- TEGRA194_MAIN_GPIO_PORT( O, 0x5000, 6, 5),
- TEGRA194_MAIN_GPIO_PORT( P, 0x2a00, 8, 2),
- TEGRA194_MAIN_GPIO_PORT( Q, 0x2c00, 8, 2),
- TEGRA194_MAIN_GPIO_PORT( R, 0x2e00, 6, 2),
- TEGRA194_MAIN_GPIO_PORT( S, 0x3600, 8, 3),
- TEGRA194_MAIN_GPIO_PORT( T, 0x3800, 8, 3),
- TEGRA194_MAIN_GPIO_PORT( U, 0x3a00, 1, 3),
- TEGRA194_MAIN_GPIO_PORT( V, 0x1000, 8, 1),
- TEGRA194_MAIN_GPIO_PORT( W, 0x1200, 2, 1),
- TEGRA194_MAIN_GPIO_PORT( X, 0x2000, 8, 2),
- TEGRA194_MAIN_GPIO_PORT( Y, 0x2200, 8, 2),
- TEGRA194_MAIN_GPIO_PORT( Z, 0x2400, 8, 2),
- TEGRA194_MAIN_GPIO_PORT(FF, 0x3400, 2, 3),
- TEGRA194_MAIN_GPIO_PORT(GG, 0x0000, 2, 0)
+ TEGRA194_MAIN_GPIO_PORT( A, 1, 2, 8),
+ TEGRA194_MAIN_GPIO_PORT( B, 4, 7, 2),
+ TEGRA194_MAIN_GPIO_PORT( C, 4, 3, 8),
+ TEGRA194_MAIN_GPIO_PORT( D, 4, 4, 4),
+ TEGRA194_MAIN_GPIO_PORT( E, 4, 5, 8),
+ TEGRA194_MAIN_GPIO_PORT( F, 4, 6, 6),
+ TEGRA194_MAIN_GPIO_PORT( G, 4, 0, 8),
+ TEGRA194_MAIN_GPIO_PORT( H, 4, 1, 8),
+ TEGRA194_MAIN_GPIO_PORT( I, 4, 2, 5),
+ TEGRA194_MAIN_GPIO_PORT( J, 5, 1, 6),
+ TEGRA194_MAIN_GPIO_PORT( K, 3, 0, 8),
+ TEGRA194_MAIN_GPIO_PORT( L, 3, 1, 4),
+ TEGRA194_MAIN_GPIO_PORT( M, 2, 3, 8),
+ TEGRA194_MAIN_GPIO_PORT( N, 2, 4, 3),
+ TEGRA194_MAIN_GPIO_PORT( O, 5, 0, 6),
+ TEGRA194_MAIN_GPIO_PORT( P, 2, 5, 8),
+ TEGRA194_MAIN_GPIO_PORT( Q, 2, 6, 8),
+ TEGRA194_MAIN_GPIO_PORT( R, 2, 7, 6),
+ TEGRA194_MAIN_GPIO_PORT( S, 3, 3, 8),
+ TEGRA194_MAIN_GPIO_PORT( T, 3, 4, 8),
+ TEGRA194_MAIN_GPIO_PORT( U, 3, 5, 1),
+ TEGRA194_MAIN_GPIO_PORT( V, 1, 0, 8),
+ TEGRA194_MAIN_GPIO_PORT( W, 1, 1, 2),
+ TEGRA194_MAIN_GPIO_PORT( X, 2, 0, 8),
+ TEGRA194_MAIN_GPIO_PORT( Y, 2, 1, 8),
+ TEGRA194_MAIN_GPIO_PORT( Z, 2, 2, 8),
+ TEGRA194_MAIN_GPIO_PORT(FF, 3, 2, 2),
+ TEGRA194_MAIN_GPIO_PORT(GG, 0, 0, 2)
+};
+
+static const struct tegra186_pin_range tegra194_main_pin_ranges[] = {
+ { TEGRA194_MAIN_GPIO(GG, 0), "pex_l5_clkreq_n_pgg0" },
+ { TEGRA194_MAIN_GPIO(GG, 1), "pex_l5_rst_n_pgg1" },
};
static const struct tegra_gpio_soc tegra194_main_soc = {
.num_ports = ARRAY_SIZE(tegra194_main_ports),
.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",
};
-#define TEGRA194_AON_GPIO_PORT(port, base, count, controller) \
- [TEGRA194_AON_GPIO_PORT_##port] = { \
- .name = #port, \
- .offset = base, \
- .pins = count, \
- .irq = controller, \
+#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ [TEGRA194_AON_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
}
static const struct tegra_gpio_port tegra194_aon_ports[] = {
- TEGRA194_AON_GPIO_PORT(AA, 0x0600, 8, 0),
- TEGRA194_AON_GPIO_PORT(BB, 0x0800, 4, 0),
- TEGRA194_AON_GPIO_PORT(CC, 0x0200, 8, 0),
- TEGRA194_AON_GPIO_PORT(DD, 0x0400, 3, 0),
- TEGRA194_AON_GPIO_PORT(EE, 0x0000, 7, 0)
+ TEGRA194_AON_GPIO_PORT(AA, 0, 3, 8),
+ TEGRA194_AON_GPIO_PORT(BB, 0, 4, 4),
+ TEGRA194_AON_GPIO_PORT(CC, 0, 1, 8),
+ TEGRA194_AON_GPIO_PORT(DD, 0, 2, 3),
+ TEGRA194_AON_GPIO_PORT(EE, 0, 0, 7)
};
static const struct tegra_gpio_soc tegra194_aon_soc = {
.num_ports = ARRAY_SIZE(tegra194_aon_ports),
.ports = tegra194_aon_ports,
.name = "tegra194-gpio-aon",
+ .instance = 1,
+ .num_irqs_per_bank = 8,
+ .has_gte = true,
+};
+
+#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ [TEGRA234_MAIN_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
+ }
+
+static const struct tegra_gpio_port tegra234_main_ports[] = {
+ TEGRA234_MAIN_GPIO_PORT( A, 0, 0, 8),
+ TEGRA234_MAIN_GPIO_PORT( B, 0, 3, 1),
+ TEGRA234_MAIN_GPIO_PORT( C, 5, 1, 8),
+ TEGRA234_MAIN_GPIO_PORT( D, 5, 2, 4),
+ TEGRA234_MAIN_GPIO_PORT( E, 5, 3, 8),
+ TEGRA234_MAIN_GPIO_PORT( F, 5, 4, 6),
+ TEGRA234_MAIN_GPIO_PORT( G, 4, 0, 8),
+ TEGRA234_MAIN_GPIO_PORT( H, 4, 1, 8),
+ TEGRA234_MAIN_GPIO_PORT( I, 4, 2, 7),
+ TEGRA234_MAIN_GPIO_PORT( J, 5, 0, 6),
+ TEGRA234_MAIN_GPIO_PORT( K, 3, 0, 8),
+ TEGRA234_MAIN_GPIO_PORT( L, 3, 1, 4),
+ TEGRA234_MAIN_GPIO_PORT( M, 2, 0, 8),
+ TEGRA234_MAIN_GPIO_PORT( N, 2, 1, 8),
+ TEGRA234_MAIN_GPIO_PORT( P, 2, 2, 8),
+ TEGRA234_MAIN_GPIO_PORT( Q, 2, 3, 8),
+ TEGRA234_MAIN_GPIO_PORT( R, 2, 4, 6),
+ TEGRA234_MAIN_GPIO_PORT( X, 1, 0, 8),
+ TEGRA234_MAIN_GPIO_PORT( Y, 1, 1, 8),
+ TEGRA234_MAIN_GPIO_PORT( Z, 1, 2, 8),
+ TEGRA234_MAIN_GPIO_PORT(AC, 0, 1, 8),
+ TEGRA234_MAIN_GPIO_PORT(AD, 0, 2, 4),
+ TEGRA234_MAIN_GPIO_PORT(AE, 3, 3, 2),
+ TEGRA234_MAIN_GPIO_PORT(AF, 3, 4, 4),
+ TEGRA234_MAIN_GPIO_PORT(AG, 3, 2, 8),
+};
+
+static const struct tegra_gpio_soc tegra234_main_soc = {
+ .num_ports = ARRAY_SIZE(tegra234_main_ports),
+ .ports = tegra234_main_ports,
+ .name = "tegra234-gpio",
+ .instance = 0,
+ .num_irqs_per_bank = 8,
+};
+
+#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ [TEGRA234_AON_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
+ }
+
+static const struct tegra_gpio_port tegra234_aon_ports[] = {
+ TEGRA234_AON_GPIO_PORT(AA, 0, 4, 8),
+ TEGRA234_AON_GPIO_PORT(BB, 0, 5, 4),
+ TEGRA234_AON_GPIO_PORT(CC, 0, 2, 8),
+ TEGRA234_AON_GPIO_PORT(DD, 0, 3, 3),
+ TEGRA234_AON_GPIO_PORT(EE, 0, 0, 8),
+ TEGRA234_AON_GPIO_PORT(GG, 0, 1, 1),
+};
+
+static const struct tegra_gpio_soc tegra234_aon_soc = {
+ .num_ports = ARRAY_SIZE(tegra234_aon_ports),
+ .ports = tegra234_aon_ports,
+ .name = "tegra234-gpio-aon",
+ .instance = 1,
+ .num_irqs_per_bank = 8,
+};
+
+#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ [TEGRA241_MAIN_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
+ }
+
+static const struct tegra_gpio_port tegra241_main_ports[] = {
+ TEGRA241_MAIN_GPIO_PORT(A, 0, 0, 8),
+ TEGRA241_MAIN_GPIO_PORT(B, 0, 1, 8),
+ TEGRA241_MAIN_GPIO_PORT(C, 0, 2, 2),
+ TEGRA241_MAIN_GPIO_PORT(D, 0, 3, 6),
+ TEGRA241_MAIN_GPIO_PORT(E, 0, 4, 8),
+ TEGRA241_MAIN_GPIO_PORT(F, 1, 0, 8),
+ TEGRA241_MAIN_GPIO_PORT(G, 1, 1, 8),
+ TEGRA241_MAIN_GPIO_PORT(H, 1, 2, 8),
+ TEGRA241_MAIN_GPIO_PORT(J, 1, 3, 8),
+ TEGRA241_MAIN_GPIO_PORT(K, 1, 4, 4),
+ TEGRA241_MAIN_GPIO_PORT(L, 1, 5, 6),
+};
+
+static const struct tegra_gpio_soc tegra241_main_soc = {
+ .num_ports = ARRAY_SIZE(tegra241_main_ports),
+ .ports = tegra241_main_ports,
+ .name = "tegra241-gpio",
+ .instance = 0,
+ .num_irqs_per_bank = 8,
+};
+
+#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ [TEGRA241_AON_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
+ }
+
+static const struct tegra_gpio_port tegra241_aon_ports[] = {
+ TEGRA241_AON_GPIO_PORT(AA, 0, 0, 8),
+ TEGRA241_AON_GPIO_PORT(BB, 0, 0, 4),
+};
+
+static const struct tegra_gpio_soc tegra241_aon_soc = {
+ .num_ports = ARRAY_SIZE(tegra241_aon_ports),
+ .ports = tegra241_aon_ports,
+ .name = "tegra241-gpio-aon",
+ .instance = 1,
+ .num_irqs_per_bank = 8,
};
static const struct of_device_id tegra186_gpio_of_match[] = {
@@ -672,17 +1206,35 @@ static const struct of_device_id tegra186_gpio_of_match[] = {
.compatible = "nvidia,tegra194-gpio-aon",
.data = &tegra194_aon_soc
}, {
+ .compatible = "nvidia,tegra234-gpio",
+ .data = &tegra234_main_soc
+ }, {
+ .compatible = "nvidia,tegra234-gpio-aon",
+ .data = &tegra234_aon_soc
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra186_gpio_of_match);
+
+static const struct acpi_device_id tegra186_gpio_acpi_match[] = {
+ { .id = "NVDA0108", .driver_data = (kernel_ulong_t)&tegra186_main_soc },
+ { .id = "NVDA0208", .driver_data = (kernel_ulong_t)&tegra186_aon_soc },
+ { .id = "NVDA0308", .driver_data = (kernel_ulong_t)&tegra194_main_soc },
+ { .id = "NVDA0408", .driver_data = (kernel_ulong_t)&tegra194_aon_soc },
+ { .id = "NVDA0508", .driver_data = (kernel_ulong_t)&tegra241_main_soc },
+ { .id = "NVDA0608", .driver_data = (kernel_ulong_t)&tegra241_aon_soc },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tegra186_gpio_acpi_match);
static struct platform_driver tegra186_gpio_driver = {
.driver = {
.name = "tegra186-gpio",
.of_match_table = tegra186_gpio_of_match,
+ .acpi_match_table = tegra186_gpio_acpi_match,
},
.probe = tegra186_gpio_probe,
- .remove = tegra186_gpio_remove,
};
module_platform_driver(tegra186_gpio_driver);
diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c
index 715371b5102a..9f66deab46ea 100644
--- a/drivers/gpio/gpio-thunderx.c
+++ b/drivers/gpio/gpio-thunderx.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
+#include <asm-generic/msi.h>
#define GPIO_RX_DAT 0x0
@@ -53,7 +54,6 @@ struct thunderx_line {
struct thunderx_gpio {
struct gpio_chip chip;
u8 __iomem *register_base;
- struct irq_domain *irqd;
struct msix_entry *msix_entries; /* per line MSI-X */
struct thunderx_line *line_entries; /* per line irq info */
raw_spinlock_t lock;
@@ -170,7 +170,10 @@ static int thunderx_gpio_get_direction(struct gpio_chip *chip, unsigned int line
bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
- return !(bit_cfg & GPIO_BIT_CFG_TX_OE);
+ if (bit_cfg & GPIO_BIT_CFG_TX_OE)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int thunderx_gpio_set_config(struct gpio_chip *chip,
@@ -283,54 +286,60 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip,
}
}
-static void thunderx_gpio_irq_ack(struct irq_data *data)
+static void thunderx_gpio_irq_ack(struct irq_data *d)
{
- struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
writeq(GPIO_INTR_INTR,
- txline->txgpio->register_base + intr_reg(txline->line));
+ txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
}
-static void thunderx_gpio_irq_mask(struct irq_data *data)
+static void thunderx_gpio_irq_mask(struct irq_data *d)
{
- struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
writeq(GPIO_INTR_ENA_W1C,
- txline->txgpio->register_base + intr_reg(txline->line));
+ txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
}
-static void thunderx_gpio_irq_mask_ack(struct irq_data *data)
+static void thunderx_gpio_irq_mask_ack(struct irq_data *d)
{
- struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR,
- txline->txgpio->register_base + intr_reg(txline->line));
+ txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
}
-static void thunderx_gpio_irq_unmask(struct irq_data *data)
+static void thunderx_gpio_irq_unmask(struct irq_data *d)
{
- struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
writeq(GPIO_INTR_ENA_W1S,
- txline->txgpio->register_base + intr_reg(txline->line));
+ txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
}
-static int thunderx_gpio_irq_set_type(struct irq_data *data,
+static int thunderx_gpio_irq_set_type(struct irq_data *d,
unsigned int flow_type)
{
- struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
- struct thunderx_gpio *txgpio = txline->txgpio;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
+ struct thunderx_line *txline =
+ &txgpio->line_entries[irqd_to_hwirq(d)];
u64 bit_cfg;
- irqd_set_trigger_type(data, flow_type);
+ irqd_set_trigger_type(d, flow_type);
bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN;
if (flow_type & IRQ_TYPE_EDGE_BOTH) {
- irq_set_handler_locked(data, handle_fasteoi_ack_irq);
+ irq_set_handler_locked(d, handle_fasteoi_ack_irq);
bit_cfg |= GPIO_BIT_CFG_INT_TYPE;
} else {
- irq_set_handler_locked(data, handle_fasteoi_mask_irq);
+ irq_set_handler_locked(d, handle_fasteoi_mask_irq);
}
raw_spin_lock(&txgpio->lock);
@@ -359,33 +368,6 @@ static void thunderx_gpio_irq_disable(struct irq_data *data)
irq_chip_disable_parent(data);
}
-static int thunderx_gpio_irq_request_resources(struct irq_data *data)
-{
- struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
- struct thunderx_gpio *txgpio = txline->txgpio;
- int r;
-
- r = gpiochip_lock_as_irq(&txgpio->chip, txline->line);
- if (r)
- return r;
-
- r = irq_chip_request_resources_parent(data);
- if (r)
- gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
-
- return r;
-}
-
-static void thunderx_gpio_irq_release_resources(struct irq_data *data)
-{
- struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
- struct thunderx_gpio *txgpio = txline->txgpio;
-
- irq_chip_release_resources_parent(data);
-
- gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
-}
-
/*
* Interrupts are chained from underlying MSI-X vectors. We have
* these irq_chip functions to be able to handle level triggering
@@ -402,48 +384,42 @@ static struct irq_chip thunderx_gpio_irq_chip = {
.irq_unmask = thunderx_gpio_irq_unmask,
.irq_eoi = irq_chip_eoi_parent,
.irq_set_affinity = irq_chip_set_affinity_parent,
- .irq_request_resources = thunderx_gpio_irq_request_resources,
- .irq_release_resources = thunderx_gpio_irq_release_resources,
.irq_set_type = thunderx_gpio_irq_set_type,
.flags = IRQCHIP_SET_TYPE_MASKED
};
-static int thunderx_gpio_irq_translate(struct irq_domain *d,
- struct irq_fwspec *fwspec,
- irq_hw_number_t *hwirq,
- unsigned int *type)
+static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+ unsigned int child,
+ unsigned int child_type,
+ unsigned int *parent,
+ unsigned int *parent_type)
{
- struct thunderx_gpio *txgpio = d->host_data;
+ struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
+ struct irq_data *irqd;
+ unsigned int irq;
- if (WARN_ON(fwspec->param_count < 2))
- return -EINVAL;
- if (fwspec->param[0] >= txgpio->chip.ngpio)
+ irq = txgpio->msix_entries[child].vector;
+ irqd = irq_domain_get_irq_data(gc->irq.parent_domain, irq);
+ if (!irqd)
return -EINVAL;
- *hwirq = fwspec->param[0];
- *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+ *parent = irqd_to_hwirq(irqd);
+ *parent_type = IRQ_TYPE_LEVEL_HIGH;
return 0;
}
-static int thunderx_gpio_irq_alloc(struct irq_domain *d, unsigned int virq,
- unsigned int nr_irqs, void *arg)
+static void *thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip,
+ unsigned int parent_hwirq,
+ unsigned int parent_type)
{
- struct thunderx_line *txline = arg;
-
- return irq_domain_set_hwirq_and_chip(d, virq, txline->line,
- &thunderx_gpio_irq_chip, txline);
-}
-
-static const struct irq_domain_ops thunderx_gpio_irqd_ops = {
- .alloc = thunderx_gpio_irq_alloc,
- .translate = thunderx_gpio_irq_translate
-};
+ msi_alloc_info_t *info;
-static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
-{
- struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
- return irq_find_mapping(txgpio->irqd, offset);
+ info->hwirq = parent_hwirq;
+ return info;
}
static int thunderx_gpio_probe(struct pci_dev *pdev,
@@ -453,6 +429,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
struct device *dev = &pdev->dev;
struct thunderx_gpio *txgpio;
struct gpio_chip *chip;
+ struct gpio_irq_chip *girq;
int ngpio, i;
int err = 0;
@@ -497,8 +474,8 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
}
txgpio->msix_entries = devm_kcalloc(dev,
- ngpio, sizeof(struct msix_entry),
- GFP_KERNEL);
+ ngpio, sizeof(struct msix_entry),
+ GFP_KERNEL);
if (!txgpio->msix_entries) {
err = -ENOMEM;
goto out;
@@ -539,27 +516,6 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
if (err < 0)
goto out;
- /*
- * Push GPIO specific irqdomain on hierarchy created as a side
- * effect of the pci_enable_msix()
- */
- txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain,
- 0, 0, of_node_to_fwnode(dev->of_node),
- &thunderx_gpio_irqd_ops, txgpio);
- if (!txgpio->irqd) {
- err = -ENOMEM;
- goto out;
- }
-
- /* Push on irq_data and the domain for each line. */
- for (i = 0; i < ngpio; i++) {
- err = irq_domain_push_irq(txgpio->irqd,
- txgpio->msix_entries[i].vector,
- &txgpio->line_entries[i]);
- if (err < 0)
- dev_err(dev, "irq_domain_push_irq: %d\n", err);
- }
-
chip->label = KBUILD_MODNAME;
chip->parent = dev;
chip->owner = THIS_MODULE;
@@ -574,11 +530,35 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
chip->set = thunderx_gpio_set;
chip->set_multiple = thunderx_gpio_set_multiple;
chip->set_config = thunderx_gpio_set_config;
- chip->to_irq = thunderx_gpio_to_irq;
+ girq = &chip->irq;
+ girq->chip = &thunderx_gpio_irq_chip;
+ girq->fwnode = of_node_to_fwnode(dev->of_node);
+ girq->parent_domain =
+ irq_get_irq_data(txgpio->msix_entries[0].vector)->domain;
+ girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq;
+ girq->populate_parent_alloc_arg = thunderx_gpio_populate_parent_alloc_info;
+ girq->handler = handle_bad_irq;
+ girq->default_type = IRQ_TYPE_NONE;
+
err = devm_gpiochip_add_data(dev, chip, txgpio);
if (err)
goto out;
+ /* Push on irq_data and the domain for each line. */
+ for (i = 0; i < ngpio; i++) {
+ struct irq_fwspec fwspec;
+
+ fwspec.fwnode = of_node_to_fwnode(dev->of_node);
+ fwspec.param_count = 2;
+ fwspec.param[0] = i;
+ fwspec.param[1] = IRQ_TYPE_NONE;
+ err = irq_domain_push_irq(girq->domain,
+ txgpio->msix_entries[i].vector,
+ &fwspec);
+ if (err < 0)
+ dev_err(dev, "irq_domain_push_irq: %d\n", err);
+ }
+
dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n",
ngpio, chip->base);
return 0;
@@ -593,10 +573,10 @@ static void thunderx_gpio_remove(struct pci_dev *pdev)
struct thunderx_gpio *txgpio = pci_get_drvdata(pdev);
for (i = 0; i < txgpio->chip.ngpio; i++)
- irq_domain_pop_irq(txgpio->irqd,
+ irq_domain_pop_irq(txgpio->chip.irq.domain,
txgpio->msix_entries[i].vector);
- irq_domain_remove(txgpio->irqd);
+ irq_domain_remove(txgpio->chip.irq.domain);
pci_set_drvdata(pdev, NULL);
}
diff --git a/drivers/gpio/gpio-tn48m.c b/drivers/gpio/gpio-tn48m.c
new file mode 100644
index 000000000000..cd4a80b22794
--- /dev/null
+++ b/drivers/gpio/gpio-tn48m.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Delta TN48M CPLD GPIO driver
+ *
+ * Copyright (C) 2021 Sartura Ltd.
+ *
+ * Author: Robert Marko <robert.marko@sartura.hr>
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+enum tn48m_gpio_type {
+ TN48M_GP0 = 1,
+ TN48M_GPI,
+};
+
+struct tn48m_gpio_config {
+ int ngpio;
+ int ngpio_per_reg;
+ enum tn48m_gpio_type type;
+};
+
+static const struct tn48m_gpio_config tn48m_gpo_config = {
+ .ngpio = 4,
+ .ngpio_per_reg = 4,
+ .type = TN48M_GP0,
+};
+
+static const struct tn48m_gpio_config tn48m_gpi_config = {
+ .ngpio = 4,
+ .ngpio_per_reg = 4,
+ .type = TN48M_GPI,
+};
+
+static int tn48m_gpio_probe(struct platform_device *pdev)
+{
+ const struct tn48m_gpio_config *gpio_config;
+ struct gpio_regmap_config config = {};
+ struct regmap *regmap;
+ u32 base;
+ int ret;
+
+ if (!pdev->dev.parent)
+ return -ENODEV;
+
+ gpio_config = device_get_match_data(&pdev->dev);
+ if (!gpio_config)
+ return -ENODEV;
+
+ ret = device_property_read_u32(&pdev->dev, "reg", &base);
+ if (ret)
+ return ret;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ config.regmap = regmap;
+ config.parent = &pdev->dev;
+ config.ngpio = gpio_config->ngpio;
+ config.ngpio_per_reg = gpio_config->ngpio_per_reg;
+ switch (gpio_config->type) {
+ case TN48M_GP0:
+ config.reg_set_base = base;
+ break;
+ case TN48M_GPI:
+ config.reg_dat_base = base;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
+}
+
+static const struct of_device_id tn48m_gpio_of_match[] = {
+ { .compatible = "delta,tn48m-gpo", .data = &tn48m_gpo_config },
+ { .compatible = "delta,tn48m-gpi", .data = &tn48m_gpi_config },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tn48m_gpio_of_match);
+
+static struct platform_driver tn48m_gpio_driver = {
+ .driver = {
+ .name = "delta-tn48m-gpio",
+ .of_match_table = tn48m_gpio_of_match,
+ },
+ .probe = tn48m_gpio_probe,
+};
+module_platform_driver(tn48m_gpio_driver);
+
+MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
+MODULE_DESCRIPTION("Delta TN48M CPLD GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c
index c8b34d787eed..99d5a84a9129 100644
--- a/drivers/gpio/gpio-tpic2810.c
+++ b/drivers/gpio/gpio-tpic2810.c
@@ -39,7 +39,7 @@ static int tpic2810_get_direction(struct gpio_chip *chip,
unsigned offset)
{
/* This device always output */
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
}
static int tpic2810_direction_input(struct gpio_chip *chip,
diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c
index 2eea98ff4ea3..1e9d8262d0ff 100644
--- a/drivers/gpio/gpio-tps65086.c
+++ b/drivers/gpio/gpio-tps65086.c
@@ -21,7 +21,7 @@ static int tps65086_gpio_get_direction(struct gpio_chip *chip,
unsigned offset)
{
/* This device is output only */
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
}
static int tps65086_gpio_direction_input(struct gpio_chip *chip,
diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c
index 43a1150055ce..e1d425a18854 100644
--- a/drivers/gpio/gpio-tps65218.c
+++ b/drivers/gpio/gpio-tps65218.c
@@ -187,7 +187,6 @@ static int tps65218_gpio_probe(struct platform_device *pdev)
{
struct tps65218 *tps65218 = dev_get_drvdata(pdev->dev.parent);
struct tps65218_gpio *tps65218_gpio;
- int ret;
tps65218_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65218_gpio),
GFP_KERNEL);
@@ -197,20 +196,9 @@ static int tps65218_gpio_probe(struct platform_device *pdev)
tps65218_gpio->tps65218 = tps65218;
tps65218_gpio->gpio_chip = template_chip;
tps65218_gpio->gpio_chip.parent = &pdev->dev;
-#ifdef CONFIG_OF_GPIO
- tps65218_gpio->gpio_chip.of_node = pdev->dev.of_node;
-#endif
-
- ret = devm_gpiochip_add_data(&pdev->dev, &tps65218_gpio->gpio_chip,
- tps65218_gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, tps65218_gpio);
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &tps65218_gpio->gpio_chip,
+ tps65218_gpio);
}
static const struct of_device_id tps65218_dt_match[] = {
@@ -239,4 +227,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-tps6586x.c b/drivers/gpio/gpio-tps6586x.c
index 9b6cc74f47c8..c5713524b581 100644
--- a/drivers/gpio/gpio-tps6586x.c
+++ b/drivers/gpio/gpio-tps6586x.c
@@ -76,7 +76,8 @@ static int tps6586x_gpio_probe(struct platform_device *pdev)
{
struct tps6586x_platform_data *pdata;
struct tps6586x_gpio *tps6586x_gpio;
- int ret;
+
+ device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent));
pdata = dev_get_platdata(pdev->dev.parent);
tps6586x_gpio = devm_kzalloc(&pdev->dev,
@@ -98,24 +99,13 @@ static int tps6586x_gpio_probe(struct platform_device *pdev)
tps6586x_gpio->gpio_chip.get = tps6586x_gpio_get;
tps6586x_gpio->gpio_chip.to_irq = tps6586x_gpio_to_irq;
-#ifdef CONFIG_OF_GPIO
- tps6586x_gpio->gpio_chip.of_node = pdev->dev.parent->of_node;
-#endif
if (pdata && pdata->gpio_base)
tps6586x_gpio->gpio_chip.base = pdata->gpio_base;
else
tps6586x_gpio->gpio_chip.base = -1;
- ret = devm_gpiochip_add_data(&pdev->dev, &tps6586x_gpio->gpio_chip,
- tps6586x_gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, tps6586x_gpio);
-
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &tps6586x_gpio->gpio_chip,
+ tps6586x_gpio);
}
static struct platform_driver tps6586x_gpio_driver = {
diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c
index 0c785b0fd161..321e6945f0be 100644
--- a/drivers/gpio/gpio-tps65910.c
+++ b/drivers/gpio/gpio-tps65910.c
@@ -28,7 +28,7 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)
struct tps65910 *tps65910 = tps65910_gpio->tps65910;
unsigned int val;
- tps65910_reg_read(tps65910, TPS65910_GPIO0 + offset, &val);
+ regmap_read(tps65910->regmap, TPS65910_GPIO0 + offset, &val);
if (val & GPIO_STS_MASK)
return 1;
@@ -43,10 +43,10 @@ static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset,
struct tps65910 *tps65910 = tps65910_gpio->tps65910;
if (value)
- tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
+ regmap_set_bits(tps65910->regmap, TPS65910_GPIO0 + offset,
GPIO_SET_MASK);
else
- tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
+ regmap_clear_bits(tps65910->regmap, TPS65910_GPIO0 + offset,
GPIO_SET_MASK);
}
@@ -59,7 +59,7 @@ static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset,
/* Set the initial value */
tps65910_gpio_set(gc, offset, value);
- return tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
+ return regmap_set_bits(tps65910->regmap, TPS65910_GPIO0 + offset,
GPIO_CFG_MASK);
}
@@ -68,7 +68,7 @@ static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)
struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc);
struct tps65910 *tps65910 = tps65910_gpio->tps65910;
- return tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
+ return regmap_clear_bits(tps65910->regmap, TPS65910_GPIO0 + offset,
GPIO_CFG_MASK);
}
@@ -111,6 +111,8 @@ static int tps65910_gpio_probe(struct platform_device *pdev)
int ret;
int i;
+ device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent));
+
tps65910_gpio = devm_kzalloc(&pdev->dev,
sizeof(*tps65910_gpio), GFP_KERNEL);
if (!tps65910_gpio)
@@ -137,9 +139,7 @@ static int tps65910_gpio_probe(struct platform_device *pdev)
tps65910_gpio->gpio_chip.set = tps65910_gpio_set;
tps65910_gpio->gpio_chip.get = tps65910_gpio_get;
tps65910_gpio->gpio_chip.parent = &pdev->dev;
-#ifdef CONFIG_OF_GPIO
- tps65910_gpio->gpio_chip.of_node = tps65910->dev->of_node;
-#endif
+
if (pdata && pdata->gpio_base)
tps65910_gpio->gpio_chip.base = pdata->gpio_base;
else
@@ -157,7 +157,7 @@ static int tps65910_gpio_probe(struct platform_device *pdev)
if (!pdata->en_gpio_sleep[i])
continue;
- ret = tps65910_reg_set_bits(tps65910,
+ ret = regmap_set_bits(tps65910->regmap,
TPS65910_GPIO0 + i, GPIO_SLEEP_MASK);
if (ret < 0)
dev_warn(tps65910->dev,
@@ -165,16 +165,8 @@ static int tps65910_gpio_probe(struct platform_device *pdev)
}
skip_init:
- ret = devm_gpiochip_add_data(&pdev->dev, &tps65910_gpio->gpio_chip,
- tps65910_gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, tps65910_gpio);
-
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &tps65910_gpio->gpio_chip,
+ tps65910_gpio);
}
static struct platform_driver tps65910_gpio_driver = {
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
index 3ad68bd78282..fab771cb6a87 100644
--- a/drivers/gpio/gpio-tps65912.c
+++ b/drivers/gpio/gpio-tps65912.c
@@ -32,9 +32,9 @@ static int tps65912_gpio_get_direction(struct gpio_chip *gc,
return ret;
if (val & GPIO_CFG_MASK)
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
else
- return 1;
+ return GPIO_LINE_DIRECTION_IN;
}
static int tps65912_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
@@ -99,7 +99,6 @@ static int tps65912_gpio_probe(struct platform_device *pdev)
{
struct tps65912 *tps = dev_get_drvdata(pdev->dev.parent);
struct tps65912_gpio *gpio;
- int ret;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
@@ -109,16 +108,7 @@ static int tps65912_gpio_probe(struct platform_device *pdev)
gpio->gpio_chip = template_chip;
gpio->gpio_chip.parent = tps->dev;
- ret = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip,
- gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, gpio);
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio);
}
static const struct platform_device_id tps65912_gpio_id_table[] = {
diff --git a/drivers/gpio/gpio-tps68470.c b/drivers/gpio/gpio-tps68470.c
index aff6e504c666..aaddcabe9b35 100644
--- a/drivers/gpio/gpio-tps68470.c
+++ b/drivers/gpio/gpio-tps68470.c
@@ -47,7 +47,6 @@ static int tps68470_gpio_get(struct gpio_chip *gc, unsigned int offset)
return !!(val & BIT(offset));
}
-/* Return 0 if output, 1 if input */
static int tps68470_gpio_get_direction(struct gpio_chip *gc,
unsigned int offset)
{
@@ -57,7 +56,7 @@ static int tps68470_gpio_get_direction(struct gpio_chip *gc,
/* rest are always outputs */
if (offset >= TPS68470_N_REGULAR_GPIO)
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
ret = regmap_read(regmap, TPS68470_GPIO_CTL_REG_A(offset), &val);
if (ret) {
@@ -67,7 +66,8 @@ static int tps68470_gpio_get_direction(struct gpio_chip *gc,
}
val &= TPS68470_GPIO_MODE_MASK;
- return val >= TPS68470_GPIO_MODE_OUT_CMOS ? 0 : 1;
+ return val >= TPS68470_GPIO_MODE_OUT_CMOS ? GPIO_LINE_DIRECTION_OUT :
+ GPIO_LINE_DIRECTION_IN;
}
static void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset,
@@ -125,7 +125,6 @@ static const char *tps68470_names[TPS68470_N_GPIO] = {
static int tps68470_gpio_probe(struct platform_device *pdev)
{
struct tps68470_gpio_data *tps68470_gpio;
- int ret;
tps68470_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps68470_gpio),
GFP_KERNEL);
@@ -146,16 +145,7 @@ static int tps68470_gpio_probe(struct platform_device *pdev)
tps68470_gpio->gc.base = -1;
tps68470_gpio->gc.parent = &pdev->dev;
- ret = devm_gpiochip_add_data(&pdev->dev, &tps68470_gpio->gc,
- tps68470_gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register gpio_chip: %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, tps68470_gpio);
-
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &tps68470_gpio->gc, tps68470_gpio);
}
static struct platform_driver tps68470_gpio_driver = {
@@ -164,5 +154,8 @@ static struct platform_driver tps68470_gpio_driver = {
},
.probe = tps68470_gpio_probe,
};
+module_platform_driver(tps68470_gpio_driver);
-builtin_platform_driver(tps68470_gpio_driver)
+MODULE_ALIAS("platform:tps68470-gpio");
+MODULE_DESCRIPTION("GPIO driver for TPS68470 PMIC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c
index d5880db7f9d4..fa4bc7481f9a 100644
--- a/drivers/gpio/gpio-tqmx86.c
+++ b/drivers/gpio/gpio-tqmx86.c
@@ -101,7 +101,10 @@ static int tqmx86_gpio_direction_output(struct gpio_chip *chip,
static int tqmx86_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
- return !!(TQMX86_DIR_INPUT_MASK & BIT(offset));
+ if (TQMX86_DIR_INPUT_MASK & BIT(offset))
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static void tqmx86_gpio_irq_mask(struct irq_data *data)
@@ -180,7 +183,7 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc)
struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip);
struct irq_chip *irq_chip = irq_desc_get_chip(desc);
unsigned long irq_bits;
- int i = 0, child_irq;
+ int i = 0;
u8 irq_status;
chained_irq_enter(irq_chip, desc);
@@ -189,11 +192,9 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc)
tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS);
irq_bits = irq_status;
- for_each_set_bit(i, &irq_bits, TQMX86_NGPI) {
- child_irq = irq_find_mapping(gpio->chip.irq.domain,
- i + TQMX86_NGPO);
- generic_handle_irq(child_irq);
- }
+ for_each_set_bit(i, &irq_bits, TQMX86_NGPI)
+ generic_handle_domain_irq(gpio->chip.irq.domain,
+ i + TQMX86_NGPO);
chained_irq_exit(irq_chip, desc);
}
@@ -214,17 +215,29 @@ static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = {
tqmx86_gpio_runtime_resume, NULL)
};
+static void tqmx86_init_irq_valid_mask(struct gpio_chip *chip,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ /* Only GPIOs 4-7 are valid for interrupts. Clear the others */
+ clear_bit(0, valid_mask);
+ clear_bit(1, valid_mask);
+ clear_bit(2, valid_mask);
+ clear_bit(3, valid_mask);
+}
+
static int tqmx86_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tqmx86_gpio_data *gpio;
struct gpio_chip *chip;
+ struct gpio_irq_chip *girq;
void __iomem *io_base;
struct resource *res;
int ret, irq;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq < 0 && irq != -ENXIO)
return irq;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
@@ -259,23 +272,15 @@ static int tqmx86_gpio_probe(struct platform_device *pdev)
chip->get = tqmx86_gpio_get;
chip->set = tqmx86_gpio_set;
chip->ngpio = TQMX86_NGPIO;
- chip->irq.need_valid_mask = true;
chip->parent = pdev->dev.parent;
pm_runtime_enable(&pdev->dev);
- ret = devm_gpiochip_add_data(dev, chip, gpio);
- if (ret) {
- dev_err(dev, "Could not register GPIO chip\n");
- goto out_pm_dis;
- }
-
- if (irq) {
+ if (irq > 0) {
struct irq_chip *irq_chip = &gpio->irq_chip;
u8 irq_status;
irq_chip->name = chip->label;
- irq_chip->parent_device = &pdev->dev;
irq_chip->irq_mask = tqmx86_gpio_irq_mask;
irq_chip->irq_unmask = tqmx86_gpio_irq_unmask;
irq_chip->irq_set_type = tqmx86_gpio_irq_set_type;
@@ -287,23 +292,30 @@ static int tqmx86_gpio_probe(struct platform_device *pdev)
irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS);
tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS);
- ret = gpiochip_irqchip_add(chip, irq_chip,
- 0, handle_simple_irq,
- IRQ_TYPE_EDGE_BOTH);
- if (ret) {
- dev_err(dev, "Could not add irq chip\n");
+ girq = &chip->irq;
+ girq->chip = irq_chip;
+ girq->parent_handler = tqmx86_gpio_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents) {
+ ret = -ENOMEM;
goto out_pm_dis;
}
+ girq->parents[0] = irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->init_valid_mask = tqmx86_init_irq_valid_mask;
+ }
- gpiochip_set_chained_irqchip(chip, irq_chip,
- irq, tqmx86_gpio_irq_handler);
+ ret = devm_gpiochip_add_data(dev, chip, gpio);
+ if (ret) {
+ dev_err(dev, "Could not register GPIO chip\n");
+ goto out_pm_dis;
}
- /* Only GPIOs 4-7 are valid for interrupts. Clear the others */
- clear_bit(0, chip->irq.valid_mask);
- clear_bit(1, chip->irq.valid_mask);
- clear_bit(2, chip->irq.valid_mask);
- clear_bit(3, chip->irq.valid_mask);
+ irq_domain_set_pm_device(girq->domain, dev);
dev_info(dev, "GPIO functionality initialized with %d pins\n",
chip->ngpio);
diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c
index 1da8d0586329..416725c26e94 100644
--- a/drivers/gpio/gpio-ts4900.c
+++ b/drivers/gpio/gpio-ts4900.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Digital I/O driver for Technologic Systems I2C FPGA Core
*
- * Copyright (C) 2015 Technologic Systems
+ * Copyright (C) 2015, 2018 Technologic Systems
* Copyright (C) 2016 Savoir-Faire Linux
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether expressed or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License version 2 for more details.
*/
#include <linux/gpio/driver.h>
@@ -44,7 +36,10 @@ static int ts4900_gpio_get_direction(struct gpio_chip *chip,
regmap_read(priv->regmap, offset, &reg);
- return !(reg & TS4900_GPIO_OE);
+ if (reg & TS4900_GPIO_OE)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int ts4900_gpio_direction_input(struct gpio_chip *chip,
@@ -53,18 +48,34 @@ static int ts4900_gpio_direction_input(struct gpio_chip *chip,
struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
/*
- * This will clear the output enable bit, the other bits are
- * dontcare when this is cleared
+ * Only clear the OE bit here, requires a RMW. Prevents a potential issue
+ * with OE and DAT getting to the physical pin at different times.
*/
- return regmap_write(priv->regmap, offset, 0);
+ return regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OE, 0);
}
static int ts4900_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
+ unsigned int reg;
int ret;
+ /*
+ * If changing from an input to an output, we need to first set the
+ * GPIO's DAT bit to what is requested and then set the OE bit. This
+ * prevents a glitch that can occur on the IO line.
+ */
+ regmap_read(priv->regmap, offset, &reg);
+ if (!(reg & TS4900_GPIO_OE)) {
+ if (value)
+ reg = TS4900_GPIO_OUT;
+ else
+ reg &= ~TS4900_GPIO_OUT;
+
+ regmap_write(priv->regmap, offset, reg);
+ }
+
if (value)
ret = regmap_write(priv->regmap, offset, TS4900_GPIO_OE |
TS4900_GPIO_OUT);
diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c
index c91890488402..8e03614c7a24 100644
--- a/drivers/gpio/gpio-ts5500.c
+++ b/drivers/gpio/gpio-ts5500.c
@@ -11,11 +11,11 @@
* Actually, the following platforms have DIO support:
*
* TS-5500:
- * Documentation: http://wiki.embeddedarm.com/wiki/TS-5500
+ * Documentation: https://docs.embeddedts.com/TS-5500
* Blocks: DIO1, DIO2 and LCD port.
*
* TS-5600:
- * Documentation: http://wiki.embeddedarm.com/wiki/TS-5600
+ * Documentation: https://docs.embeddedts.com/TS-5600
* Blocks: LCD port (identical to TS-5500 LCD).
*/
@@ -317,22 +317,19 @@ static int ts5500_dio_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
const char *name = dev_name(dev);
struct ts5500_priv *priv;
- struct resource *res;
unsigned long flags;
int ret;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "missing IRQ resource\n");
- return -EINVAL;
- }
+ 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 = res->start;
+ priv->hwirq = ret;
spin_lock_init(&priv->lock);
priv->gpio_chip.owner = THIS_MODULE;
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
index fbfb648d3502..de249726230e 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -165,10 +165,10 @@ static int twl4030_get_gpio_direction(int gpio)
if (ret < 0)
return ret;
- /* 1 = output, but gpiolib semantics are inverse so invert */
- ret = !(ret & d_msk);
+ if (ret & d_msk)
+ return GPIO_LINE_DIRECTION_OUT;
- return ret;
+ return GPIO_LINE_DIRECTION_IN;
}
static int twl4030_set_gpio_dataout(int gpio, int enable)
@@ -380,10 +380,10 @@ static int twl_get_direction(struct gpio_chip *chip, unsigned offset)
{
struct gpio_twl4030_priv *priv = gpiochip_get_data(chip);
/*
- * Default 0 = output
+ * Default GPIO_LINE_DIRECTION_OUT
* LED GPIOs >= TWL4030_GPIO_MAX are always output
*/
- int ret = 0;
+ int ret = GPIO_LINE_DIRECTION_OUT;
mutex_lock(&priv->mutex);
if (offset < TWL4030_GPIO_MAX) {
diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c
index c845b2ff1f43..6c3fbf382dba 100644
--- a/drivers/gpio/gpio-twl6040.c
+++ b/drivers/gpio/gpio-twl6040.c
@@ -34,8 +34,7 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset)
static int twl6040gpo_get_direction(struct gpio_chip *chip, unsigned offset)
{
- /* This means "out" */
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
}
static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
@@ -81,6 +80,8 @@ static int gpo_twl6040_probe(struct platform_device *pdev)
struct twl6040 *twl6040 = dev_get_drvdata(twl6040_core_dev);
int ret;
+ device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent));
+
twl6040gpo_chip.base = -1;
if (twl6040_get_revid(twl6040) < TWL6041_REV_ES2_0)
@@ -89,9 +90,6 @@ static int gpo_twl6040_probe(struct platform_device *pdev)
twl6040gpo_chip.ngpio = 1; /* twl6041 have 1 GPO */
twl6040gpo_chip.parent = &pdev->dev;
-#ifdef CONFIG_OF_GPIO
- twl6040gpo_chip.of_node = twl6040_core_dev->of_node;
-#endif
ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, NULL);
if (ret < 0) {
diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c
index 93cdcc41e9fb..19ce6675cbc0 100644
--- a/drivers/gpio/gpio-uniphier.c
+++ b/drivers/gpio/gpio-uniphier.c
@@ -15,9 +15,6 @@
#include <linux/spinlock.h>
#include <dt-bindings/gpio/uniphier-gpio.h>
-#define UNIPHIER_GPIO_BANK_MASK \
- GENMASK((UNIPHIER_GPIO_LINES_PER_BANK) - 1, 0)
-
#define UNIPHIER_GPIO_IRQ_MAX_NUM 24
#define UNIPHIER_GPIO_PORT_DATA 0x0 /* data */
@@ -33,7 +30,7 @@ struct uniphier_gpio_priv {
struct irq_domain *domain;
void __iomem *regs;
spinlock_t lock;
- u32 saved_vals[0];
+ u32 saved_vals[];
};
static unsigned int uniphier_gpio_bank_to_reg(unsigned int bank)
@@ -113,7 +110,10 @@ static int uniphier_gpio_offset_read(struct gpio_chip *chip,
static int uniphier_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
- return uniphier_gpio_offset_read(chip, offset, UNIPHIER_GPIO_PORT_DIR);
+ if (uniphier_gpio_offset_read(chip, offset, UNIPHIER_GPIO_PORT_DIR))
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int uniphier_gpio_direction_input(struct gpio_chip *chip,
@@ -147,15 +147,11 @@ static void uniphier_gpio_set(struct gpio_chip *chip,
static void uniphier_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
- unsigned int bank, shift, bank_mask, bank_bits;
- int i;
+ unsigned long i, bank, bank_mask, bank_bits;
- for (i = 0; i < chip->ngpio; i += UNIPHIER_GPIO_LINES_PER_BANK) {
+ for_each_set_clump8(i, bank_mask, mask, chip->ngpio) {
bank = i / UNIPHIER_GPIO_LINES_PER_BANK;
- shift = i % BITS_PER_LONG;
- bank_mask = (mask[BIT_WORD(i)] >> shift) &
- UNIPHIER_GPIO_BANK_MASK;
- bank_bits = bits[BIT_WORD(i)] >> shift;
+ bank_bits = bitmap_get_value8(bits, i);
uniphier_gpio_bank_write(chip, bank, UNIPHIER_GPIO_PORT_DATA,
bank_mask, bank_bits);
@@ -183,28 +179,28 @@ 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);
- return irq_chip_mask_parent(data);
+ irq_chip_mask_parent(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);
- return irq_chip_unmask_parent(data);
+ irq_chip_unmask_parent(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) {
@@ -301,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,
@@ -310,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-vf610.c b/drivers/gpio/gpio-vf610.c
index 7ba668db171b..23cddb265a0d 100644
--- a/drivers/gpio/gpio-vf610.c
+++ b/drivers/gpio/gpio-vf610.c
@@ -125,9 +125,13 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
{
struct vf610_gpio_port *port = gpiochip_get_data(chip);
unsigned long mask = BIT(gpio);
+ u32 val;
- if (port->sdata && port->sdata->have_paddr)
- vf610_gpio_writel(mask, port->gpio_base + GPIO_PDDR);
+ if (port->sdata && port->sdata->have_paddr) {
+ val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
+ val |= mask;
+ vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
+ }
vf610_gpio_set(chip, gpio, value);
@@ -149,7 +153,7 @@ static void vf610_gpio_irq_handler(struct irq_desc *desc)
for_each_set_bit(pin, &irq_isfr, VF610_GPIO_PER_PORT) {
vf610_gpio_writel(BIT(pin), port->base + PORT_ISFR);
- generic_handle_irq(irq_find_mapping(port->gc.irq.domain, pin));
+ generic_handle_domain_irq(port->gc.irq.domain, pin);
}
chained_irq_exit(chip, desc);
@@ -243,6 +247,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct vf610_gpio_port *port;
struct gpio_chip *gc;
+ struct gpio_irq_chip *girq;
struct irq_chip *ic;
int i;
int ret;
@@ -297,7 +302,6 @@ static int vf610_gpio_probe(struct platform_device *pdev)
}
gc = &port->gc;
- gc->of_node = np;
gc->parent = dev;
gc->label = "vf610-gpio";
gc->ngpio = VF610_GPIO_PER_PORT;
@@ -318,10 +322,6 @@ static int vf610_gpio_probe(struct platform_device *pdev)
ic->irq_set_type = vf610_gpio_irq_set_type;
ic->irq_set_wake = vf610_gpio_irq_set_wake;
- ret = devm_gpiochip_add_data(dev, gc, port);
- if (ret < 0)
- return ret;
-
/* Mask all GPIO interrupts */
for (i = 0; i < gc->ngpio; i++)
vf610_gpio_writel(0, port->base + PORT_PCR(i));
@@ -329,15 +329,20 @@ static int vf610_gpio_probe(struct platform_device *pdev)
/* Clear the interrupt status register for all GPIO's */
vf610_gpio_writel(~0, port->base + PORT_ISFR);
- ret = gpiochip_irqchip_add(gc, ic, 0, handle_edge_irq, IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "failed to add irqchip\n");
- return ret;
- }
- gpiochip_set_chained_irqchip(gc, ic, port->irq,
- vf610_gpio_irq_handler);
+ girq = &gc->irq;
+ girq->chip = ic;
+ girq->parent_handler = vf610_gpio_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = port->irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_edge_irq;
- return 0;
+ return devm_gpiochip_add_data(dev, gc, port);
}
static struct platform_driver vf610_gpio_driver = {
diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c
index 9b604f13e302..e55d28a8a66f 100644
--- a/drivers/gpio/gpio-viperboard.c
+++ b/drivers/gpio/gpio-viperboard.c
@@ -79,7 +79,7 @@ MODULE_PARM_DESC(gpioa_freq,
/* ----- begin of gipo a chip -------------------------------------------- */
static int vprbrd_gpioa_get(struct gpio_chip *chip,
- unsigned offset)
+ unsigned int offset)
{
int ret, answer, error = 0;
struct vprbrd_gpio *gpio = gpiochip_get_data(chip);
@@ -129,7 +129,7 @@ static int vprbrd_gpioa_get(struct gpio_chip *chip,
}
static void vprbrd_gpioa_set(struct gpio_chip *chip,
- unsigned offset, int value)
+ unsigned int offset, int value)
{
int ret;
struct vprbrd_gpio *gpio = gpiochip_get_data(chip);
@@ -170,7 +170,7 @@ static void vprbrd_gpioa_set(struct gpio_chip *chip,
}
static int vprbrd_gpioa_direction_input(struct gpio_chip *chip,
- unsigned offset)
+ unsigned int offset)
{
int ret;
struct vprbrd_gpio *gpio = gpiochip_get_data(chip);
@@ -207,7 +207,7 @@ static int vprbrd_gpioa_direction_input(struct gpio_chip *chip,
}
static int vprbrd_gpioa_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
+ unsigned int offset, int value)
{
int ret;
struct vprbrd_gpio *gpio = gpiochip_get_data(chip);
@@ -251,8 +251,8 @@ static int vprbrd_gpioa_direction_output(struct gpio_chip *chip,
/* ----- begin of gipo b chip -------------------------------------------- */
-static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset,
- unsigned dir)
+static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned int offset,
+ unsigned int dir)
{
struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
int ret;
@@ -273,7 +273,7 @@ static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset,
}
static int vprbrd_gpiob_get(struct gpio_chip *chip,
- unsigned offset)
+ unsigned int offset)
{
int ret;
u16 val;
@@ -305,7 +305,7 @@ static int vprbrd_gpiob_get(struct gpio_chip *chip,
}
static void vprbrd_gpiob_set(struct gpio_chip *chip,
- unsigned offset, int value)
+ unsigned int offset, int value)
{
int ret;
struct vprbrd_gpio *gpio = gpiochip_get_data(chip);
@@ -338,7 +338,7 @@ static void vprbrd_gpiob_set(struct gpio_chip *chip,
}
static int vprbrd_gpiob_direction_input(struct gpio_chip *chip,
- unsigned offset)
+ unsigned int offset)
{
int ret;
struct vprbrd_gpio *gpio = gpiochip_get_data(chip);
@@ -359,7 +359,7 @@ static int vprbrd_gpiob_direction_input(struct gpio_chip *chip,
}
static int vprbrd_gpiob_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
+ unsigned int offset, int value)
{
int ret;
struct vprbrd_gpio *gpio = gpiochip_get_data(chip);
@@ -404,11 +404,10 @@ static int vprbrd_gpio_probe(struct platform_device *pdev)
vb_gpio->gpioa.get = vprbrd_gpioa_get;
vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input;
vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output;
+
ret = devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpioa, vb_gpio);
- if (ret < 0) {
- dev_err(vb_gpio->gpioa.parent, "could not add gpio a");
+ if (ret < 0)
return ret;
- }
/* registering gpio b */
vb_gpio->gpiob.label = "viperboard gpio b";
@@ -421,15 +420,8 @@ static int vprbrd_gpio_probe(struct platform_device *pdev)
vb_gpio->gpiob.get = vprbrd_gpiob_get;
vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input;
vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output;
- ret = devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpiob, vb_gpio);
- if (ret < 0) {
- dev_err(vb_gpio->gpiob.parent, "could not add gpio b");
- return ret;
- }
-
- platform_set_drvdata(pdev, vb_gpio);
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpiob, vb_gpio);
}
static struct platform_driver vprbrd_gpio_driver = {
diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c
new file mode 100644
index 000000000000..fcc5e8c08973
--- /dev/null
+++ b/drivers/gpio/gpio-virtio.c
@@ -0,0 +1,664 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GPIO driver for virtio-based virtual GPIO controllers
+ *
+ * Copyright (C) 2021 metux IT consult
+ * Enrico Weigelt, metux IT consult <info@metux.net>
+ *
+ * Copyright (C) 2021 Linaro.
+ * Viresh Kumar <viresh.kumar@linaro.org>
+ */
+
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#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>
+
+struct virtio_gpio_line {
+ struct mutex lock; /* Protects line operation */
+ struct completion completion;
+ struct virtio_gpio_request req ____cacheline_aligned;
+ struct virtio_gpio_response res ____cacheline_aligned;
+ 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,
+ u8 txvalue, u8 *rxvalue, void *response, u32 rxlen)
+{
+ struct virtio_gpio_line *line = &vgpio->lines[gpio];
+ struct virtio_gpio_request *req = &line->req;
+ struct virtio_gpio_response *res = response;
+ struct scatterlist *sgs[2], req_sg, res_sg;
+ struct device *dev = &vgpio->vdev->dev;
+ int ret;
+
+ /*
+ * Prevent concurrent requests for the same line since we have
+ * pre-allocated request/response buffers for each GPIO line. Moreover
+ * Linux always accesses a GPIO line sequentially, so this locking shall
+ * always go through without any delays.
+ */
+ mutex_lock(&line->lock);
+
+ req->type = cpu_to_le16(type);
+ req->gpio = cpu_to_le16(gpio);
+ req->value = cpu_to_le32(txvalue);
+
+ sg_init_one(&req_sg, req, sizeof(*req));
+ sg_init_one(&res_sg, res, rxlen);
+ sgs[0] = &req_sg;
+ sgs[1] = &res_sg;
+
+ line->rxlen = 0;
+ reinit_completion(&line->completion);
+
+ /*
+ * Virtqueue callers need to ensure they don't call its APIs with other
+ * virtqueue operations at the same time.
+ */
+ mutex_lock(&vgpio->lock);
+ ret = virtqueue_add_sgs(vgpio->request_vq, sgs, 1, 1, line, GFP_KERNEL);
+ if (ret) {
+ dev_err(dev, "failed to add request to vq\n");
+ mutex_unlock(&vgpio->lock);
+ goto out;
+ }
+
+ virtqueue_kick(vgpio->request_vq);
+ mutex_unlock(&vgpio->lock);
+
+ wait_for_completion(&line->completion);
+
+ if (unlikely(res->status != VIRTIO_GPIO_STATUS_OK)) {
+ dev_err(dev, "GPIO request failed: %d\n", gpio);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (unlikely(line->rxlen != rxlen)) {
+ dev_err(dev, "GPIO operation returned incorrect len (%u : %u)\n",
+ rxlen, line->rxlen);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (rxvalue)
+ *rxvalue = res->value;
+
+out:
+ mutex_unlock(&line->lock);
+ return ret;
+}
+
+static int virtio_gpio_req(struct virtio_gpio *vgpio, u16 type, u16 gpio,
+ u8 txvalue, u8 *rxvalue)
+{
+ struct virtio_gpio_line *line = &vgpio->lines[gpio];
+ struct virtio_gpio_response *res = &line->res;
+
+ return _virtio_gpio_req(vgpio, type, gpio, txvalue, rxvalue, res,
+ sizeof(*res));
+}
+
+static void virtio_gpio_free(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct virtio_gpio *vgpio = gpiochip_get_data(gc);
+
+ virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_DIRECTION, gpio,
+ VIRTIO_GPIO_DIRECTION_NONE, NULL);
+}
+
+static int virtio_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct virtio_gpio *vgpio = gpiochip_get_data(gc);
+ u8 direction;
+ int ret;
+
+ ret = virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_GET_DIRECTION, gpio, 0,
+ &direction);
+ if (ret)
+ return ret;
+
+ switch (direction) {
+ case VIRTIO_GPIO_DIRECTION_IN:
+ return GPIO_LINE_DIRECTION_IN;
+ case VIRTIO_GPIO_DIRECTION_OUT:
+ return GPIO_LINE_DIRECTION_OUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int virtio_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct virtio_gpio *vgpio = gpiochip_get_data(gc);
+
+ return virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_DIRECTION, gpio,
+ VIRTIO_GPIO_DIRECTION_IN, NULL);
+}
+
+static int virtio_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
+ int value)
+{
+ struct virtio_gpio *vgpio = gpiochip_get_data(gc);
+ int ret;
+
+ ret = virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, NULL);
+ if (ret)
+ return ret;
+
+ return virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_DIRECTION, gpio,
+ VIRTIO_GPIO_DIRECTION_OUT, NULL);
+}
+
+static int virtio_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct virtio_gpio *vgpio = gpiochip_get_data(gc);
+ u8 value;
+ int ret;
+
+ ret = virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_GET_VALUE, gpio, 0, &value);
+ return ret ? ret : value;
+}
+
+static void virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
+{
+ struct virtio_gpio *vgpio = gpiochip_get_data(gc);
+
+ 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;
+ unsigned int len;
+
+ do {
+ line = virtqueue_get_buf(vq, &len);
+ if (!line)
+ return;
+
+ line->rxlen = len;
+ complete(&line->completion);
+ } while (1);
+}
+
+static void virtio_gpio_free_vqs(struct virtio_device *vdev)
+{
+ virtio_reset_device(vdev);
+ vdev->config->del_vqs(vdev);
+}
+
+static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio,
+ struct virtio_device *vdev)
+{
+ const char * const names[] = { "requestq", "eventq" };
+ vq_callback_t *cbs[] = {
+ virtio_gpio_request_vq,
+ virtio_gpio_event_vq,
+ };
+ struct virtqueue *vqs[2] = { NULL, NULL };
+ int ret;
+
+ 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;
+ }
+
+ if (!vqs[0]) {
+ dev_err(&vdev->dev, "failed to find requestq vq\n");
+ 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,
+ u32 gpio_names_size, u16 ngpio)
+{
+ struct virtio_gpio_response_get_names *res;
+ struct device *dev = &vgpio->vdev->dev;
+ u8 *gpio_names, *str;
+ const char **names;
+ int i, ret, len;
+
+ if (!gpio_names_size)
+ return NULL;
+
+ len = sizeof(*res) + gpio_names_size;
+ res = devm_kzalloc(dev, len, GFP_KERNEL);
+ if (!res)
+ return NULL;
+ gpio_names = res->value;
+
+ ret = _virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_GET_NAMES, 0, 0, NULL,
+ res, len);
+ if (ret) {
+ dev_err(dev, "Failed to get GPIO names: %d\n", ret);
+ return NULL;
+ }
+
+ names = devm_kcalloc(dev, ngpio, sizeof(*names), GFP_KERNEL);
+ if (!names)
+ return NULL;
+
+ /* NULL terminate the string instead of checking it */
+ gpio_names[gpio_names_size - 1] = '\0';
+
+ for (i = 0, str = gpio_names; i < ngpio; i++) {
+ names[i] = str;
+ str += strlen(str) + 1; /* zero-length strings are allowed */
+
+ if (str > gpio_names + gpio_names_size) {
+ dev_err(dev, "gpio_names block is too short (%d)\n", i);
+ return NULL;
+ }
+ }
+
+ return names;
+}
+
+static int virtio_gpio_probe(struct virtio_device *vdev)
+{
+ struct virtio_gpio_config config;
+ struct device *dev = &vdev->dev;
+ struct virtio_gpio *vgpio;
+ u32 gpio_names_size;
+ u16 ngpio;
+ int ret, i;
+
+ vgpio = devm_kzalloc(dev, sizeof(*vgpio), GFP_KERNEL);
+ if (!vgpio)
+ return -ENOMEM;
+
+ /* Read configuration */
+ virtio_cread_bytes(vdev, 0, &config, sizeof(config));
+ gpio_names_size = le32_to_cpu(config.gpio_names_size);
+ ngpio = le16_to_cpu(config.ngpio);
+ if (!ngpio) {
+ dev_err(dev, "Number of GPIOs can't be zero\n");
+ return -EINVAL;
+ }
+
+ vgpio->lines = devm_kcalloc(dev, ngpio, sizeof(*vgpio->lines), GFP_KERNEL);
+ if (!vgpio->lines)
+ return -ENOMEM;
+
+ for (i = 0; i < ngpio; i++) {
+ mutex_init(&vgpio->lines[i].lock);
+ init_completion(&vgpio->lines[i].completion);
+ }
+
+ mutex_init(&vgpio->lock);
+ vdev->priv = vgpio;
+
+ vgpio->vdev = vdev;
+ vgpio->gc.free = virtio_gpio_free;
+ vgpio->gc.get_direction = virtio_gpio_get_direction;
+ vgpio->gc.direction_input = virtio_gpio_direction_input;
+ vgpio->gc.direction_output = virtio_gpio_direction_output;
+ vgpio->gc.get = virtio_gpio_get;
+ vgpio->gc.set = virtio_gpio_set;
+ vgpio->gc.ngpio = ngpio;
+ vgpio->gc.base = -1; /* Allocate base dynamically */
+ vgpio->gc.label = dev_name(dev);
+ vgpio->gc.parent = dev;
+ 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;
+
+ /* Mark the device ready to perform operations from within probe() */
+ virtio_device_ready(vdev);
+
+ vgpio->gc.names = virtio_gpio_get_names(vgpio, gpio_names_size, ngpio);
+
+ ret = gpiochip_add_data(&vgpio->gc, vgpio);
+ if (ret) {
+ virtio_gpio_free_vqs(vdev);
+ dev_err(dev, "Failed to add virtio-gpio controller\n");
+ }
+
+ return ret;
+}
+
+static void virtio_gpio_remove(struct virtio_device *vdev)
+{
+ struct virtio_gpio *vgpio = vdev->priv;
+
+ gpiochip_remove(&vgpio->gc);
+ virtio_gpio_free_vqs(vdev);
+}
+
+static const struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_GPIO, VIRTIO_DEV_ANY_ID },
+ {},
+};
+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,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+};
+module_virtio_driver(virtio_gpio_driver);
+
+MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
+MODULE_DESCRIPTION("VirtIO GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-visconti.c b/drivers/gpio/gpio-visconti.c
new file mode 100644
index 000000000000..e6534ea1eaa7
--- /dev/null
+++ b/drivers/gpio/gpio-visconti.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Toshiba Visconti GPIO Support
+ *
+ * (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation
+ * (C) Copyright 2020 TOSHIBA CORPORATION
+ *
+ * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+
+/* register offset */
+#define GPIO_DIR 0x00
+#define GPIO_IDATA 0x08
+#define GPIO_ODATA 0x10
+#define GPIO_OSET 0x18
+#define GPIO_OCLR 0x20
+#define GPIO_INTMODE 0x30
+
+#define BASE_HW_IRQ 24
+
+struct visconti_gpio {
+ void __iomem *base;
+ spinlock_t lock; /* protect gpio register */
+ struct gpio_chip gpio_chip;
+ struct irq_chip irq_chip;
+};
+
+static int visconti_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct visconti_gpio *priv = gpiochip_get_data(gc);
+ u32 offset = irqd_to_hwirq(d);
+ u32 bit = BIT(offset);
+ u32 intc_type = IRQ_TYPE_EDGE_RISING;
+ u32 intmode, odata;
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ odata = readl(priv->base + GPIO_ODATA);
+ intmode = readl(priv->base + GPIO_INTMODE);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ odata &= ~bit;
+ intmode &= ~bit;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ odata |= bit;
+ intmode &= ~bit;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ intmode |= bit;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ intc_type = IRQ_TYPE_LEVEL_HIGH;
+ odata &= ~bit;
+ intmode &= ~bit;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ intc_type = IRQ_TYPE_LEVEL_HIGH;
+ odata |= bit;
+ intmode &= ~bit;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ writel(odata, priv->base + GPIO_ODATA);
+ writel(intmode, priv->base + GPIO_INTMODE);
+ irq_set_irq_type(offset, intc_type);
+
+ ret = irq_chip_set_type_parent(d, type);
+err:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return ret;
+}
+
+static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+ unsigned int child,
+ unsigned int child_type,
+ unsigned int *parent,
+ unsigned int *parent_type)
+{
+ /* Interrupts 0..15 mapped to interrupts 24..39 on the GIC */
+ if (child < 16) {
+ /* All these interrupts are level high in the CPU */
+ *parent_type = IRQ_TYPE_LEVEL_HIGH;
+ *parent = child + BASE_HW_IRQ;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
+ unsigned int parent_hwirq,
+ unsigned int parent_type)
+{
+ struct irq_fwspec *fwspec;
+
+ fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
+ if (!fwspec)
+ return NULL;
+
+ fwspec->fwnode = chip->irq.parent_domain->fwnode;
+ fwspec->param_count = 3;
+ fwspec->param[0] = 0;
+ fwspec->param[1] = parent_hwirq;
+ fwspec->param[2] = parent_type;
+
+ return fwspec;
+}
+
+static int visconti_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct visconti_gpio *priv;
+ struct irq_chip *irq_chip;
+ struct gpio_irq_chip *girq;
+ struct irq_domain *parent;
+ struct device_node *irq_parent;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ irq_parent = of_irq_find_parent(dev->of_node);
+ if (!irq_parent) {
+ dev_err(dev, "No IRQ parent node\n");
+ return -ENODEV;
+ }
+
+ parent = irq_find_host(irq_parent);
+ of_node_put(irq_parent);
+ if (!parent) {
+ dev_err(dev, "No IRQ parent domain\n");
+ return -ENODEV;
+ }
+
+ ret = bgpio_init(&priv->gpio_chip, dev, 4,
+ priv->base + GPIO_IDATA,
+ priv->base + GPIO_OSET,
+ priv->base + GPIO_OCLR,
+ priv->base + GPIO_DIR,
+ NULL,
+ 0);
+ if (ret) {
+ dev_err(dev, "unable to init generic GPIO\n");
+ return ret;
+ }
+
+ irq_chip = &priv->irq_chip;
+ irq_chip->name = dev_name(dev);
+ irq_chip->irq_mask = irq_chip_mask_parent;
+ irq_chip->irq_unmask = irq_chip_unmask_parent;
+ irq_chip->irq_eoi = irq_chip_eoi_parent;
+ irq_chip->irq_set_type = visconti_gpio_irq_set_type;
+ irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
+
+ girq = &priv->gpio_chip.irq;
+ girq->chip = irq_chip;
+ girq->fwnode = of_node_to_fwnode(dev->of_node);
+ girq->parent_domain = parent;
+ girq->child_to_parent_hwirq = visconti_gpio_child_to_parent_hwirq;
+ girq->populate_parent_alloc_arg = visconti_gpio_populate_parent_fwspec;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
+
+ return devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
+}
+
+static const struct of_device_id visconti_gpio_of_match[] = {
+ { .compatible = "toshiba,gpio-tmpv7708", },
+ { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, visconti_gpio_of_match);
+
+static struct platform_driver visconti_gpio_driver = {
+ .probe = visconti_gpio_probe,
+ .driver = {
+ .name = "visconti_gpio",
+ .of_match_table = of_match_ptr(visconti_gpio_of_match),
+ }
+};
+module_platform_driver(visconti_gpio_driver);
+
+MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>");
+MODULE_DESCRIPTION("Toshiba Visconti GPIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c
index 4ff146ca32fe..69713fd5485b 100644
--- a/drivers/gpio/gpio-vx855.c
+++ b/drivers/gpio/gpio-vx855.c
@@ -71,7 +71,7 @@ static inline u_int32_t gpio_o_bit(int i)
return 1 << (i + 13);
}
-/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering:
+/* Mapping between numeric GPIO ID and the actual GPIO hardware numbering:
* 0..13 GPI 0..13
* 14..26 GPO 0..12
* 27..41 GPIO 0..14
@@ -216,7 +216,7 @@ static void vx855gpio_gpio_setup(struct vx855_gpio *vg)
c->direction_output = vx855gpio_direction_output;
c->get = vx855gpio_get;
c->set = vx855gpio_set;
- c->set_config = vx855gpio_set_config,
+ c->set_config = vx855gpio_set_config;
c->dbg_show = NULL;
c->base = 0;
c->ngpio = NR_VX855_GP;
diff --git a/drivers/gpio/gpio-wcd934x.c b/drivers/gpio/gpio-wcd934x.c
new file mode 100644
index 000000000000..97e6caedf1f3
--- /dev/null
+++ b/drivers/gpio/gpio-wcd934x.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019, Linaro Limited
+
+#include <linux/module.h>
+#include <linux/gpio/driver.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+
+#define WCD_PIN_MASK(p) BIT(p)
+#define WCD_REG_DIR_CTL_OFFSET 0x42
+#define WCD_REG_VAL_CTL_OFFSET 0x43
+#define WCD934X_NPINS 5
+
+struct wcd_gpio_data {
+ struct regmap *map;
+ struct gpio_chip chip;
+};
+
+static int wcd_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
+{
+ struct wcd_gpio_data *data = gpiochip_get_data(chip);
+ unsigned int value;
+ int ret;
+
+ ret = regmap_read(data->map, WCD_REG_DIR_CTL_OFFSET, &value);
+ if (ret < 0)
+ return ret;
+
+ if (value & WCD_PIN_MASK(pin))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
+{
+ struct wcd_gpio_data *data = gpiochip_get_data(chip);
+
+ return regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET,
+ WCD_PIN_MASK(pin), 0);
+}
+
+static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
+ int val)
+{
+ struct wcd_gpio_data *data = gpiochip_get_data(chip);
+
+ regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET,
+ WCD_PIN_MASK(pin), WCD_PIN_MASK(pin));
+
+ return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET,
+ WCD_PIN_MASK(pin),
+ val ? WCD_PIN_MASK(pin) : 0);
+}
+
+static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
+{
+ struct wcd_gpio_data *data = gpiochip_get_data(chip);
+ unsigned int value;
+
+ regmap_read(data->map, WCD_REG_VAL_CTL_OFFSET, &value);
+
+ return !!(value & WCD_PIN_MASK(pin));
+}
+
+static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val)
+{
+ struct wcd_gpio_data *data = gpiochip_get_data(chip);
+
+ regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET,
+ WCD_PIN_MASK(pin), val ? WCD_PIN_MASK(pin) : 0);
+}
+
+static int wcd_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wcd_gpio_data *data;
+ struct gpio_chip *chip;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->map = dev_get_regmap(dev->parent, NULL);
+ if (!data->map) {
+ dev_err(dev, "%s: failed to get regmap\n", __func__);
+ return -EINVAL;
+ }
+
+ chip = &data->chip;
+ chip->direction_input = wcd_gpio_direction_input;
+ chip->direction_output = wcd_gpio_direction_output;
+ chip->get_direction = wcd_gpio_get_direction;
+ chip->get = wcd_gpio_get;
+ chip->set = wcd_gpio_set;
+ chip->parent = dev;
+ chip->base = -1;
+ chip->ngpio = WCD934X_NPINS;
+ chip->label = dev_name(dev);
+ chip->of_gpio_n_cells = 2;
+ chip->can_sleep = false;
+
+ return devm_gpiochip_add_data(dev, chip, data);
+}
+
+static const struct of_device_id wcd_gpio_of_match[] = {
+ { .compatible = "qcom,wcd9340-gpio" },
+ { .compatible = "qcom,wcd9341-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wcd_gpio_of_match);
+
+static struct platform_driver wcd_gpio_driver = {
+ .driver = {
+ .name = "wcd934x-gpio",
+ .of_match_table = wcd_gpio_of_match,
+ },
+ .probe = wcd_gpio_probe,
+};
+
+module_platform_driver(wcd_gpio_driver);
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c
index 444fe9e7f04a..c18b6b47384f 100644
--- a/drivers/gpio/gpio-wcove.c
+++ b/drivers/gpio/gpio-wcove.c
@@ -73,6 +73,8 @@
enum ctrl_register {
CTRL_IN,
CTRL_OUT,
+ IRQ_STATUS,
+ IRQ_MASK,
};
/*
@@ -97,46 +99,45 @@ struct wcove_gpio {
bool set_irq_mask;
};
-static inline int to_reg(int gpio, enum ctrl_register reg_type)
+static inline int to_reg(int gpio, enum ctrl_register type)
{
- unsigned int reg;
+ unsigned int reg = type == CTRL_IN ? GPIO_IN_CTRL_BASE : GPIO_OUT_CTRL_BASE;
if (gpio >= WCOVE_GPIO_NUM)
return -EOPNOTSUPP;
- if (reg_type == CTRL_IN)
- reg = GPIO_IN_CTRL_BASE + gpio;
- else
- reg = GPIO_OUT_CTRL_BASE + gpio;
-
- return reg;
+ return reg + gpio;
}
-static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio)
+static inline int to_ireg(int gpio, enum ctrl_register type, unsigned int *mask)
{
- unsigned int reg, mask;
+ unsigned int reg = type == IRQ_STATUS ? IRQ_STATUS_BASE : IRQ_MASK_BASE;
if (gpio < GROUP0_NR_IRQS) {
- reg = IRQ_MASK_BASE;
- mask = BIT(gpio % GROUP0_NR_IRQS);
+ reg += 0;
+ *mask = BIT(gpio);
} else {
- reg = IRQ_MASK_BASE + 1;
- mask = BIT((gpio - GROUP0_NR_IRQS) % GROUP1_NR_IRQS);
+ reg += 1;
+ *mask = BIT(gpio - GROUP0_NR_IRQS);
}
+ return reg;
+}
+
+static void wcove_update_irq_mask(struct wcove_gpio *wg, irq_hw_number_t gpio)
+{
+ unsigned int mask, reg = to_ireg(gpio, IRQ_MASK, &mask);
+
if (wg->set_irq_mask)
- regmap_update_bits(wg->regmap, reg, mask, mask);
+ regmap_set_bits(wg->regmap, reg, mask);
else
- regmap_update_bits(wg->regmap, reg, mask, 0);
+ regmap_clear_bits(wg->regmap, reg, mask);
}
-static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio)
+static void wcove_update_irq_ctrl(struct wcove_gpio *wg, irq_hw_number_t gpio)
{
int reg = to_reg(gpio, CTRL_IN);
- if (reg < 0)
- return;
-
regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt);
}
@@ -170,13 +171,16 @@ static int wcove_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio)
int ret, reg = to_reg(gpio, CTRL_OUT);
if (reg < 0)
- return 0;
+ return GPIO_LINE_DIRECTION_OUT;
ret = regmap_read(wg->regmap, reg, &val);
if (ret)
return ret;
- return !(val & CTLO_DIR_OUT);
+ if (val & CTLO_DIR_OUT)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio)
@@ -204,9 +208,9 @@ static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value)
return;
if (value)
- regmap_update_bits(wg->regmap, reg, 1, 1);
+ regmap_set_bits(wg->regmap, reg, 1);
else
- regmap_update_bits(wg->regmap, reg, 1, 0);
+ regmap_clear_bits(wg->regmap, reg, 1);
}
static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio,
@@ -236,8 +240,9 @@ static int wcove_irq_type(struct irq_data *data, unsigned int type)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct wcove_gpio *wg = gpiochip_get_data(chip);
+ irq_hw_number_t gpio = irqd_to_hwirq(data);
- if (data->hwirq >= WCOVE_GPIO_NUM)
+ if (gpio >= WCOVE_GPIO_NUM)
return 0;
switch (type) {
@@ -274,7 +279,7 @@ static void wcove_bus_sync_unlock(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct wcove_gpio *wg = gpiochip_get_data(chip);
- int gpio = data->hwirq;
+ irq_hw_number_t gpio = irqd_to_hwirq(data);
if (wg->update & UPDATE_IRQ_TYPE)
wcove_update_irq_ctrl(wg, gpio);
@@ -289,10 +294,13 @@ static void wcove_irq_unmask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct wcove_gpio *wg = gpiochip_get_data(chip);
+ irq_hw_number_t gpio = irqd_to_hwirq(data);
- if (data->hwirq >= WCOVE_GPIO_NUM)
+ if (gpio >= WCOVE_GPIO_NUM)
return;
+ gpiochip_enable_irq(chip, gpio);
+
wg->set_irq_mask = false;
wg->update |= UPDATE_IRQ_MASK;
}
@@ -301,27 +309,33 @@ static void wcove_irq_mask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct wcove_gpio *wg = gpiochip_get_data(chip);
+ irq_hw_number_t gpio = irqd_to_hwirq(data);
- if (data->hwirq >= WCOVE_GPIO_NUM)
+ if (gpio >= WCOVE_GPIO_NUM)
return;
wg->set_irq_mask = true;
wg->update |= UPDATE_IRQ_MASK;
+
+ gpiochip_disable_irq(chip, gpio);
}
-static struct irq_chip wcove_irqchip = {
+static const struct irq_chip wcove_irqchip = {
.name = "Whiskey Cove",
.irq_mask = wcove_irq_mask,
.irq_unmask = wcove_irq_unmask,
.irq_set_type = wcove_irq_type,
.irq_bus_lock = wcove_bus_lock,
.irq_bus_sync_unlock = wcove_bus_sync_unlock,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static irqreturn_t wcove_gpio_irq_handler(int irq, void *data)
{
struct wcove_gpio *wg = (struct wcove_gpio *)data;
- unsigned int pending, virq, gpio, mask, offset;
+ unsigned int virq, gpio;
+ unsigned long pending;
u8 p[2];
if (regmap_bulk_read(wg->regmap, IRQ_STATUS_BASE, p, 2)) {
@@ -336,15 +350,12 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data)
/* Iterate until no interrupt is pending */
while (pending) {
/* One iteration is for all pending bits */
- for_each_set_bit(gpio, (const unsigned long *)&pending,
- WCOVE_GPIO_NUM) {
- offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0;
- mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) :
- BIT(gpio);
+ for_each_set_bit(gpio, &pending, WCOVE_GPIO_NUM) {
+ unsigned int mask, reg = to_ireg(gpio, IRQ_STATUS, &mask);
+
virq = irq_find_mapping(wg->chip.irq.domain, gpio);
handle_nested_irq(virq);
- regmap_update_bits(wg->regmap, IRQ_STATUS_BASE + offset,
- mask, mask);
+ regmap_set_bits(wg->regmap, reg, mask);
}
/* Next iteration */
@@ -359,35 +370,35 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static void wcove_gpio_dbg_show(struct seq_file *s,
- struct gpio_chip *chip)
+static void wcove_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
unsigned int ctlo, ctli, irq_mask, irq_status;
struct wcove_gpio *wg = gpiochip_get_data(chip);
- int gpio, offset, group, ret = 0;
+ int gpio, mask, ret = 0;
for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) {
- group = gpio < GROUP0_NR_IRQS ? 0 : 1;
ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo);
ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli);
- ret += regmap_read(wg->regmap, IRQ_MASK_BASE + group,
- &irq_mask);
- ret += regmap_read(wg->regmap, IRQ_STATUS_BASE + group,
- &irq_status);
if (ret) {
- pr_err("Failed to read registers: ctrl out/in or irq status/mask\n");
+ dev_err(wg->dev, "Failed to read registers: CTRL out/in\n");
+ break;
+ }
+
+ ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_MASK, &mask), &irq_mask);
+ ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_STATUS, &mask), &irq_status);
+ if (ret) {
+ dev_err(wg->dev, "Failed to read registers: IRQ status/mask\n");
break;
}
- offset = gpio % 8;
seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s\n",
gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ",
ctli & 0x1 ? "hi" : "lo",
ctli & CTLI_INTCNT_NE ? "fall" : " ",
ctli & CTLI_INTCNT_PE ? "rise" : " ",
ctlo,
- irq_mask & BIT(offset) ? "mask " : "unmask",
- irq_status & BIT(offset) ? "pending" : " ");
+ irq_mask & mask ? "mask " : "unmask",
+ irq_status & mask ? "pending" : " ");
}
}
@@ -397,6 +408,7 @@ static int wcove_gpio_probe(struct platform_device *pdev)
struct wcove_gpio *wg;
int virq, ret, irq;
struct device *dev;
+ struct gpio_irq_chip *girq;
/*
* This gpio platform device is created by a mfd device (see
@@ -430,7 +442,7 @@ static int wcove_gpio_probe(struct platform_device *pdev)
wg->chip.get_direction = wcove_gpio_get_direction;
wg->chip.get = wcove_gpio_get;
wg->chip.set = wcove_gpio_set;
- wg->chip.set_config = wcove_gpio_set_config,
+ wg->chip.set_config = wcove_gpio_set_config;
wg->chip.base = -1;
wg->chip.ngpio = WCOVE_VGPIO_NUM;
wg->chip.can_sleep = true;
@@ -439,43 +451,42 @@ static int wcove_gpio_probe(struct platform_device *pdev)
wg->dev = dev;
wg->regmap = pmic->regmap;
- ret = devm_gpiochip_add_data(dev, &wg->chip, wg);
- if (ret) {
- dev_err(dev, "Failed to add gpiochip: %d\n", ret);
- return ret;
- }
-
- ret = gpiochip_irqchip_add_nested(&wg->chip, &wcove_irqchip, 0,
- handle_simple_irq, IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "Failed to add irqchip: %d\n", ret);
- return ret;
- }
-
virq = regmap_irq_get_virq(wg->regmap_irq_chip, irq);
if (virq < 0) {
dev_err(dev, "Failed to get virq by irq %d\n", irq);
return virq;
}
- ret = devm_request_threaded_irq(dev, virq, NULL,
- wcove_gpio_irq_handler, IRQF_ONESHOT, pdev->name, wg);
+ girq = &wg->chip.irq;
+ gpio_irq_chip_set_chip(girq, &wcove_irqchip);
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ girq->threaded = true;
+
+ ret = devm_request_threaded_irq(dev, virq, NULL, wcove_gpio_irq_handler,
+ IRQF_ONESHOT, pdev->name, wg);
if (ret) {
dev_err(dev, "Failed to request irq %d\n", virq);
return ret;
}
- gpiochip_set_nested_irqchip(&wg->chip, &wcove_irqchip, virq);
+ ret = devm_gpiochip_add_data(dev, &wg->chip, wg);
+ if (ret) {
+ dev_err(dev, "Failed to add gpiochip: %d\n", ret);
+ return ret;
+ }
/* Enable GPIO0 interrupts */
- ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE, GPIO_IRQ0_MASK,
- 0x00);
+ ret = regmap_clear_bits(wg->regmap, IRQ_MASK_BASE + 0, GPIO_IRQ0_MASK);
if (ret)
return ret;
/* Enable GPIO1 interrupts */
- ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK,
- 0x00);
+ ret = regmap_clear_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK);
if (ret)
return ret;
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
index a3a32a77041f..7eaf8a28638c 100644
--- a/drivers/gpio/gpio-wm831x.c
+++ b/drivers/gpio/gpio-wm831x.c
@@ -261,7 +261,8 @@ static int wm831x_gpio_probe(struct platform_device *pdev)
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = &wm831x->pdata;
struct wm831x_gpio *wm831x_gpio;
- int ret;
+
+ device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent));
wm831x_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm831x_gpio),
GFP_KERNEL);
@@ -276,20 +277,8 @@ static int wm831x_gpio_probe(struct platform_device *pdev)
wm831x_gpio->gpio_chip.base = pdata->gpio_base;
else
wm831x_gpio->gpio_chip.base = -1;
-#ifdef CONFIG_OF_GPIO
- wm831x_gpio->gpio_chip.of_node = wm831x->dev->of_node;
-#endif
-
- ret = devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip,
- wm831x_gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, wm831x_gpio);
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip, wm831x_gpio);
}
static struct platform_driver wm831x_gpio_driver = {
diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c
index 460f0a4b04bd..b1b131fb9804 100644
--- a/drivers/gpio/gpio-wm8350.c
+++ b/drivers/gpio/gpio-wm8350.c
@@ -105,7 +105,6 @@ static int wm8350_gpio_probe(struct platform_device *pdev)
struct wm8350 *wm8350 = dev_get_drvdata(pdev->dev.parent);
struct wm8350_platform_data *pdata = dev_get_platdata(wm8350->dev);
struct wm8350_gpio_data *wm8350_gpio;
- int ret;
wm8350_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8350_gpio),
GFP_KERNEL);
@@ -121,16 +120,7 @@ static int wm8350_gpio_probe(struct platform_device *pdev)
else
wm8350_gpio->gpio_chip.base = -1;
- ret = devm_gpiochip_add_data(&pdev->dev, &wm8350_gpio->gpio_chip,
- wm8350_gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, wm8350_gpio);
-
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &wm8350_gpio->gpio_chip, wm8350_gpio);
}
static struct platform_driver wm8350_gpio_driver = {
diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c
index 9af89cf7f6bc..f4a474cef32d 100644
--- a/drivers/gpio/gpio-wm8994.c
+++ b/drivers/gpio/gpio-wm8994.c
@@ -263,7 +263,6 @@ static int wm8994_gpio_probe(struct platform_device *pdev)
struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
struct wm8994_pdata *pdata = dev_get_platdata(wm8994->dev);
struct wm8994_gpio *wm8994_gpio;
- int ret;
wm8994_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8994_gpio),
GFP_KERNEL);
@@ -279,17 +278,7 @@ static int wm8994_gpio_probe(struct platform_device *pdev)
else
wm8994_gpio->gpio_chip.base = -1;
- ret = devm_gpiochip_add_data(&pdev->dev, &wm8994_gpio->gpio_chip,
- wm8994_gpio);
- if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
- ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, wm8994_gpio);
-
- return ret;
+ return devm_gpiochip_add_data(&pdev->dev, &wm8994_gpio->gpio_chip, wm8994_gpio);
}
static struct platform_driver wm8994_gpio_driver = {
diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c
index e0ef66b6a237..5078631d8014 100644
--- a/drivers/gpio/gpio-ws16c48.c
+++ b/drivers/gpio/gpio-ws16c48.c
@@ -47,7 +47,7 @@ struct ws16c48_gpio {
raw_spinlock_t lock;
unsigned long irq_mask;
unsigned long flow_mask;
- unsigned base;
+ void __iomem *base;
};
static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
@@ -56,7 +56,10 @@ static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
- return !!(ws16c48gpio->io_state[port] & mask);
+ if (ws16c48gpio->io_state[port] & mask)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -70,7 +73,7 @@ static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
ws16c48gpio->io_state[port] |= mask;
ws16c48gpio->out_state[port] &= ~mask;
- outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
+ iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
@@ -92,7 +95,7 @@ static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
ws16c48gpio->out_state[port] |= mask;
else
ws16c48gpio->out_state[port] &= ~mask;
- outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
+ iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
@@ -115,7 +118,7 @@ static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
return -EINVAL;
}
- port_state = inb(ws16c48gpio->base + port);
+ port_state = ioread8(ws16c48gpio->base + port);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
@@ -126,42 +129,19 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
- const unsigned int gpio_reg_size = 8;
- size_t i;
- const size_t num_ports = chip->ngpio / gpio_reg_size;
- unsigned int bits_offset;
- size_t word_index;
- unsigned int word_offset;
- unsigned long word_mask;
- const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long offset;
+ unsigned long gpio_mask;
+ void __iomem *port_addr;
unsigned long port_state;
/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);
- /* get bits are evaluated a gpio port register at a time */
- for (i = 0; i < num_ports; i++) {
- /* gpio offset in bits array */
- bits_offset = i * gpio_reg_size;
-
- /* word index for bits array */
- word_index = BIT_WORD(bits_offset);
-
- /* gpio offset within current word of bits array */
- word_offset = bits_offset % BITS_PER_LONG;
-
- /* mask of get bits for current gpio within current word */
- word_mask = mask[word_index] & (port_mask << word_offset);
- if (!word_mask) {
- /* no get bits in this port so skip to next one */
- continue;
- }
-
- /* read bits from current gpio port */
- port_state = inb(ws16c48gpio->base + i);
+ for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
+ port_addr = ws16c48gpio->base + offset / 8;
+ port_state = ioread8(port_addr) & gpio_mask;
- /* store acquired bits at respective bits array offset */
- bits[word_index] |= (port_state << word_offset) & word_mask;
+ bitmap_set_value8(bits, port_state, offset);
}
return 0;
@@ -186,7 +166,7 @@ static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
ws16c48gpio->out_state[port] |= mask;
else
ws16c48gpio->out_state[port] &= ~mask;
- outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
+ iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
@@ -195,39 +175,29 @@ static void ws16c48_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
- unsigned int i;
- const unsigned int gpio_reg_size = 8;
- unsigned int port;
- unsigned int iomask;
- unsigned int bitmask;
+ unsigned long offset;
+ unsigned long gpio_mask;
+ size_t index;
+ void __iomem *port_addr;
+ unsigned long bitmask;
unsigned long flags;
- /* set bits are evaluated a gpio register size at a time */
- for (i = 0; i < chip->ngpio; i += gpio_reg_size) {
- /* no more set bits in this mask word; skip to the next word */
- if (!mask[BIT_WORD(i)]) {
- i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size;
- continue;
- }
-
- port = i / gpio_reg_size;
+ for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
+ index = offset / 8;
+ port_addr = ws16c48gpio->base + index;
/* mask out GPIO configured for input */
- iomask = mask[BIT_WORD(i)] & ~ws16c48gpio->io_state[port];
- bitmask = iomask & bits[BIT_WORD(i)];
+ gpio_mask &= ~ws16c48gpio->io_state[index];
+ bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
/* update output state data and set device gpio register */
- ws16c48gpio->out_state[port] &= ~iomask;
- ws16c48gpio->out_state[port] |= bitmask;
- outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
+ ws16c48gpio->out_state[index] &= ~gpio_mask;
+ ws16c48gpio->out_state[index] |= bitmask;
+ iowrite8(ws16c48gpio->out_state[index], port_addr);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
-
- /* prepare for next gpio register set */
- mask[BIT_WORD(i)] >>= gpio_reg_size;
- bits[BIT_WORD(i)] >>= gpio_reg_size;
}
}
@@ -249,10 +219,10 @@ static void ws16c48_irq_ack(struct irq_data *data)
port_state = ws16c48gpio->irq_mask >> (8*port);
- outb(0x80, ws16c48gpio->base + 7);
- outb(port_state & ~mask, ws16c48gpio->base + 8 + port);
- outb(port_state | mask, ws16c48gpio->base + 8 + port);
- outb(0xC0, ws16c48gpio->base + 7);
+ iowrite8(0x80, ws16c48gpio->base + 7);
+ iowrite8(port_state & ~mask, ws16c48gpio->base + 8 + port);
+ iowrite8(port_state | mask, ws16c48gpio->base + 8 + port);
+ iowrite8(0xC0, ws16c48gpio->base + 7);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
@@ -274,9 +244,9 @@ static void ws16c48_irq_mask(struct irq_data *data)
ws16c48gpio->irq_mask &= ~mask;
- outb(0x80, ws16c48gpio->base + 7);
- outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
- outb(0xC0, ws16c48gpio->base + 7);
+ iowrite8(0x80, ws16c48gpio->base + 7);
+ iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
+ iowrite8(0xC0, ws16c48gpio->base + 7);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
@@ -298,9 +268,9 @@ static void ws16c48_irq_unmask(struct irq_data *data)
ws16c48gpio->irq_mask |= mask;
- outb(0x80, ws16c48gpio->base + 7);
- outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
- outb(0xC0, ws16c48gpio->base + 7);
+ iowrite8(0x80, ws16c48gpio->base + 7);
+ iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
+ iowrite8(0xC0, ws16c48gpio->base + 7);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
@@ -334,9 +304,9 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
return -EINVAL;
}
- outb(0x40, ws16c48gpio->base + 7);
- outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port);
- outb(0xC0, ws16c48gpio->base + 7);
+ iowrite8(0x40, ws16c48gpio->base + 7);
+ iowrite8(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port);
+ iowrite8(0xC0, ws16c48gpio->base + 7);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
@@ -360,20 +330,20 @@ static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
unsigned long int_id;
unsigned long gpio;
- int_pending = inb(ws16c48gpio->base + 6) & 0x7;
+ int_pending = ioread8(ws16c48gpio->base + 6) & 0x7;
if (!int_pending)
return IRQ_NONE;
/* loop until all pending interrupts are handled */
do {
for_each_set_bit(port, &int_pending, 3) {
- int_id = inb(ws16c48gpio->base + 8 + port);
+ int_id = ioread8(ws16c48gpio->base + 8 + port);
for_each_set_bit(gpio, &int_id, 8)
- generic_handle_irq(irq_find_mapping(
- chip->irq.domain, gpio + 8*port));
+ generic_handle_domain_irq(chip->irq.domain,
+ gpio + 8*port);
}
- int_pending = inb(ws16c48gpio->base + 6) & 0x7;
+ int_pending = ioread8(ws16c48gpio->base + 6) & 0x7;
} while (int_pending);
return IRQ_HANDLED;
@@ -395,10 +365,25 @@ static const char *ws16c48_names[WS16C48_NGPIO] = {
"Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7"
};
+static int ws16c48_irq_init_hw(struct gpio_chip *gc)
+{
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc);
+
+ /* Disable IRQ by default */
+ iowrite8(0x80, ws16c48gpio->base + 7);
+ iowrite8(0, ws16c48gpio->base + 8);
+ iowrite8(0, ws16c48gpio->base + 9);
+ iowrite8(0, ws16c48gpio->base + 10);
+ iowrite8(0xC0, ws16c48gpio->base + 7);
+
+ return 0;
+}
+
static int ws16c48_probe(struct device *dev, unsigned int id)
{
struct ws16c48_gpio *ws16c48gpio;
const char *const name = dev_name(dev);
+ struct gpio_irq_chip *girq;
int err;
ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
@@ -411,6 +396,10 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
+ ws16c48gpio->base = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
+ if (!ws16c48gpio->base)
+ return -ENOMEM;
+
ws16c48gpio->chip.label = name;
ws16c48gpio->chip.parent = dev;
ws16c48gpio->chip.owner = THIS_MODULE;
@@ -424,7 +413,16 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple;
ws16c48gpio->chip.set = ws16c48_gpio_set;
ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
- ws16c48gpio->base = base[id];
+
+ girq = &ws16c48gpio->chip.irq;
+ girq->chip = &ws16c48_irqchip;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_edge_irq;
+ girq->init_hw = ws16c48_irq_init_hw;
raw_spin_lock_init(&ws16c48gpio->lock);
@@ -434,20 +432,6 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
return err;
}
- /* Disable IRQ by default */
- outb(0x80, base[id] + 7);
- outb(0, base[id] + 8);
- outb(0, base[id] + 9);
- outb(0, base[id] + 10);
- outb(0xC0, base[id] + 7);
-
- err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0,
- handle_edge_irq, IRQ_TYPE_NONE);
- if (err) {
- dev_err(dev, "Could not add irqchip (%d)\n", err);
- return err;
- }
-
err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED,
name, ws16c48gpio);
if (err) {
diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c
index 38c01912c7b2..a809609ee957 100644
--- a/drivers/gpio/gpio-xgene-sb.c
+++ b/drivers/gpio/gpio-xgene-sb.c
@@ -10,12 +10,13 @@
#include <linux/module.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/of_gpio.h>
#include <linux/gpio/driver.h>
#include <linux/acpi.h>
#include "gpiolib.h"
+#include "gpiolib-acpi.h"
/* Common property names */
#define XGENE_NIRQ_PROPERTY "apm,nr-irqs"
@@ -121,7 +122,7 @@ static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio)
fwspec.fwnode = gc->parent->fwnode;
fwspec.param_count = 2;
fwspec.param[0] = GPIO_TO_HWIRQ(priv, gpio);
- fwspec.param[1] = IRQ_TYPE_NONE;
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
return irq_create_fwspec_mapping(&fwspec);
}
@@ -289,10 +290,8 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
- if (priv->nirq > 0) {
- /* Register interrupt handlers for gpio signaled acpi events */
- acpi_gpiochip_request_interrupts(&priv->gc);
- }
+ /* Register interrupt handlers for GPIO signaled ACPI Events */
+ acpi_gpiochip_request_interrupts(&priv->gc);
return ret;
}
@@ -301,9 +300,7 @@ static int xgene_gpio_sb_remove(struct platform_device *pdev)
{
struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);
- if (priv->nirq > 0) {
- acpi_gpiochip_free_interrupts(&priv->gc);
- }
+ acpi_gpiochip_free_interrupts(&priv->gc);
irq_domain_remove(priv->irq_domain);
diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c
index 2918363884de..fb4b0c67aeef 100644
--- a/drivers/gpio/gpio-xgene.c
+++ b/drivers/gpio/gpio-xgene.c
@@ -80,7 +80,10 @@ static int xgene_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
bit_offset = GPIO_BIT_OFFSET(offset);
- return !!(ioread32(chip->base + bank_offset) & BIT(bit_offset));
+ if (ioread32(chip->base + bank_offset) & BIT(bit_offset))
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int xgene_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
@@ -155,28 +158,15 @@ static SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume);
static int xgene_gpio_probe(struct platform_device *pdev)
{
- struct resource *res;
struct xgene_gpio *gpio;
- int err = 0;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
- if (!gpio) {
- err = -ENOMEM;
- goto err;
- }
+ if (!gpio)
+ return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -EINVAL;
- goto err;
- }
-
- gpio->base = devm_ioremap_nocache(&pdev->dev, res->start,
- resource_size(res));
- if (!gpio->base) {
- err = -ENOMEM;
- goto err;
- }
+ gpio->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(gpio->base))
+ return PTR_ERR(gpio->base);
gpio->chip.ngpio = XGENE_MAX_GPIOS;
@@ -192,18 +182,7 @@ static int xgene_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, gpio);
- err = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
- if (err) {
- dev_err(&pdev->dev,
- "failed to register gpiochip.\n");
- goto err;
- }
-
- dev_info(&pdev->dev, "X-Gene GPIO driver registered.\n");
- return 0;
-err:
- dev_err(&pdev->dev, "X-Gene GPIO driver registration failed.\n");
- return err;
+ return devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
}
static const struct of_device_id xgene_gpio_of_match[] = {
diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c
new file mode 100644
index 000000000000..43ca52fa6f9a
--- /dev/null
+++ b/drivers/gpio/gpio-xgs-iproc.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017 Broadcom
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#define IPROC_CCA_INT_F_GPIOINT BIT(0)
+#define IPROC_CCA_INT_STS 0x20
+#define IPROC_CCA_INT_MASK 0x24
+
+#define IPROC_GPIO_CCA_DIN 0x0
+#define IPROC_GPIO_CCA_DOUT 0x4
+#define IPROC_GPIO_CCA_OUT_EN 0x8
+#define IPROC_GPIO_CCA_INT_LEVEL 0x10
+#define IPROC_GPIO_CCA_INT_LEVEL_MASK 0x14
+#define IPROC_GPIO_CCA_INT_EVENT 0x18
+#define IPROC_GPIO_CCA_INT_EVENT_MASK 0x1C
+#define IPROC_GPIO_CCA_INT_EDGE 0x24
+
+struct iproc_gpio_chip {
+ struct irq_chip irqchip;
+ struct gpio_chip gc;
+ spinlock_t lock;
+ struct device *dev;
+ void __iomem *base;
+ void __iomem *intr;
+};
+
+static inline struct iproc_gpio_chip *
+to_iproc_gpio(struct gpio_chip *gc)
+{
+ return container_of(gc, struct iproc_gpio_chip, gc);
+}
+
+static void iproc_gpio_irq_ack(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct iproc_gpio_chip *chip = to_iproc_gpio(gc);
+ int pin = d->hwirq;
+ unsigned long flags;
+ u32 irq = d->irq;
+ u32 irq_type, event_status = 0;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ irq_type = irq_get_trigger_type(irq);
+ if (irq_type & IRQ_TYPE_EDGE_BOTH) {
+ event_status |= BIT(pin);
+ writel_relaxed(event_status,
+ chip->base + IPROC_GPIO_CCA_INT_EVENT);
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void iproc_gpio_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct iproc_gpio_chip *chip = to_iproc_gpio(gc);
+ int pin = d->hwirq;
+ unsigned long flags;
+ u32 irq = d->irq;
+ u32 int_mask, irq_type, event_mask;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ irq_type = irq_get_trigger_type(irq);
+ event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK);
+ int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK);
+
+ if (irq_type & IRQ_TYPE_EDGE_BOTH) {
+ event_mask |= 1 << pin;
+ writel_relaxed(event_mask,
+ chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK);
+ } else {
+ int_mask |= 1 << pin;
+ writel_relaxed(int_mask,
+ chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK);
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void iproc_gpio_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct iproc_gpio_chip *chip = to_iproc_gpio(gc);
+ int pin = d->hwirq;
+ unsigned long flags;
+ u32 irq = d->irq;
+ u32 irq_type, int_mask, event_mask;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ irq_type = irq_get_trigger_type(irq);
+ event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK);
+ int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK);
+
+ if (irq_type & IRQ_TYPE_EDGE_BOTH) {
+ event_mask &= ~BIT(pin);
+ writel_relaxed(event_mask,
+ chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK);
+ } else {
+ int_mask &= ~BIT(pin);
+ writel_relaxed(int_mask,
+ chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK);
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int iproc_gpio_irq_set_type(struct irq_data *d, u32 type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct iproc_gpio_chip *chip = to_iproc_gpio(gc);
+ int pin = d->hwirq;
+ unsigned long flags;
+ u32 irq = d->irq;
+ u32 event_pol, int_pol;
+ int ret = 0;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE);
+ event_pol &= ~BIT(pin);
+ writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE);
+ event_pol |= BIT(pin);
+ writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL);
+ int_pol &= ~BIT(pin);
+ writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL);
+ int_pol |= BIT(pin);
+ writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL);
+ break;
+ default:
+ /* should not come here */
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (type & IRQ_TYPE_LEVEL_MASK)
+ irq_set_handler_locked(irq_get_irq_data(irq), handle_level_irq);
+ else if (type & IRQ_TYPE_EDGE_BOTH)
+ irq_set_handler_locked(irq_get_irq_data(irq), handle_edge_irq);
+
+out_unlock:
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return ret;
+}
+
+static irqreturn_t iproc_gpio_irq_handler(int irq, void *data)
+{
+ struct gpio_chip *gc = (struct gpio_chip *)data;
+ struct iproc_gpio_chip *chip = to_iproc_gpio(gc);
+ int bit;
+ unsigned long int_bits = 0;
+ u32 int_status;
+
+ /* go through the entire GPIOs and handle all interrupts */
+ int_status = readl_relaxed(chip->intr + IPROC_CCA_INT_STS);
+ if (int_status & IPROC_CCA_INT_F_GPIOINT) {
+ u32 event, level;
+
+ /* Get level and edge interrupts */
+ event =
+ readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK);
+ event &= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT);
+ level = readl_relaxed(chip->base + IPROC_GPIO_CCA_DIN);
+ level ^= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL);
+ level &=
+ readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK);
+ int_bits = level | event;
+
+ for_each_set_bit(bit, &int_bits, gc->ngpio)
+ generic_handle_domain_irq(gc->irq.domain, bit);
+ }
+
+ return int_bits ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int iproc_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *dn = pdev->dev.of_node;
+ struct iproc_gpio_chip *chip;
+ u32 num_gpios;
+ int irq, ret;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = dev;
+ platform_set_drvdata(pdev, chip);
+ spin_lock_init(&chip->lock);
+
+ chip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(chip->base))
+ return PTR_ERR(chip->base);
+
+ ret = bgpio_init(&chip->gc, dev, 4,
+ chip->base + IPROC_GPIO_CCA_DIN,
+ chip->base + IPROC_GPIO_CCA_DOUT,
+ NULL,
+ chip->base + IPROC_GPIO_CCA_OUT_EN,
+ NULL,
+ 0);
+ if (ret) {
+ dev_err(dev, "unable to init GPIO chip\n");
+ return ret;
+ }
+
+ chip->gc.label = dev_name(dev);
+ if (!of_property_read_u32(dn, "ngpios", &num_gpios))
+ chip->gc.ngpio = num_gpios;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ struct gpio_irq_chip *girq;
+ struct irq_chip *irqc;
+ u32 val;
+
+ irqc = &chip->irqchip;
+ irqc->name = dev_name(dev);
+ irqc->irq_ack = iproc_gpio_irq_ack;
+ irqc->irq_mask = iproc_gpio_irq_mask;
+ irqc->irq_unmask = iproc_gpio_irq_unmask;
+ irqc->irq_set_type = iproc_gpio_irq_set_type;
+
+ chip->intr = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(chip->intr))
+ return PTR_ERR(chip->intr);
+
+ /* Enable GPIO interrupts for CCA GPIO */
+ val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK);
+ val |= IPROC_CCA_INT_F_GPIOINT;
+ writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK);
+
+ /*
+ * Directly request the irq here instead of passing
+ * a flow-handler because the irq is shared.
+ */
+ ret = devm_request_irq(dev, irq, iproc_gpio_irq_handler,
+ IRQF_SHARED, chip->gc.label, &chip->gc);
+ if (ret) {
+ dev_err(dev, "Fail to request IRQ%d: %d\n", irq, ret);
+ return ret;
+ }
+
+ girq = &chip->gc.irq;
+ girq->chip = irqc;
+ /* This will let us handle the parent IRQ in the driver */
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ }
+
+ ret = devm_gpiochip_add_data(dev, &chip->gc, chip);
+ if (ret) {
+ dev_err(dev, "unable to add GPIO chip\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int iproc_gpio_remove(struct platform_device *pdev)
+{
+ struct iproc_gpio_chip *chip;
+
+ chip = platform_get_drvdata(pdev);
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->intr) {
+ u32 val;
+
+ val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK);
+ val &= ~IPROC_CCA_INT_F_GPIOINT;
+ writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id bcm_iproc_gpio_of_match[] = {
+ { .compatible = "brcm,iproc-gpio-cca" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, bcm_iproc_gpio_of_match);
+
+static struct platform_driver bcm_iproc_gpio_driver = {
+ .driver = {
+ .name = "iproc-xgs-gpio",
+ .of_match_table = bcm_iproc_gpio_of_match,
+ },
+ .probe = iproc_gpio_probe,
+ .remove = iproc_gpio_remove,
+};
+
+module_platform_driver(bcm_iproc_gpio_driver);
+
+MODULE_DESCRIPTION("XGS IPROC GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index a9748b5198e6..b6d3a57e27ed 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -5,21 +5,32 @@
* Copyright 2008 - 2013 Xilinx, Inc.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
-#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
-#include <linux/io.h>
-#include <linux/gpio/driver.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
/* Register Offset Definitions */
#define XGPIO_DATA_OFFSET (0x0) /* Data register */
#define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */
-#define XGPIO_CHANNEL_OFFSET 0x8
+#define XGPIO_CHANNEL0_OFFSET 0x0
+#define XGPIO_CHANNEL1_OFFSET 0x8
+
+#define XGPIO_GIER_OFFSET 0x11c /* Global Interrupt Enable */
+#define XGPIO_GIER_IE BIT(31)
+#define XGPIO_IPISR_OFFSET 0x120 /* IP Interrupt Status */
+#define XGPIO_IPIER_OFFSET 0x128 /* IP Interrupt Enable */
/* Read/Write access to the GPIO registers */
#if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_X86)
@@ -34,42 +45,101 @@
* struct xgpio_instance - Stores information about GPIO device
* @gc: GPIO chip
* @regs: register block
- * @gpio_width: GPIO width for every channel
- * @gpio_state: GPIO state shadow register
- * @gpio_dir: GPIO direction shadow register
+ * @hw_map: GPIO pin mapping on hardware side
+ * @sw_map: GPIO pin mapping on software side
+ * @state: GPIO write state shadow register
+ * @last_irq_read: GPIO read state register from last interrupt
+ * @dir: GPIO direction shadow register
* @gpio_lock: Lock used for synchronization
+ * @irq: IRQ used by GPIO device
+ * @irqchip: IRQ chip
+ * @enable: GPIO IRQ enable/disable bitfield
+ * @rising_edge: GPIO IRQ rising edge enable/disable bitfield
+ * @falling_edge: GPIO IRQ falling edge enable/disable bitfield
+ * @clk: clock resource for this driver
*/
struct xgpio_instance {
struct gpio_chip gc;
void __iomem *regs;
- unsigned int gpio_width[2];
- u32 gpio_state[2];
- u32 gpio_dir[2];
- spinlock_t gpio_lock[2];
+ DECLARE_BITMAP(hw_map, 64);
+ DECLARE_BITMAP(sw_map, 64);
+ DECLARE_BITMAP(state, 64);
+ DECLARE_BITMAP(last_irq_read, 64);
+ DECLARE_BITMAP(dir, 64);
+ spinlock_t gpio_lock; /* For serializing operations */
+ int irq;
+ struct irq_chip irqchip;
+ DECLARE_BITMAP(enable, 64);
+ DECLARE_BITMAP(rising_edge, 64);
+ DECLARE_BITMAP(falling_edge, 64);
+ struct clk *clk;
};
-static inline int xgpio_index(struct xgpio_instance *chip, int gpio)
+static inline int xgpio_from_bit(struct xgpio_instance *chip, int bit)
{
- if (gpio >= chip->gpio_width[0])
- return 1;
+ return bitmap_bitremap(bit, chip->hw_map, chip->sw_map, 64);
+}
- return 0;
+static inline int xgpio_to_bit(struct xgpio_instance *chip, int gpio)
+{
+ return bitmap_bitremap(gpio, chip->sw_map, chip->hw_map, 64);
}
-static inline int xgpio_regoffset(struct xgpio_instance *chip, int gpio)
+static inline u32 xgpio_get_value32(const unsigned long *map, int bit)
{
- if (xgpio_index(chip, gpio))
- return XGPIO_CHANNEL_OFFSET;
+ const size_t index = BIT_WORD(bit);
+ const unsigned long offset = (bit % BITS_PER_LONG) & BIT(5);
- return 0;
+ return (map[index] >> offset) & 0xFFFFFFFFul;
+}
+
+static inline void xgpio_set_value32(unsigned long *map, int bit, u32 v)
+{
+ const size_t index = BIT_WORD(bit);
+ const unsigned long offset = (bit % BITS_PER_LONG) & BIT(5);
+
+ map[index] &= ~(0xFFFFFFFFul << offset);
+ map[index] |= v << offset;
}
-static inline int xgpio_offset(struct xgpio_instance *chip, int gpio)
+static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch)
{
- if (xgpio_index(chip, gpio))
- return gpio - chip->gpio_width[0];
+ switch (ch) {
+ case 0:
+ return XGPIO_CHANNEL0_OFFSET;
+ case 1:
+ return XGPIO_CHANNEL1_OFFSET;
+ default:
+ return -EINVAL;
+ }
+}
- return gpio;
+static void xgpio_read_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a)
+{
+ void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32);
+ xgpio_set_value32(a, bit, xgpio_readreg(addr));
+}
+
+static void xgpio_write_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a)
+{
+ void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32);
+ xgpio_writereg(addr, xgpio_get_value32(a, bit));
+}
+
+static void xgpio_read_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a)
+{
+ int bit, lastbit = xgpio_to_bit(chip, chip->gc.ngpio - 1);
+
+ for (bit = 0; bit <= lastbit ; bit += 32)
+ xgpio_read_ch(chip, reg, bit, a);
+}
+
+static void xgpio_write_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a)
+{
+ int bit, lastbit = xgpio_to_bit(chip, chip->gc.ngpio - 1);
+
+ for (bit = 0; bit <= lastbit ; bit += 32)
+ xgpio_write_ch(chip, reg, bit, a);
}
/**
@@ -86,12 +156,12 @@ static inline int xgpio_offset(struct xgpio_instance *chip, int gpio)
static int xgpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct xgpio_instance *chip = gpiochip_get_data(gc);
- u32 val;
+ int bit = xgpio_to_bit(chip, gpio);
+ DECLARE_BITMAP(state, 64);
- val = xgpio_readreg(chip->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, gpio));
+ xgpio_read_ch(chip, XGPIO_DATA_OFFSET, bit, state);
- return !!(val & BIT(xgpio_offset(chip, gpio)));
+ return test_bit(bit, state);
}
/**
@@ -107,21 +177,16 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
unsigned long flags;
struct xgpio_instance *chip = gpiochip_get_data(gc);
- int index = xgpio_index(chip, gpio);
- int offset = xgpio_offset(chip, gpio);
+ int bit = xgpio_to_bit(chip, gpio);
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
+ spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write to GPIO signal and set its direction to output */
- if (val)
- chip->gpio_state[index] |= BIT(offset);
- else
- chip->gpio_state[index] &= ~BIT(offset);
+ __assign_bit(bit, chip->state, val);
- xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
+ xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
@@ -136,38 +201,24 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
+ DECLARE_BITMAP(hw_mask, 64);
+ DECLARE_BITMAP(hw_bits, 64);
+ DECLARE_BITMAP(state, 64);
unsigned long flags;
struct xgpio_instance *chip = gpiochip_get_data(gc);
- int index = xgpio_index(chip, 0);
- int offset, i;
-
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
-
- /* Write to GPIO signals */
- for (i = 0; i < gc->ngpio; i++) {
- if (*mask == 0)
- break;
- if (index != xgpio_index(chip, i)) {
- xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, i),
- chip->gpio_state[index]);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
- index = xgpio_index(chip, i);
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
- }
- if (__test_and_clear_bit(i, mask)) {
- offset = xgpio_offset(chip, i);
- if (test_bit(i, bits))
- chip->gpio_state[index] |= BIT(offset);
- else
- chip->gpio_state[index] &= ~BIT(offset);
- }
- }
- xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, i), chip->gpio_state[index]);
+ bitmap_remap(hw_mask, mask, chip->sw_map, chip->hw_map, 64);
+ bitmap_remap(hw_bits, bits, chip->sw_map, chip->hw_map, 64);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+
+ bitmap_replace(state, chip->state, hw_bits, hw_mask, 64);
+
+ xgpio_write_ch_all(chip, XGPIO_DATA_OFFSET, state);
+
+ bitmap_copy(chip->state, state, 64);
+
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
@@ -183,17 +234,15 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
unsigned long flags;
struct xgpio_instance *chip = gpiochip_get_data(gc);
- int index = xgpio_index(chip, gpio);
- int offset = xgpio_offset(chip, gpio);
+ int bit = xgpio_to_bit(chip, gpio);
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
+ spin_lock_irqsave(&chip->gpio_lock, flags);
/* Set the GPIO bit in shadow register and set direction as input */
- chip->gpio_dir[index] |= BIT(offset);
- xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET +
- xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
+ __set_bit(bit, chip->dir);
+ xgpio_write_ch(chip, XGPIO_TRI_OFFSET, bit, chip->dir);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
return 0;
}
@@ -214,25 +263,19 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{
unsigned long flags;
struct xgpio_instance *chip = gpiochip_get_data(gc);
- int index = xgpio_index(chip, gpio);
- int offset = xgpio_offset(chip, gpio);
+ int bit = xgpio_to_bit(chip, gpio);
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
+ spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write state of GPIO signal */
- if (val)
- chip->gpio_state[index] |= BIT(offset);
- else
- chip->gpio_state[index] &= ~BIT(offset);
- xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
+ __assign_bit(bit, chip->state, val);
+ xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state);
/* Clear the GPIO bit in shadow register and set direction as output */
- chip->gpio_dir[index] &= ~BIT(offset);
- xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET +
- xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
+ __clear_bit(bit, chip->dir);
+ xgpio_write_ch(chip, XGPIO_TRI_OFFSET, bit, chip->dir);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
return 0;
}
@@ -243,20 +286,264 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
*/
static void xgpio_save_regs(struct xgpio_instance *chip)
{
- xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET, chip->gpio_state[0]);
- xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET, chip->gpio_dir[0]);
+ xgpio_write_ch_all(chip, XGPIO_DATA_OFFSET, chip->state);
+ xgpio_write_ch_all(chip, XGPIO_TRI_OFFSET, chip->dir);
+}
+
+static int xgpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ int ret;
+
+ ret = pm_runtime_get_sync(chip->parent);
+ /*
+ * If the device is already active pm_runtime_get() will return 1 on
+ * success, but gpio_request still needs to return 0.
+ */
+ return ret < 0 ? ret : 0;
+}
+
+static void xgpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ pm_runtime_put(chip->parent);
+}
+
+static int __maybe_unused xgpio_suspend(struct device *dev)
+{
+ struct xgpio_instance *gpio = dev_get_drvdata(dev);
+ struct irq_data *data = irq_get_irq_data(gpio->irq);
+
+ if (!data) {
+ dev_dbg(dev, "IRQ not connected\n");
+ return pm_runtime_force_suspend(dev);
+ }
+
+ if (!irqd_is_wakeup_set(data))
+ return pm_runtime_force_suspend(dev);
+
+ return 0;
+}
+
+/**
+ * xgpio_remove - Remove method for the GPIO device.
+ * @pdev: pointer to the platform device
+ *
+ * This function remove gpiochips and frees all the allocated resources.
+ *
+ * Return: 0 always
+ */
+static int xgpio_remove(struct platform_device *pdev)
+{
+ struct xgpio_instance *gpio = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ clk_disable_unprepare(gpio->clk);
+
+ return 0;
+}
+
+/**
+ * xgpio_irq_ack - Acknowledge a child GPIO interrupt.
+ * @irq_data: per IRQ and chip data passed down to chip functions
+ * This currently does nothing, but irq_ack is unconditionally called by
+ * handle_edge_irq and therefore must be defined.
+ */
+static void xgpio_irq_ack(struct irq_data *irq_data)
+{
+}
+
+static int __maybe_unused xgpio_resume(struct device *dev)
+{
+ struct xgpio_instance *gpio = dev_get_drvdata(dev);
+ struct irq_data *data = irq_get_irq_data(gpio->irq);
+
+ if (!data) {
+ dev_dbg(dev, "IRQ not connected\n");
+ return pm_runtime_force_resume(dev);
+ }
+
+ if (!irqd_is_wakeup_set(data))
+ return pm_runtime_force_resume(dev);
+
+ return 0;
+}
+
+static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
+{
+ struct xgpio_instance *gpio = dev_get_drvdata(dev);
+
+ clk_disable(gpio->clk);
+
+ return 0;
+}
+
+static int __maybe_unused xgpio_runtime_resume(struct device *dev)
+{
+ struct xgpio_instance *gpio = dev_get_drvdata(dev);
+
+ return clk_enable(gpio->clk);
+}
+
+static const struct dev_pm_ops xgpio_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume)
+ SET_RUNTIME_PM_OPS(xgpio_runtime_suspend,
+ xgpio_runtime_resume, NULL)
+};
+
+/**
+ * xgpio_irq_mask - Write the specified signal of the GPIO device.
+ * @irq_data: per IRQ and chip data passed down to chip functions
+ */
+static void xgpio_irq_mask(struct irq_data *irq_data)
+{
+ unsigned long flags;
+ struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
+ int irq_offset = irqd_to_hwirq(irq_data);
+ int bit = xgpio_to_bit(chip, irq_offset);
+ u32 mask = BIT(bit / 32), temp;
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
- if (!chip->gpio_width[1])
- return;
+ __clear_bit(bit, chip->enable);
- xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET + XGPIO_CHANNEL_OFFSET,
- chip->gpio_state[1]);
- xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET + XGPIO_CHANNEL_OFFSET,
- chip->gpio_dir[1]);
+ if (xgpio_get_value32(chip->enable, bit) == 0) {
+ /* Disable per channel interrupt */
+ temp = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET);
+ temp &= ~mask;
+ xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, temp);
+ }
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
- * xgpio_of_probe - Probe method for the GPIO device.
+ * xgpio_irq_unmask - Write the specified signal of the GPIO device.
+ * @irq_data: per IRQ and chip data passed down to chip functions
+ */
+static void xgpio_irq_unmask(struct irq_data *irq_data)
+{
+ unsigned long flags;
+ struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
+ int irq_offset = irqd_to_hwirq(irq_data);
+ int bit = xgpio_to_bit(chip, irq_offset);
+ u32 old_enable = xgpio_get_value32(chip->enable, bit);
+ u32 mask = BIT(bit / 32), val;
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+
+ __set_bit(bit, chip->enable);
+
+ if (old_enable == 0) {
+ /* Clear any existing per-channel interrupts */
+ val = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
+ val &= mask;
+ xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, val);
+
+ /* Update GPIO IRQ read data before enabling interrupt*/
+ xgpio_read_ch(chip, XGPIO_DATA_OFFSET, bit, chip->last_irq_read);
+
+ /* Enable per channel interrupt */
+ val = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET);
+ val |= mask;
+ xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, val);
+ }
+
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
+ * xgpio_set_irq_type - Write the specified signal of the GPIO device.
+ * @irq_data: Per IRQ and chip data passed down to chip functions
+ * @type: Interrupt type that is to be set for the gpio pin
+ *
+ * Return:
+ * 0 if interrupt type is supported otherwise -EINVAL
+ */
+static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
+{
+ struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
+ int irq_offset = irqd_to_hwirq(irq_data);
+ int bit = xgpio_to_bit(chip, irq_offset);
+
+ /*
+ * The Xilinx GPIO hardware provides a single interrupt status
+ * indication for any state change in a given GPIO channel (bank).
+ * Therefore, only rising edge or falling edge triggers are
+ * supported.
+ */
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_BOTH:
+ __set_bit(bit, chip->rising_edge);
+ __set_bit(bit, chip->falling_edge);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ __set_bit(bit, chip->rising_edge);
+ __clear_bit(bit, chip->falling_edge);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ __clear_bit(bit, chip->rising_edge);
+ __set_bit(bit, chip->falling_edge);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ irq_set_handler_locked(irq_data, handle_edge_irq);
+ return 0;
+}
+
+/**
+ * xgpio_irqhandler - Gpio interrupt service routine
+ * @desc: Pointer to interrupt description
+ */
+static void xgpio_irqhandler(struct irq_desc *desc)
+{
+ struct xgpio_instance *chip = irq_desc_get_handler_data(desc);
+ struct gpio_chip *gc = &chip->gc;
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+ DECLARE_BITMAP(rising, 64);
+ DECLARE_BITMAP(falling, 64);
+ DECLARE_BITMAP(all, 64);
+ int irq_offset;
+ u32 status;
+ u32 bit;
+
+ status = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
+ xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, status);
+
+ chained_irq_enter(irqchip, desc);
+
+ spin_lock(&chip->gpio_lock);
+
+ xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, all);
+
+ bitmap_complement(rising, chip->last_irq_read, 64);
+ bitmap_and(rising, rising, all, 64);
+ bitmap_and(rising, rising, chip->enable, 64);
+ bitmap_and(rising, rising, chip->rising_edge, 64);
+
+ bitmap_complement(falling, all, 64);
+ bitmap_and(falling, falling, chip->last_irq_read, 64);
+ bitmap_and(falling, falling, chip->enable, 64);
+ bitmap_and(falling, falling, chip->falling_edge, 64);
+
+ bitmap_copy(chip->last_irq_read, all, 64);
+ bitmap_or(all, rising, falling, 64);
+
+ spin_unlock(&chip->gpio_lock);
+
+ dev_dbg(gc->parent, "IRQ rising %*pb falling %*pb\n", 64, rising, 64, falling);
+
+ for_each_set_bit(bit, all, 64) {
+ irq_offset = xgpio_from_bit(chip, bit);
+ generic_handle_domain_irq(gc->irq.domain, irq_offset);
+ }
+
+ chained_irq_exit(irqchip, desc);
+}
+
+/**
+ * xgpio_probe - Probe method for the GPIO device.
* @pdev: pointer to the platform device
*
* Return:
@@ -268,7 +555,13 @@ static int xgpio_probe(struct platform_device *pdev)
struct xgpio_instance *chip;
int status = 0;
struct device_node *np = pdev->dev.of_node;
- u32 is_dual;
+ u32 is_dual = 0;
+ u32 cells = 2;
+ u32 width[2];
+ u32 state[2];
+ u32 dir[2];
+ struct gpio_irq_chip *girq;
+ u32 temp;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -276,53 +569,70 @@ static int xgpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, chip);
+ /* First, check if the device is dual-channel */
+ of_property_read_u32(np, "xlnx,is-dual", &is_dual);
+
+ /* Setup defaults */
+ memset32(width, 0, ARRAY_SIZE(width));
+ memset32(state, 0, ARRAY_SIZE(state));
+ memset32(dir, 0xFFFFFFFF, ARRAY_SIZE(dir));
+
/* Update GPIO state shadow register with default value */
- of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state[0]);
+ of_property_read_u32(np, "xlnx,dout-default", &state[0]);
+ of_property_read_u32(np, "xlnx,dout-default-2", &state[1]);
+
+ bitmap_from_arr32(chip->state, state, 64);
/* Update GPIO direction shadow register with default value */
- if (of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir[0]))
- chip->gpio_dir[0] = 0xFFFFFFFF;
+ of_property_read_u32(np, "xlnx,tri-default", &dir[0]);
+ of_property_read_u32(np, "xlnx,tri-default-2", &dir[1]);
+
+ bitmap_from_arr32(chip->dir, dir, 64);
+
+ /* Update cells with gpio-cells value */
+ if (of_property_read_u32(np, "#gpio-cells", &cells))
+ dev_dbg(&pdev->dev, "Missing gpio-cells property\n");
+
+ if (cells != 2) {
+ dev_err(&pdev->dev, "#gpio-cells mismatch\n");
+ return -EINVAL;
+ }
/*
* Check device node and parent device node for device width
* and assume default width of 32
*/
- if (of_property_read_u32(np, "xlnx,gpio-width", &chip->gpio_width[0]))
- chip->gpio_width[0] = 32;
+ if (of_property_read_u32(np, "xlnx,gpio-width", &width[0]))
+ width[0] = 32;
- spin_lock_init(&chip->gpio_lock[0]);
+ if (width[0] > 32)
+ return -EINVAL;
- if (of_property_read_u32(np, "xlnx,is-dual", &is_dual))
- is_dual = 0;
+ if (is_dual && of_property_read_u32(np, "xlnx,gpio2-width", &width[1]))
+ width[1] = 32;
- if (is_dual) {
- /* Update GPIO state shadow register with default value */
- of_property_read_u32(np, "xlnx,dout-default-2",
- &chip->gpio_state[1]);
+ if (width[1] > 32)
+ return -EINVAL;
- /* Update GPIO direction shadow register with default value */
- if (of_property_read_u32(np, "xlnx,tri-default-2",
- &chip->gpio_dir[1]))
- chip->gpio_dir[1] = 0xFFFFFFFF;
+ /* Setup software pin mapping */
+ bitmap_set(chip->sw_map, 0, width[0] + width[1]);
- /*
- * Check device node and parent device node for device width
- * and assume default width of 32
- */
- if (of_property_read_u32(np, "xlnx,gpio2-width",
- &chip->gpio_width[1]))
- chip->gpio_width[1] = 32;
+ /* Setup hardware pin mapping */
+ bitmap_set(chip->hw_map, 0, width[0]);
+ bitmap_set(chip->hw_map, 32, width[1]);
- spin_lock_init(&chip->gpio_lock[1]);
- }
+ spin_lock_init(&chip->gpio_lock);
chip->gc.base = -1;
- chip->gc.ngpio = chip->gpio_width[0] + chip->gpio_width[1];
+ chip->gc.ngpio = bitmap_weight(chip->hw_map, 64);
chip->gc.parent = &pdev->dev;
chip->gc.direction_input = xgpio_dir_in;
chip->gc.direction_output = xgpio_dir_out;
+ chip->gc.of_gpio_n_cells = cells;
chip->gc.get = xgpio_get;
chip->gc.set = xgpio_set;
+ chip->gc.request = xgpio_request;
+ chip->gc.free = xgpio_free;
chip->gc.set_multiple = xgpio_set_multiple;
chip->gc.label = dev_name(&pdev->dev);
@@ -333,15 +643,69 @@ static int xgpio_probe(struct platform_device *pdev)
return PTR_ERR(chip->regs);
}
+ chip->clk = devm_clk_get_optional(&pdev->dev, NULL);
+ if (IS_ERR(chip->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk), "input clock not found.\n");
+
+ status = clk_prepare_enable(chip->clk);
+ if (status < 0) {
+ dev_err(&pdev->dev, "Failed to prepare clk\n");
+ return status;
+ }
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
xgpio_save_regs(chip);
+ chip->irq = platform_get_irq_optional(pdev, 0);
+ if (chip->irq <= 0)
+ goto skip_irq;
+
+ chip->irqchip.name = "gpio-xilinx";
+ chip->irqchip.irq_ack = xgpio_irq_ack;
+ chip->irqchip.irq_mask = xgpio_irq_mask;
+ chip->irqchip.irq_unmask = xgpio_irq_unmask;
+ chip->irqchip.irq_set_type = xgpio_set_irq_type;
+
+ /* Disable per-channel interrupts */
+ xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, 0);
+ /* Clear any existing per-channel interrupts */
+ temp = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
+ xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, temp);
+ /* Enable global interrupts */
+ xgpio_writereg(chip->regs + XGPIO_GIER_OFFSET, XGPIO_GIER_IE);
+
+ girq = &chip->gc.irq;
+ girq->chip = &chip->irqchip;
+ girq->parent_handler = xgpio_irqhandler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents) {
+ status = -ENOMEM;
+ goto err_pm_put;
+ }
+ girq->parents[0] = chip->irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
+
+skip_irq:
status = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip);
if (status) {
dev_err(&pdev->dev, "failed to add GPIO chip\n");
- return status;
+ goto err_pm_put;
}
+ pm_runtime_put(&pdev->dev);
return 0;
+
+err_pm_put:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ clk_disable_unprepare(chip->clk);
+ return status;
}
static const struct of_device_id xgpio_of_match[] = {
@@ -353,9 +717,11 @@ MODULE_DEVICE_TABLE(of, xgpio_of_match);
static struct platform_driver xgpio_plat_driver = {
.probe = xgpio_probe,
+ .remove = xgpio_remove,
.driver = {
.name = "gpio-xilinx",
.of_match_table = xgpio_of_match,
+ .pm = &xgpio_dev_pm_ops,
},
};
diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c
index 54d3359444f3..0199f545335f 100644
--- a/drivers/gpio/gpio-xlp.c
+++ b/drivers/gpio/gpio-xlp.c
@@ -6,7 +6,6 @@
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
-#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
@@ -26,16 +25,6 @@
*
* where addr is base address of the that feature register and gpio is the pin.
*/
-#define GPIO_OUTPUT_EN 0x00
-#define GPIO_PADDRV 0x08
-#define GPIO_INT_EN00 0x18
-#define GPIO_INT_EN10 0x20
-#define GPIO_INT_EN20 0x28
-#define GPIO_INT_EN30 0x30
-#define GPIO_INT_POL 0x38
-#define GPIO_INT_TYPE 0x40
-#define GPIO_INT_STAT 0x48
-
#define GPIO_9XX_BYTESWAP 0X00
#define GPIO_9XX_CTRL 0X04
#define GPIO_9XX_OUTPUT_EN 0x14
@@ -52,14 +41,6 @@
#define GPIO_9XX_INT_TYPE 0x114
#define GPIO_9XX_INT_STAT 0x124
-#define GPIO_3XX_INT_EN00 0x18
-#define GPIO_3XX_INT_EN10 0x20
-#define GPIO_3XX_INT_EN20 0x28
-#define GPIO_3XX_INT_EN30 0x30
-#define GPIO_3XX_INT_POL 0x78
-#define GPIO_3XX_INT_TYPE 0x80
-#define GPIO_3XX_INT_STAT 0x88
-
/* Interrupt type register mask */
#define XLP_GPIO_IRQ_TYPE_LVL 0x0
#define XLP_GPIO_IRQ_TYPE_EDGE 0x1
@@ -72,16 +53,6 @@
#define XLP_GPIO_IRQ_BASE 768
#define XLP_MAX_NR_GPIO 96
-/* XLP variants supported by this driver */
-enum {
- XLP_GPIO_VARIANT_XLP832 = 1,
- XLP_GPIO_VARIANT_XLP316,
- XLP_GPIO_VARIANT_XLP208,
- XLP_GPIO_VARIANT_XLP980,
- XLP_GPIO_VARIANT_XLP532,
- GPIO_VARIANT_VULCAN
-};
-
struct xlp_gpio_priv {
struct gpio_chip chip;
DECLARE_BITMAP(gpio_enabled_mask, XLP_MAX_NR_GPIO);
@@ -216,8 +187,7 @@ static void xlp_gpio_generic_handler(struct irq_desc *desc)
}
if (gpio_stat & BIT(gpio % XLP_GPIO_REGSZ))
- generic_handle_irq(irq_find_mapping(
- priv->chip.irq.domain, gpio));
+ generic_handle_domain_irq(priv->chip.irq.domain, gpio);
}
chained_irq_exit(irqchip, desc);
}
@@ -258,43 +228,13 @@ static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state)
xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state);
}
-static const struct of_device_id xlp_gpio_of_ids[] = {
- {
- .compatible = "netlogic,xlp832-gpio",
- .data = (void *)XLP_GPIO_VARIANT_XLP832,
- },
- {
- .compatible = "netlogic,xlp316-gpio",
- .data = (void *)XLP_GPIO_VARIANT_XLP316,
- },
- {
- .compatible = "netlogic,xlp208-gpio",
- .data = (void *)XLP_GPIO_VARIANT_XLP208,
- },
- {
- .compatible = "netlogic,xlp980-gpio",
- .data = (void *)XLP_GPIO_VARIANT_XLP980,
- },
- {
- .compatible = "netlogic,xlp532-gpio",
- .data = (void *)XLP_GPIO_VARIANT_XLP532,
- },
- {
- .compatible = "brcm,vulcan-gpio",
- .data = (void *)GPIO_VARIANT_VULCAN,
- },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, xlp_gpio_of_ids);
-
static int xlp_gpio_probe(struct platform_device *pdev)
{
struct gpio_chip *gc;
+ struct gpio_irq_chip *girq;
struct xlp_gpio_priv *priv;
void __iomem *gpio_base;
- int irq_base, irq, err;
- int ngpio;
- u32 soc_type;
+ int irq, err;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -308,62 +248,12 @@ static int xlp_gpio_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- if (pdev->dev.of_node) {
- soc_type = (uintptr_t)of_device_get_match_data(&pdev->dev);
- } else {
- const struct acpi_device_id *acpi_id;
-
- acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
- &pdev->dev);
- if (!acpi_id || !acpi_id->driver_data) {
- dev_err(&pdev->dev, "Unable to match ACPI ID\n");
- return -ENODEV;
- }
- soc_type = (uintptr_t) acpi_id->driver_data;
- }
-
- switch (soc_type) {
- case XLP_GPIO_VARIANT_XLP832:
- priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN;
- priv->gpio_paddrv = gpio_base + GPIO_PADDRV;
- priv->gpio_intr_stat = gpio_base + GPIO_INT_STAT;
- priv->gpio_intr_type = gpio_base + GPIO_INT_TYPE;
- priv->gpio_intr_pol = gpio_base + GPIO_INT_POL;
- priv->gpio_intr_en = gpio_base + GPIO_INT_EN00;
- ngpio = 41;
- break;
- case XLP_GPIO_VARIANT_XLP208:
- case XLP_GPIO_VARIANT_XLP316:
- priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN;
- priv->gpio_paddrv = gpio_base + GPIO_PADDRV;
- priv->gpio_intr_stat = gpio_base + GPIO_3XX_INT_STAT;
- priv->gpio_intr_type = gpio_base + GPIO_3XX_INT_TYPE;
- priv->gpio_intr_pol = gpio_base + GPIO_3XX_INT_POL;
- priv->gpio_intr_en = gpio_base + GPIO_3XX_INT_EN00;
-
- ngpio = (soc_type == XLP_GPIO_VARIANT_XLP208) ? 42 : 57;
- break;
- case XLP_GPIO_VARIANT_XLP980:
- case XLP_GPIO_VARIANT_XLP532:
- case GPIO_VARIANT_VULCAN:
- priv->gpio_out_en = gpio_base + GPIO_9XX_OUTPUT_EN;
- priv->gpio_paddrv = gpio_base + GPIO_9XX_PADDRV;
- priv->gpio_intr_stat = gpio_base + GPIO_9XX_INT_STAT;
- priv->gpio_intr_type = gpio_base + GPIO_9XX_INT_TYPE;
- priv->gpio_intr_pol = gpio_base + GPIO_9XX_INT_POL;
- priv->gpio_intr_en = gpio_base + GPIO_9XX_INT_EN00;
-
- if (soc_type == XLP_GPIO_VARIANT_XLP980)
- ngpio = 66;
- else if (soc_type == XLP_GPIO_VARIANT_XLP532)
- ngpio = 67;
- else
- ngpio = 70;
- break;
- default:
- dev_err(&pdev->dev, "Unknown Processor type!\n");
- return -ENODEV;
- }
+ priv->gpio_out_en = gpio_base + GPIO_9XX_OUTPUT_EN;
+ priv->gpio_paddrv = gpio_base + GPIO_9XX_PADDRV;
+ priv->gpio_intr_stat = gpio_base + GPIO_9XX_INT_STAT;
+ priv->gpio_intr_type = gpio_base + GPIO_9XX_INT_TYPE;
+ priv->gpio_intr_pol = gpio_base + GPIO_9XX_INT_POL;
+ priv->gpio_intr_en = gpio_base + GPIO_9XX_INT_EN00;
bitmap_zero(priv->gpio_enabled_mask, XLP_MAX_NR_GPIO);
@@ -373,8 +263,7 @@ static int xlp_gpio_probe(struct platform_device *pdev)
gc->label = dev_name(&pdev->dev);
gc->base = 0;
gc->parent = &pdev->dev;
- gc->ngpio = ngpio;
- gc->of_node = pdev->dev.of_node;
+ gc->ngpio = 70;
gc->direction_output = xlp_gpio_dir_output;
gc->direction_input = xlp_gpio_dir_input;
gc->set = xlp_gpio_set;
@@ -382,46 +271,33 @@ static int xlp_gpio_probe(struct platform_device *pdev)
spin_lock_init(&priv->lock);
- /* XLP(MIPS) has fixed range for GPIO IRQs, Vulcan(ARM64) does not */
- if (soc_type != GPIO_VARIANT_VULCAN) {
- irq_base = devm_irq_alloc_descs(&pdev->dev, -1,
- XLP_GPIO_IRQ_BASE,
- gc->ngpio, 0);
- if (irq_base < 0) {
- dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
- return irq_base;
- }
- } else {
- irq_base = 0;
- }
+ girq = &gc->irq;
+ girq->chip = &xlp_gpio_irq_chip;
+ girq->parent_handler = xlp_gpio_generic_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = irq;
+ girq->first = 0;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
err = gpiochip_add_data(gc, priv);
if (err < 0)
return err;
- err = gpiochip_irqchip_add(gc, &xlp_gpio_irq_chip, irq_base,
- handle_level_irq, IRQ_TYPE_NONE);
- if (err) {
- dev_err(&pdev->dev, "Could not connect irqchip to gpiochip!\n");
- goto out_gpio_remove;
- }
-
- gpiochip_set_chained_irqchip(gc, &xlp_gpio_irq_chip, irq,
- xlp_gpio_generic_handler);
-
dev_info(&pdev->dev, "registered %d GPIOs\n", gc->ngpio);
return 0;
-
-out_gpio_remove:
- gpiochip_remove(gc);
- return err;
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id xlp_gpio_acpi_match[] = {
- { "BRCM9006", GPIO_VARIANT_VULCAN },
- { "CAV9006", GPIO_VARIANT_VULCAN },
+ { "BRCM9006" },
+ { "CAV9006" },
{},
};
MODULE_DEVICE_TABLE(acpi, xlp_gpio_acpi_match);
@@ -430,7 +306,6 @@ MODULE_DEVICE_TABLE(acpi, xlp_gpio_acpi_match);
static struct platform_driver xlp_gpio_driver = {
.driver = {
.name = "xlp-gpio",
- .of_match_table = xlp_gpio_of_ids,
.acpi_match_table = ACPI_PTR(xlp_gpio_acpi_match),
},
.probe = xlp_gpio_probe,
diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c
index 05f1998c11a4..49c878cfd5c6 100644
--- a/drivers/gpio/gpio-xra1403.c
+++ b/drivers/gpio/gpio-xra1403.c
@@ -83,7 +83,10 @@ static int xra1403_get_direction(struct gpio_chip *chip, unsigned int offset)
if (ret)
return ret;
- return !!(val & BIT(offset % 8));
+ if (val & BIT(offset % 8))
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
}
static int xra1403_get(struct gpio_chip *chip, unsigned int offset)
@@ -118,6 +121,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
struct xra1403 *xra = gpiochip_get_data(chip);
int value[XRA_LAST];
int i;
+ const char *label;
unsigned int gcr;
unsigned int gsr;
@@ -133,12 +137,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
gcr = value[XRA_GCR + 1] << 8 | value[XRA_GCR];
gsr = value[XRA_GSR + 1] << 8 | value[XRA_GSR];
- for (i = 0; i < chip->ngpio; i++) {
- const char *label = gpiochip_is_requested(chip, i);
-
- if (!label)
- continue;
-
+ for_each_requested_gpio(chip, i, label) {
seq_printf(s, " gpio-%-3d (%-12s) %s %s\n",
chip->base + i, label,
(gcr & BIT(i)) ? "in" : "out",
@@ -187,15 +186,7 @@ static int xra1403_probe(struct spi_device *spi)
return ret;
}
- ret = devm_gpiochip_add_data(&spi->dev, &xra->chip, xra);
- if (ret < 0) {
- dev_err(&spi->dev, "Unable to register gpiochip\n");
- return ret;
- }
-
- spi_set_drvdata(spi, xra);
-
- return 0;
+ return devm_gpiochip_add_data(&spi->dev, &xra->chip, xra);
}
static const struct spi_device_id xra1403_ids[] = {
diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c
index 43d3fa5f511a..c8af34a6368f 100644
--- a/drivers/gpio/gpio-xtensa.c
+++ b/drivers/gpio/gpio-xtensa.c
@@ -44,15 +44,14 @@ static inline unsigned long enable_cp(unsigned long *cpenable)
unsigned long flags;
local_irq_save(flags);
- RSR_CPENABLE(*cpenable);
- WSR_CPENABLE(*cpenable | BIT(XCHAL_CP_ID_XTIOP));
-
+ *cpenable = xtensa_get_sr(cpenable);
+ xtensa_set_sr(*cpenable | BIT(XCHAL_CP_ID_XTIOP), cpenable);
return flags;
}
static inline void disable_cp(unsigned long flags, unsigned long cpenable)
{
- WSR_CPENABLE(cpenable);
+ xtensa_set_sr(cpenable, cpenable);
local_irq_restore(flags);
}
@@ -72,7 +71,7 @@ static inline void disable_cp(unsigned long flags, unsigned long cpenable)
static int xtensa_impwire_get_direction(struct gpio_chip *gc, unsigned offset)
{
- return 1; /* input only */
+ return GPIO_LINE_DIRECTION_IN; /* input only */
}
static int xtensa_impwire_get_value(struct gpio_chip *gc, unsigned offset)
@@ -95,7 +94,7 @@ static void xtensa_impwire_set_value(struct gpio_chip *gc, unsigned offset,
static int xtensa_expstate_get_direction(struct gpio_chip *gc, unsigned offset)
{
- return 0; /* output only */
+ return GPIO_LINE_DIRECTION_OUT; /* output only */
}
static int xtensa_expstate_get_value(struct gpio_chip *gc, unsigned offset)
diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c
index f6f8a541348f..ce9d1282165c 100644
--- a/drivers/gpio/gpio-zevio.c
+++ b/drivers/gpio/gpio-zevio.c
@@ -11,7 +11,6 @@
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/gpio/driver.h>
@@ -53,22 +52,23 @@
#define ZEVIO_GPIO_BIT(gpio) (gpio&7)
struct zevio_gpio {
+ struct gpio_chip chip;
spinlock_t lock;
- struct of_mm_gpio_chip chip;
+ void __iomem *regs;
};
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->chip.regs + section_offset + port_offset));
+ return readl(IOMEM(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->chip.regs + section_offset + port_offset));
+ writel(val, IOMEM(c->regs + section_offset + port_offset));
}
/* Functions for struct gpio_chip */
@@ -178,12 +178,15 @@ static int zevio_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, controller);
/* Copy our reference */
- controller->chip.gc = zevio_gpio_chip;
- controller->chip.gc.parent = &pdev->dev;
+ controller->chip = zevio_gpio_chip;
+ controller->chip.parent = &pdev->dev;
- status = of_mm_gpiochip_add_data(pdev->dev.of_node,
- &(controller->chip),
- controller);
+ controller->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(controller->regs))
+ return dev_err_probe(&pdev->dev, PTR_ERR(controller->regs),
+ "failed to ioremap memory resource\n");
+
+ status = devm_gpiochip_add_data(&pdev->dev, &controller->chip, controller);
if (status) {
dev_err(&pdev->dev, "failed to add gpiochip: %d\n", status);
return status;
@@ -192,10 +195,10 @@ static int zevio_gpio_probe(struct platform_device *pdev)
spin_lock_init(&controller->lock);
/* Disable interrupts, they only cause errors */
- for (i = 0; i < controller->chip.gc.ngpio; i += 8)
+ for (i = 0; i < controller->chip.ngpio; i += 8)
zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF);
- dev_dbg(controller->chip.gc.parent, "ZEVIO GPIO controller set up!\n");
+ dev_dbg(controller->chip.parent, "ZEVIO GPIO controller set up!\n");
return 0;
}
diff --git a/drivers/gpio/gpio-zx.c b/drivers/gpio/gpio-zx.c
deleted file mode 100644
index 8637adb6bc20..000000000000
--- a/drivers/gpio/gpio-zx.c
+++ /dev/null
@@ -1,292 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ZTE ZX296702 GPIO driver
- *
- * Author: Jun Nie <jun.nie@linaro.org>
- *
- * Copyright (C) 2015 Linaro Ltd.
- */
-#include <linux/bitops.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/gpio/driver.h>
-#include <linux/irqchip/chained_irq.h>
-#include <linux/init.h>
-#include <linux/of.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-
-#define ZX_GPIO_DIR 0x00
-#define ZX_GPIO_IVE 0x04
-#define ZX_GPIO_IV 0x08
-#define ZX_GPIO_IEP 0x0C
-#define ZX_GPIO_IEN 0x10
-#define ZX_GPIO_DI 0x14
-#define ZX_GPIO_DO1 0x18
-#define ZX_GPIO_DO0 0x1C
-#define ZX_GPIO_DO 0x20
-
-#define ZX_GPIO_IM 0x28
-#define ZX_GPIO_IE 0x2C
-
-#define ZX_GPIO_MIS 0x30
-#define ZX_GPIO_IC 0x34
-
-#define ZX_GPIO_NR 16
-
-struct zx_gpio {
- raw_spinlock_t lock;
-
- void __iomem *base;
- struct gpio_chip gc;
-};
-
-static int zx_direction_input(struct gpio_chip *gc, unsigned offset)
-{
- struct zx_gpio *chip = gpiochip_get_data(gc);
- unsigned long flags;
- u16 gpiodir;
-
- if (offset >= gc->ngpio)
- return -EINVAL;
-
- raw_spin_lock_irqsave(&chip->lock, flags);
- gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR);
- gpiodir &= ~BIT(offset);
- writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR);
- raw_spin_unlock_irqrestore(&chip->lock, flags);
-
- return 0;
-}
-
-static int zx_direction_output(struct gpio_chip *gc, unsigned offset,
- int value)
-{
- struct zx_gpio *chip = gpiochip_get_data(gc);
- unsigned long flags;
- u16 gpiodir;
-
- if (offset >= gc->ngpio)
- return -EINVAL;
-
- raw_spin_lock_irqsave(&chip->lock, flags);
- gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR);
- gpiodir |= BIT(offset);
- writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR);
-
- if (value)
- writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1);
- else
- writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0);
- raw_spin_unlock_irqrestore(&chip->lock, flags);
-
- return 0;
-}
-
-static int zx_get_value(struct gpio_chip *gc, unsigned offset)
-{
- struct zx_gpio *chip = gpiochip_get_data(gc);
-
- return !!(readw_relaxed(chip->base + ZX_GPIO_DI) & BIT(offset));
-}
-
-static void zx_set_value(struct gpio_chip *gc, unsigned offset, int value)
-{
- struct zx_gpio *chip = gpiochip_get_data(gc);
-
- if (value)
- writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1);
- else
- writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0);
-}
-
-static int zx_irq_type(struct irq_data *d, unsigned trigger)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct zx_gpio *chip = gpiochip_get_data(gc);
- int offset = irqd_to_hwirq(d);
- unsigned long flags;
- u16 gpiois, gpioi_epos, gpioi_eneg, gpioiev;
- u16 bit = BIT(offset);
-
- if (offset < 0 || offset >= ZX_GPIO_NR)
- return -EINVAL;
-
- raw_spin_lock_irqsave(&chip->lock, flags);
-
- gpioiev = readw_relaxed(chip->base + ZX_GPIO_IV);
- gpiois = readw_relaxed(chip->base + ZX_GPIO_IVE);
- gpioi_epos = readw_relaxed(chip->base + ZX_GPIO_IEP);
- gpioi_eneg = readw_relaxed(chip->base + ZX_GPIO_IEN);
-
- if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
- gpiois |= bit;
- if (trigger & IRQ_TYPE_LEVEL_HIGH)
- gpioiev |= bit;
- else
- gpioiev &= ~bit;
- } else
- gpiois &= ~bit;
-
- if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
- gpioi_epos |= bit;
- gpioi_eneg |= bit;
- } else {
- if (trigger & IRQ_TYPE_EDGE_RISING) {
- gpioi_epos |= bit;
- gpioi_eneg &= ~bit;
- } else if (trigger & IRQ_TYPE_EDGE_FALLING) {
- gpioi_eneg |= bit;
- gpioi_epos &= ~bit;
- }
- }
-
- writew_relaxed(gpiois, chip->base + ZX_GPIO_IVE);
- writew_relaxed(gpioi_epos, chip->base + ZX_GPIO_IEP);
- writew_relaxed(gpioi_eneg, chip->base + ZX_GPIO_IEN);
- writew_relaxed(gpioiev, chip->base + ZX_GPIO_IV);
- raw_spin_unlock_irqrestore(&chip->lock, flags);
-
- return 0;
-}
-
-static void zx_irq_handler(struct irq_desc *desc)
-{
- unsigned long pending;
- int offset;
- struct gpio_chip *gc = irq_desc_get_handler_data(desc);
- struct zx_gpio *chip = gpiochip_get_data(gc);
- struct irq_chip *irqchip = irq_desc_get_chip(desc);
-
- chained_irq_enter(irqchip, desc);
-
- pending = readw_relaxed(chip->base + ZX_GPIO_MIS);
- writew_relaxed(pending, chip->base + ZX_GPIO_IC);
- if (pending) {
- for_each_set_bit(offset, &pending, ZX_GPIO_NR)
- generic_handle_irq(irq_find_mapping(gc->irq.domain,
- offset));
- }
-
- chained_irq_exit(irqchip, desc);
-}
-
-static void zx_irq_mask(struct irq_data *d)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct zx_gpio *chip = gpiochip_get_data(gc);
- u16 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR);
- u16 gpioie;
-
- raw_spin_lock(&chip->lock);
- gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) | mask;
- writew_relaxed(gpioie, chip->base + ZX_GPIO_IM);
- gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) & ~mask;
- writew_relaxed(gpioie, chip->base + ZX_GPIO_IE);
- raw_spin_unlock(&chip->lock);
-}
-
-static void zx_irq_unmask(struct irq_data *d)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct zx_gpio *chip = gpiochip_get_data(gc);
- u16 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR);
- u16 gpioie;
-
- raw_spin_lock(&chip->lock);
- gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) & ~mask;
- writew_relaxed(gpioie, chip->base + ZX_GPIO_IM);
- gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) | mask;
- writew_relaxed(gpioie, chip->base + ZX_GPIO_IE);
- raw_spin_unlock(&chip->lock);
-}
-
-static struct irq_chip zx_irqchip = {
- .name = "zx-gpio",
- .irq_mask = zx_irq_mask,
- .irq_unmask = zx_irq_unmask,
- .irq_set_type = zx_irq_type,
-};
-
-static int zx_gpio_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct zx_gpio *chip;
- int irq, id, ret;
-
- chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- chip->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(chip->base))
- return PTR_ERR(chip->base);
-
- raw_spin_lock_init(&chip->lock);
- if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
- chip->gc.request = gpiochip_generic_request;
- chip->gc.free = gpiochip_generic_free;
- }
-
- id = of_alias_get_id(dev->of_node, "gpio");
- chip->gc.direction_input = zx_direction_input;
- chip->gc.direction_output = zx_direction_output;
- chip->gc.get = zx_get_value;
- chip->gc.set = zx_set_value;
- chip->gc.base = ZX_GPIO_NR * id;
- chip->gc.ngpio = ZX_GPIO_NR;
- chip->gc.label = dev_name(dev);
- chip->gc.parent = dev;
- chip->gc.owner = THIS_MODULE;
-
- ret = gpiochip_add_data(&chip->gc, chip);
- if (ret)
- return ret;
-
- /*
- * irq_chip support
- */
- writew_relaxed(0xffff, chip->base + ZX_GPIO_IM);
- writew_relaxed(0, chip->base + ZX_GPIO_IE);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "invalid IRQ\n");
- gpiochip_remove(&chip->gc);
- return -ENODEV;
- }
-
- ret = gpiochip_irqchip_add(&chip->gc, &zx_irqchip,
- 0, handle_simple_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "could not add irqchip\n");
- gpiochip_remove(&chip->gc);
- return ret;
- }
- gpiochip_set_chained_irqchip(&chip->gc, &zx_irqchip,
- irq, zx_irq_handler);
-
- platform_set_drvdata(pdev, chip);
- dev_info(dev, "ZX GPIO chip registered\n");
-
- return 0;
-}
-
-static const struct of_device_id zx_gpio_match[] = {
- {
- .compatible = "zte,zx296702-gpio",
- },
- { },
-};
-
-static struct platform_driver zx_gpio_driver = {
- .probe = zx_gpio_probe,
- .driver = {
- .name = "zx_gpio",
- .of_match_table = of_match_ptr(zx_gpio_match),
- },
-};
-builtin_platform_driver(zx_gpio_driver)
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index f241b6c13dbe..06c6401f02b8 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -10,6 +10,7 @@
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -21,6 +22,9 @@
/* Maximum banks */
#define ZYNQ_GPIO_MAX_BANK 4
#define ZYNQMP_GPIO_MAX_BANK 6
+#define VERSAL_GPIO_MAX_BANK 4
+#define PMC_GPIO_MAX_BANK 5
+#define VERSAL_UNUSED_BANKS 2
#define ZYNQ_GPIO_BANK0_NGPIO 32
#define ZYNQ_GPIO_BANK1_NGPIO 22
@@ -95,6 +99,7 @@
/* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */
#define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0)
#define GPIO_QUIRK_DATA_RO_BUG BIT(1)
+#define GPIO_QUIRK_VERSAL BIT(2)
struct gpio_regs {
u32 datamsw[ZYNQMP_GPIO_MAX_BANK];
@@ -116,6 +121,7 @@ struct gpio_regs {
* @irq: interrupt for the GPIO device
* @p_data: pointer to platform data
* @context: context registers
+ * @dirlock: lock used for direction in/out synchronization
*/
struct zynq_gpio {
struct gpio_chip chip;
@@ -124,6 +130,7 @@ struct zynq_gpio {
int irq;
const struct zynq_platform_data *p_data;
struct gpio_regs context;
+ spinlock_t dirlock; /* lock */
};
/**
@@ -196,6 +203,8 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
gpio->p_data->bank_min[bank];
return;
}
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank = bank + VERSAL_UNUSED_BANKS;
}
/* default */
@@ -297,6 +306,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
{
u32 reg;
unsigned int bank_num, bank_pin_num;
+ unsigned long flags;
struct zynq_gpio *gpio = gpiochip_get_data(chip);
zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
@@ -310,9 +320,11 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
return -EINVAL;
/* clear the bit in direction mode reg to set the pin as input */
+ spin_lock_irqsave(&gpio->dirlock, flags);
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
reg &= ~BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+ spin_unlock_irqrestore(&gpio->dirlock, flags);
return 0;
}
@@ -334,11 +346,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
{
u32 reg;
unsigned int bank_num, bank_pin_num;
+ unsigned long flags;
struct zynq_gpio *gpio = gpiochip_get_data(chip);
zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
/* set the GPIO pin as output */
+ spin_lock_irqsave(&gpio->dirlock, flags);
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
reg |= BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
@@ -347,6 +361,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
reg |= BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
+ spin_unlock_irqrestore(&gpio->dirlock, flags);
/* set the state of the pin */
zynq_gpio_set_value(chip, pin, state);
@@ -360,7 +375,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
*
* This function returns the direction of the specified GPIO.
*
- * Return: 0 for output, 1 for input
+ * Return: GPIO_LINE_DIRECTION_OUT or GPIO_LINE_DIRECTION_IN
*/
static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
{
@@ -372,7 +387,10 @@ static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
- return !(reg & BIT(bank_pin_num));
+ if (reg & BIT(bank_pin_num))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
}
/**
@@ -556,7 +574,7 @@ static int zynq_gpio_irq_reqres(struct irq_data *d)
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
int ret;
- ret = pm_runtime_get_sync(chip->parent);
+ ret = pm_runtime_resume_and_get(chip->parent);
if (ret < 0)
return ret;
@@ -610,12 +628,8 @@ static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio,
if (!pending)
return;
- for_each_set_bit(offset, &pending, 32) {
- unsigned int gpio_irq;
-
- gpio_irq = irq_find_mapping(irqdomain, offset + bank_offset);
- generic_handle_irq(gpio_irq);
- }
+ for_each_set_bit(offset, &pending, 32)
+ generic_handle_domain_irq(irqdomain, offset + bank_offset);
}
/**
@@ -644,6 +658,8 @@ static void zynq_gpio_irqhandler(struct irq_desc *desc)
int_enb = readl_relaxed(gpio->base_addr +
ZYNQ_GPIO_INTMASK_OFFSET(bank_num));
zynq_gpio_handle_bank_irq(gpio, bank_num, int_sts & ~int_enb);
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank_num = bank_num + VERSAL_UNUSED_BANKS;
}
chained_irq_exit(irqchip, desc);
@@ -673,6 +689,8 @@ static void zynq_gpio_save_context(struct zynq_gpio *gpio)
gpio->context.int_any[bank_num] =
readl_relaxed(gpio->base_addr +
ZYNQ_GPIO_INTANY_OFFSET(bank_num));
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank_num = bank_num + VERSAL_UNUSED_BANKS;
}
}
@@ -681,6 +699,8 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio)
unsigned int bank_num;
for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) {
+ writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr +
+ ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
writel_relaxed(gpio->context.datalsw[bank_num],
gpio->base_addr +
ZYNQ_GPIO_DATA_LSW_OFFSET(bank_num));
@@ -690,9 +710,6 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio)
writel_relaxed(gpio->context.dirm[bank_num],
gpio->base_addr +
ZYNQ_GPIO_DIRM_OFFSET(bank_num));
- writel_relaxed(gpio->context.int_en[bank_num],
- gpio->base_addr +
- ZYNQ_GPIO_INTEN_OFFSET(bank_num));
writel_relaxed(gpio->context.int_type[bank_num],
gpio->base_addr +
ZYNQ_GPIO_INTTYPE_OFFSET(bank_num));
@@ -702,6 +719,11 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio)
writel_relaxed(gpio->context.int_any[bank_num],
gpio->base_addr +
ZYNQ_GPIO_INTANY_OFFSET(bank_num));
+ writel_relaxed(~(gpio->context.int_en[bank_num]),
+ gpio->base_addr +
+ ZYNQ_GPIO_INTEN_OFFSET(bank_num));
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank_num = bank_num + VERSAL_UNUSED_BANKS;
}
}
@@ -710,6 +732,14 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev)
struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
+ if (!data) {
+ dev_err(dev, "irq_get_irq_data() failed\n");
+ return -EINVAL;
+ }
+
+ if (!device_may_wakeup(dev))
+ disable_irq(gpio->irq);
+
if (!irqd_is_wakeup_set(data)) {
zynq_gpio_save_context(gpio);
return pm_runtime_force_suspend(dev);
@@ -724,6 +754,14 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev)
struct irq_data *data = irq_get_irq_data(gpio->irq);
int ret;
+ if (!data) {
+ dev_err(dev, "irq_get_irq_data() failed\n");
+ return -EINVAL;
+ }
+
+ if (!device_may_wakeup(dev))
+ enable_irq(gpio->irq);
+
if (!irqd_is_wakeup_set(data)) {
ret = pm_runtime_force_resume(dev);
zynq_gpio_restore_context(gpio);
@@ -773,6 +811,31 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
zynq_gpio_runtime_resume, NULL)
};
+static const struct zynq_platform_data versal_gpio_def = {
+ .label = "versal_gpio",
+ .quirks = GPIO_QUIRK_VERSAL,
+ .ngpio = 58,
+ .max_bank = VERSAL_GPIO_MAX_BANK,
+ .bank_min[0] = 0,
+ .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */
+ .bank_min[3] = 26,
+ .bank_max[3] = 57, /* Bank 3 is connected to FMIOs (32 pins) */
+};
+
+static const struct zynq_platform_data pmc_gpio_def = {
+ .label = "pmc_gpio",
+ .ngpio = 116,
+ .max_bank = PMC_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) */
+ .bank_min[3] = 52,
+ .bank_max[3] = 83, /* Bank 3 is connected to EMIOs (32 pins) */
+ .bank_min[4] = 84,
+ .bank_max[4] = 115, /* Bank 4 is connected to EMIOs (32 pins) */
+};
+
static const struct zynq_platform_data zynqmp_gpio_def = {
.label = "zynqmp_gpio",
.quirks = GPIO_QUIRK_DATA_RO_BUG,
@@ -810,6 +873,8 @@ static const struct zynq_platform_data zynq_gpio_def = {
static const struct of_device_id zynq_gpio_of_match[] = {
{ .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def },
{ .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 },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
@@ -830,6 +895,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
int ret, bank_num;
struct zynq_gpio *gpio;
struct gpio_chip *chip;
+ struct gpio_irq_chip *girq;
const struct of_device_id *match;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
@@ -849,10 +915,8 @@ static int zynq_gpio_probe(struct platform_device *pdev)
return PTR_ERR(gpio->base_addr);
gpio->irq = platform_get_irq(pdev, 0);
- if (gpio->irq < 0) {
- dev_err(&pdev->dev, "invalid IRQ\n");
+ if (gpio->irq < 0)
return gpio->irq;
- }
/* configure the gpio chip */
chip = &gpio->chip;
@@ -871,50 +935,60 @@ static int zynq_gpio_probe(struct platform_device *pdev)
/* Retrieve GPIO clock */
gpio->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(gpio->clk)) {
- dev_err(&pdev->dev, "input clock not found.\n");
- return PTR_ERR(gpio->clk);
- }
+ if (IS_ERR(gpio->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(gpio->clk), "input clock not found.\n");
+
ret = clk_prepare_enable(gpio->clk);
if (ret) {
dev_err(&pdev->dev, "Unable to enable clock.\n");
return ret;
}
+ spin_lock_init(&gpio->dirlock);
+
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- ret = pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
goto err_pm_dis;
- /* report a bug if gpio chip registration fails */
- ret = gpiochip_add_data(chip, gpio);
- if (ret) {
- dev_err(&pdev->dev, "Failed to add gpio chip\n");
- goto err_pm_put;
- }
-
/* disable interrupts for all banks */
- for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++)
+ for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) {
writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr +
ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank_num = bank_num + VERSAL_UNUSED_BANKS;
+ }
- ret = gpiochip_irqchip_add(chip, &zynq_gpio_edge_irqchip, 0,
- handle_level_irq, IRQ_TYPE_NONE);
- if (ret) {
- dev_err(&pdev->dev, "Failed to add irq chip\n");
- goto err_rm_gpiochip;
+ /* Set up the GPIO irqchip */
+ girq = &chip->irq;
+ girq->chip = &zynq_gpio_edge_irqchip;
+ girq->parent_handler = zynq_gpio_irqhandler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(&pdev->dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents) {
+ ret = -ENOMEM;
+ goto err_pm_put;
}
+ girq->parents[0] = gpio->irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
- gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, gpio->irq,
- zynq_gpio_irqhandler);
+ /* report a bug if gpio chip registration fails */
+ ret = gpiochip_add_data(chip, gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add gpio chip\n");
+ goto err_pm_put;
+ }
+ irq_set_status_flags(gpio->irq, IRQ_DISABLE_UNLAZY);
+ device_init_wakeup(&pdev->dev, 1);
pm_runtime_put(&pdev->dev);
return 0;
-err_rm_gpiochip:
- gpiochip_remove(chip);
err_pm_put:
pm_runtime_put(&pdev->dev);
err_pm_dis:
@@ -933,8 +1007,11 @@ err_pm_dis:
static int zynq_gpio_remove(struct platform_device *pdev)
{
struct zynq_gpio *gpio = platform_get_drvdata(pdev);
+ int ret;
- pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "pm_runtime_get_sync() Failed\n");
gpiochip_remove(&gpio->chip);
clk_disable_unprepare(gpio->clk);
device_set_wakeup_capable(&pdev->dev, 0);
@@ -952,22 +1029,7 @@ static struct platform_driver zynq_gpio_driver = {
.remove = zynq_gpio_remove,
};
-/**
- * zynq_gpio_init - Initial driver registration call
- *
- * Return: value from platform_driver_register
- */
-static int __init zynq_gpio_init(void)
-{
- return platform_driver_register(&zynq_gpio_driver);
-}
-postcore_initcall(zynq_gpio_init);
-
-static void __exit zynq_gpio_exit(void)
-{
- platform_driver_unregister(&zynq_gpio_driver);
-}
-module_exit(zynq_gpio_exit);
+module_platform_driver(zynq_gpio_driver);
MODULE_AUTHOR("Xilinx Inc.");
MODULE_DESCRIPTION("Zynq GPIO driver");
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(&regval);
+ 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");
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 39f2f9035c11..c2523ac26fac 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -7,6 +7,7 @@
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
+#include <linux/dmi.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
@@ -18,6 +19,23 @@
#include <linux/pinctrl/pinctrl.h>
#include "gpiolib.h"
+#include "gpiolib-acpi.h"
+
+static int run_edge_events_on_boot = -1;
+module_param(run_edge_events_on_boot, int, 0444);
+MODULE_PARM_DESC(run_edge_events_on_boot,
+ "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto");
+
+static char *ignore_wake;
+module_param(ignore_wake, charp, 0444);
+MODULE_PARM_DESC(ignore_wake,
+ "controller@pin combos on which to ignore the ACPI wake flag "
+ "ignore_wake=controller@pin[,controller@pin[,...]]");
+
+struct acpi_gpiolib_dmi_quirk {
+ bool no_edge_events_on_boot;
+ char *ignore_wake;
+};
/**
* struct acpi_gpio_event - ACPI GPIO event handler data
@@ -77,10 +95,7 @@ static bool acpi_gpio_deferred_req_irqs_done;
static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
{
- if (!gc->parent)
- return false;
-
- return ACPI_HANDLE(gc->parent) == data;
+ return gc->parent && device_match_acpi_handle(gc->parent, data);
}
/**
@@ -93,7 +108,7 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
* controller does not have GPIO chip registered at the moment. This is to
* support probe deferral.
*/
-static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
+static struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin)
{
struct gpio_chip *chip;
acpi_handle handle;
@@ -110,6 +125,34 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
return gpiochip_get_desc(chip, pin);
}
+/**
+ * acpi_get_and_request_gpiod - Translate ACPI GPIO pin to GPIO descriptor and
+ * hold a refcount to the GPIO device.
+ * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
+ * @pin: ACPI GPIO pin number (0-based, controller-relative)
+ * @label: Label to pass to gpiod_request()
+ *
+ * This function is a simple pass-through to acpi_get_gpiod(), except that
+ * as it is intended for use outside of the GPIO layer (in a similar fashion to
+ * gpiod_get_index() for example) it also holds a reference to the GPIO device.
+ */
+struct gpio_desc *acpi_get_and_request_gpiod(char *path, unsigned int pin, char *label)
+{
+ struct gpio_desc *gpio;
+ int ret;
+
+ gpio = acpi_get_gpiod(path, pin);
+ if (IS_ERR(gpio))
+ return gpio;
+
+ ret = gpiod_request(gpio, label);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return gpio;
+}
+EXPORT_SYMBOL_GPL(acpi_get_and_request_gpiod);
+
static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
{
struct acpi_gpio_event *event = data;
@@ -150,17 +193,39 @@ bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
}
EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource);
+/**
+ * acpi_gpio_get_io_resource - Fetch details of an ACPI resource if it is a GPIO
+ * I/O resource or return False if not.
+ * @ares: Pointer to the ACPI resource to fetch
+ * @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer
+ */
+bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
+ struct acpi_resource_gpio **agpio)
+{
+ struct acpi_resource_gpio *gpio;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
+ return false;
+
+ gpio = &ares->data.gpio;
+ if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_IO)
+ return false;
+
+ *agpio = gpio;
+ return true;
+}
+EXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource);
+
static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,
struct acpi_gpio_event *event)
{
+ struct device *parent = acpi_gpio->chip->parent;
int ret, value;
ret = request_threaded_irq(event->irq, NULL, event->handler,
- event->irqflags, "ACPI:Event", event);
+ event->irqflags | IRQF_ONESHOT, "ACPI:Event", event);
if (ret) {
- dev_err(acpi_gpio->chip->parent,
- "Failed to setup interrupt handler for %d\n",
- event->irq);
+ dev_err(parent, "Failed to setup interrupt handler for %d\n", event->irq);
return;
}
@@ -170,10 +235,13 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,
event->irq_requested = true;
/* Make sure we trigger the initial state of edge-triggered IRQs */
- value = gpiod_get_raw_value_cansleep(event->desc);
- if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) ||
- ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0))
- event->handler(event->irq, event);
+ if (run_edge_events_on_boot &&
+ (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) {
+ value = gpiod_get_raw_value_cansleep(event->desc);
+ if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) ||
+ ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0))
+ event->handler(event->irq, event);
+ }
}
static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio)
@@ -184,6 +252,123 @@ static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio)
acpi_gpiochip_request_irq(acpi_gpio, event);
}
+static enum gpiod_flags
+acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity)
+{
+ /* GpioInt() implies input configuration */
+ if (agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT)
+ return GPIOD_IN;
+
+ switch (agpio->io_restriction) {
+ case ACPI_IO_RESTRICT_INPUT:
+ return GPIOD_IN;
+ case ACPI_IO_RESTRICT_OUTPUT:
+ /*
+ * ACPI GPIO resources don't contain an initial value for the
+ * GPIO. Therefore we deduce that value from the pull field
+ * and the polarity instead. If the pin is pulled up we assume
+ * default to be high, if it is pulled down we assume default
+ * to be low, otherwise we leave pin untouched. For active low
+ * polarity values will be switched. See also
+ * Documentation/firmware-guide/acpi/gpio-properties.rst.
+ */
+ switch (agpio->pin_config) {
+ case ACPI_PIN_CONFIG_PULLUP:
+ return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH;
+ case ACPI_PIN_CONFIG_PULLDOWN:
+ return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Assume that the BIOS has configured the direction and pull
+ * accordingly.
+ */
+ return GPIOD_ASIS;
+}
+
+static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip,
+ struct acpi_resource_gpio *agpio,
+ unsigned int index,
+ const char *label)
+{
+ int polarity = GPIO_ACTIVE_HIGH;
+ enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity);
+ unsigned int pin = agpio->pin_table[index];
+ struct gpio_desc *desc;
+ int ret;
+
+ desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags);
+ if (IS_ERR(desc))
+ return desc;
+
+ /* ACPI uses hundredths of milliseconds units */
+ ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout * 10);
+ if (ret)
+ dev_warn(chip->parent,
+ "Failed to set debounce-timeout for pin 0x%04X, err %d\n",
+ pin, ret);
+
+ return desc;
+}
+
+static bool acpi_gpio_in_ignore_list(const char *controller_in, unsigned int pin_in)
+{
+ const char *controller, *pin_str;
+ unsigned int pin;
+ char *endp;
+ int len;
+
+ controller = ignore_wake;
+ while (controller) {
+ pin_str = strchr(controller, '@');
+ if (!pin_str)
+ goto err;
+
+ len = pin_str - controller;
+ if (len == strlen(controller_in) &&
+ strncmp(controller, controller_in, len) == 0) {
+ pin = simple_strtoul(pin_str + 1, &endp, 10);
+ if (*endp != 0 && *endp != ',')
+ goto err;
+
+ if (pin == pin_in)
+ return true;
+ }
+
+ controller = strchr(controller, ',');
+ if (controller)
+ controller++;
+ }
+
+ return false;
+err:
+ pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_wake: %s\n", ignore_wake);
+ return false;
+}
+
+static bool acpi_gpio_irq_is_wake(struct device *parent,
+ struct acpi_resource_gpio *agpio)
+{
+ unsigned int pin = agpio->pin_table[0];
+
+ if (agpio->wake_capable != ACPI_WAKE_CAPABLE)
+ return false;
+
+ if (acpi_gpio_in_ignore_list(dev_name(parent), pin)) {
+ dev_info(parent, "Ignoring wakeup on pin %u\n", pin);
+ return false;
+ }
+
+ return true;
+}
+
+/* Always returns AE_OK so that we keep looping over the resources */
static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
void *context)
{
@@ -194,7 +379,8 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
struct acpi_gpio_event *event;
irq_handler_t handler = NULL;
struct gpio_desc *desc;
- int ret, pin, irq;
+ unsigned int pin;
+ int ret, irq;
if (!acpi_gpio_get_irq_resource(ares, &agpio))
return AE_OK;
@@ -203,8 +389,8 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
pin = agpio->pin_table[0];
if (pin <= 255) {
- char ev_name[5];
- sprintf(ev_name, "_%c%02hhX",
+ char ev_name[8];
+ sprintf(ev_name, "_%c%02X",
agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L',
pin);
if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle)))
@@ -217,22 +403,27 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
if (!handler)
return AE_OK;
- desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event",
- GPIO_ACTIVE_HIGH, GPIOD_IN);
+ desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event");
if (IS_ERR(desc)) {
- dev_err(chip->parent, "Failed to request GPIO\n");
- return AE_ERROR;
+ dev_err(chip->parent,
+ "Failed to request GPIO for pin 0x%04X, err %ld\n",
+ pin, PTR_ERR(desc));
+ return AE_OK;
}
ret = gpiochip_lock_as_irq(chip, pin);
if (ret) {
- dev_err(chip->parent, "Failed to lock GPIO as interrupt\n");
+ dev_err(chip->parent,
+ "Failed to lock GPIO pin 0x%04X as interrupt, err %d\n",
+ pin, ret);
goto fail_free_desc;
}
irq = gpiod_to_irq(desc);
if (irq < 0) {
- dev_err(chip->parent, "Failed to translate GPIO to IRQ\n");
+ dev_err(chip->parent,
+ "Failed to translate GPIO pin 0x%04X to IRQ, err %d\n",
+ pin, irq);
goto fail_unlock_irq;
}
@@ -264,7 +455,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
event->handle = evt_handle;
event->handler = handler;
event->irq = irq;
- event->irq_is_wake = agpio->wake_capable == ACPI_WAKE_CAPABLE;
+ event->irq_is_wake = acpi_gpio_irq_is_wake(chip->parent, agpio);
event->pin = pin;
event->desc = desc;
@@ -277,7 +468,7 @@ fail_unlock_irq:
fail_free_desc:
gpiochip_free_own_desc(desc);
- return AE_ERROR;
+ return AE_OK;
}
/**
@@ -382,36 +573,31 @@ int acpi_dev_add_driver_gpios(struct acpi_device *adev,
}
EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios);
-static void devm_acpi_dev_release_driver_gpios(struct device *dev, void *res)
+void acpi_dev_remove_driver_gpios(struct acpi_device *adev)
+{
+ if (adev)
+ adev->driver_gpios = NULL;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_remove_driver_gpios);
+
+static void acpi_dev_release_driver_gpios(void *adev)
{
- acpi_dev_remove_driver_gpios(ACPI_COMPANION(dev));
+ acpi_dev_remove_driver_gpios(adev);
}
int devm_acpi_dev_add_driver_gpios(struct device *dev,
const struct acpi_gpio_mapping *gpios)
{
- void *res;
+ struct acpi_device *adev = ACPI_COMPANION(dev);
int ret;
- res = devres_alloc(devm_acpi_dev_release_driver_gpios, 0, GFP_KERNEL);
- if (!res)
- return -ENOMEM;
-
- ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), gpios);
- if (ret) {
- devres_free(res);
+ ret = acpi_dev_add_driver_gpios(adev, gpios);
+ if (ret)
return ret;
- }
- devres_add(dev, res);
- return 0;
-}
-EXPORT_SYMBOL_GPL(devm_acpi_dev_add_driver_gpios);
-void devm_acpi_dev_remove_driver_gpios(struct device *dev)
-{
- WARN_ON(devres_release(dev, devm_acpi_dev_release_driver_gpios, NULL, NULL));
+ return devm_add_action_or_reset(dev, acpi_dev_release_driver_gpios, adev);
}
-EXPORT_SYMBOL_GPL(devm_acpi_dev_remove_driver_gpios);
+EXPORT_SYMBOL_GPL(devm_acpi_dev_add_driver_gpios);
static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
const char *name, int index,
@@ -440,39 +626,6 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
return false;
}
-static enum gpiod_flags
-acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio)
-{
- switch (agpio->io_restriction) {
- case ACPI_IO_RESTRICT_INPUT:
- return GPIOD_IN;
- case ACPI_IO_RESTRICT_OUTPUT:
- /*
- * ACPI GPIO resources don't contain an initial value for the
- * GPIO. Therefore we deduce that value from the pull field
- * instead. If the pin is pulled up we assume default to be
- * high, if it is pulled down we assume default to be low,
- * otherwise we leave pin untouched.
- */
- switch (agpio->pin_config) {
- case ACPI_PIN_CONFIG_PULLUP:
- return GPIOD_OUT_HIGH;
- case ACPI_PIN_CONFIG_PULLDOWN:
- return GPIOD_OUT_LOW;
- default:
- break;
- }
- default:
- break;
- }
-
- /*
- * Assume that the BIOS has configured the direction and pull
- * accordingly.
- */
- return GPIOD_ASIS;
-}
-
static int
__acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
{
@@ -547,7 +700,7 @@ int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
struct acpi_gpio_lookup {
struct acpi_gpio_info info;
int index;
- int pin_index;
+ u16 pin_index;
bool active_low;
struct gpio_desc *desc;
int n;
@@ -563,7 +716,8 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
if (!lookup->desc) {
const struct acpi_resource_gpio *agpio = &ares->data.gpio;
bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
- int pin_index;
+ struct gpio_desc *desc;
+ u16 pin_index;
if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint)
lookup->index++;
@@ -575,9 +729,14 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
if (pin_index >= agpio->pin_table_length)
return 1;
- lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
+ if (lookup->info.quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER)
+ desc = gpio_to_desc(agpio->pin_table[pin_index]);
+ else
+ desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
agpio->pin_table[pin_index]);
+ lookup->desc = desc;
lookup->info.pin_config = agpio->pin_config;
+ lookup->info.debounce = agpio->debounce_timeout;
lookup->info.gpioint = gpioint;
/*
@@ -588,13 +747,13 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
* - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
*/
if (lookup->info.gpioint) {
- lookup->info.flags = GPIOD_IN;
lookup->info.polarity = agpio->polarity;
lookup->info.triggering = agpio->triggering;
} else {
- lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio);
lookup->info.polarity = lookup->active_low;
}
+
+ lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity);
}
return 1;
@@ -708,7 +867,7 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
if (ret)
return ERR_PTR(ret);
- dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %d %u\n",
+ dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %u %u\n",
dev_name(&lookup.info.adev->dev), lookup.index,
lookup.pin_index, lookup.active_low);
} else {
@@ -720,6 +879,16 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
return ret ? ERR_PTR(ret) : lookup.desc;
}
+static bool acpi_can_fallback_to_crs(struct acpi_device *adev,
+ const char *con_id)
+{
+ /* Never allow fallback if the device has properties */
+ if (acpi_dev_has_props(adev) || adev->driver_gpios)
+ return false;
+
+ return con_id == NULL;
+}
+
struct gpio_desc *acpi_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
@@ -761,7 +930,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
if (info.gpioint &&
(*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) {
- dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
+ dev_dbg(&adev->dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
return ERR_PTR(-ENOENT);
}
@@ -815,8 +984,9 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
}
/**
- * acpi_dev_gpio_irq_get() - Find GpioInt and translate it to Linux IRQ number
+ * acpi_dev_gpio_irq_get_by() - Find GpioInt and translate it to Linux IRQ number
* @adev: pointer to a ACPI device to get IRQ from
+ * @name: optional name of GpioInt resource
* @index: index of GpioInt resource (starting from %0)
*
* If the device has one or more GpioInt resources, this function can be
@@ -826,9 +996,12 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
* The function is idempotent, though each time it runs it will configure GPIO
* pin direction according to the flags in GpioInt resource.
*
+ * The function takes optional @name parameter. If the resource has a property
+ * name, then only those will be taken into account.
+ *
* Return: Linux IRQ number (> %0) on success, negative errno on failure.
*/
-int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
+int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index)
{
int idx, i;
unsigned int irq_flags;
@@ -838,7 +1011,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
struct acpi_gpio_info info;
struct gpio_desc *desc;
- desc = acpi_get_gpiod_by_index(adev, NULL, i, &info);
+ desc = acpi_get_gpiod_by_index(adev, name, i, &info);
/* Ignore -EPROBE_DEFER, it only matters if idx matches */
if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER)
@@ -846,6 +1019,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
if (info.gpioint && idx++ == index) {
unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
+ enum gpiod_flags dflags = GPIOD_ASIS;
char label[32];
int irq;
@@ -856,18 +1030,33 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
if (irq < 0)
return irq;
+ acpi_gpio_update_gpiod_flags(&dflags, &info);
+ acpi_gpio_update_gpiod_lookup_flags(&lflags, &info);
+
snprintf(label, sizeof(label), "GpioInt() %d", index);
- ret = gpiod_configure_flags(desc, label, lflags, info.flags);
+ ret = gpiod_configure_flags(desc, label, lflags, dflags);
if (ret < 0)
return ret;
+ /* ACPI uses hundredths of milliseconds units */
+ ret = gpio_set_debounce_timeout(desc, info.debounce * 10);
+ if (ret)
+ return ret;
+
irq_flags = acpi_dev_get_irq_type(info.triggering,
info.polarity);
- /* Set type if specified and different than the current one */
- if (irq_flags != IRQ_TYPE_NONE &&
- irq_flags != irq_get_trigger_type(irq))
- irq_set_irq_type(irq, irq_flags);
+ /*
+ * If the IRQ is not already in use then set type
+ * if specified and different than the current one.
+ */
+ if (can_request_irq(irq, irq_flags)) {
+ if (irq_flags != IRQ_TYPE_NONE &&
+ irq_flags != irq_get_trigger_type(irq))
+ irq_set_irq_type(irq, irq_flags);
+ } else {
+ dev_dbg(&adev->dev, "IRQ %d already in use\n", irq);
+ }
return irq;
}
@@ -875,7 +1064,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
}
return -ENOENT;
}
-EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get);
+EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get_by);
static acpi_status
acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
@@ -886,7 +1075,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
struct gpio_chip *chip = achip->chip;
struct acpi_resource_gpio *agpio;
struct acpi_resource *ares;
- int pin_index = (int)address;
+ u16 pin_index = address;
acpi_status status;
int length;
int i;
@@ -909,9 +1098,9 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
return AE_BAD_PARAMETER;
}
- length = min(agpio->pin_table_length, (u16)(pin_index + bits));
+ length = min_t(u16, agpio->pin_table_length, pin_index + bits);
for (i = pin_index; i < length; ++i) {
- int pin = agpio->pin_table[i];
+ unsigned int pin = agpio->pin_table[i];
struct acpi_gpio_connection *conn;
struct gpio_desc *desc;
bool found;
@@ -946,23 +1135,18 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
}
if (!found) {
- enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio);
- const char *label = "ACPI:OpRegion";
-
- desc = gpiochip_request_own_desc(chip, pin, label,
- GPIO_ACTIVE_HIGH,
- flags);
+ desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion");
if (IS_ERR(desc)) {
- status = AE_ERROR;
mutex_unlock(&achip->conn_lock);
+ status = AE_ERROR;
goto out;
}
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn) {
- status = AE_NO_MEMORY;
gpiochip_free_own_desc(desc);
mutex_unlock(&achip->conn_lock);
+ status = AE_NO_MEMORY;
goto out;
}
@@ -974,8 +1158,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
mutex_unlock(&achip->conn_lock);
if (function == ACPI_WRITE)
- gpiod_set_raw_value_cansleep(desc,
- !!((1 << i) & *value));
+ gpiod_set_raw_value_cansleep(desc, !!(*value & BIT(i)));
else
*value |= (u64)gpiod_get_raw_value_cansleep(desc) << i;
}
@@ -1036,7 +1219,7 @@ acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip,
int ret;
*lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- *dflags = 0;
+ *dflags = GPIOD_ASIS;
*name = NULL;
ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios,
@@ -1097,14 +1280,14 @@ static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip)
void acpi_gpiochip_add(struct gpio_chip *chip)
{
struct acpi_gpio_chip *acpi_gpio;
- acpi_handle handle;
+ struct acpi_device *adev;
acpi_status status;
if (!chip || !chip->parent)
return;
- handle = ACPI_HANDLE(chip->parent);
- if (!handle)
+ adev = ACPI_COMPANION(chip->parent);
+ if (!adev)
return;
acpi_gpio = kzalloc(sizeof(*acpi_gpio), GFP_KERNEL);
@@ -1118,19 +1301,16 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
INIT_LIST_HEAD(&acpi_gpio->events);
INIT_LIST_HEAD(&acpi_gpio->deferred_req_irqs_list_entry);
- status = acpi_attach_data(handle, acpi_gpio_chip_dh, acpi_gpio);
+ status = acpi_attach_data(adev->handle, acpi_gpio_chip_dh, acpi_gpio);
if (ACPI_FAILURE(status)) {
dev_err(chip->parent, "Failed to attach ACPI GPIO chip\n");
kfree(acpi_gpio);
return;
}
- if (!chip->names)
- devprop_gpiochip_set_names(chip, dev_fwnode(chip->parent));
-
acpi_gpiochip_request_regions(acpi_gpio);
acpi_gpiochip_scan_gpios(acpi_gpio);
- acpi_walk_dep_device_list(handle);
+ acpi_dev_clear_dependencies(adev);
}
void acpi_gpiochip_remove(struct gpio_chip *chip)
@@ -1158,6 +1338,16 @@ void acpi_gpiochip_remove(struct gpio_chip *chip)
kfree(acpi_gpio);
}
+void acpi_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev)
+{
+ /* Set default fwnode to parent's one if present */
+ if (gc->parent)
+ ACPI_COMPANION_SET(&gdev->dev, ACPI_COMPANION(gc->parent));
+
+ if (gc->fwnode)
+ device_set_node(&gdev->dev, gc->fwnode);
+}
+
static int acpi_gpio_package_count(const union acpi_object *obj)
{
const union acpi_object *element = obj->package.elements;
@@ -1168,7 +1358,7 @@ static int acpi_gpio_package_count(const union acpi_object *obj)
switch (element->type) {
case ACPI_TYPE_LOCAL_REFERENCE:
element += 3;
- /* Fallthrough */
+ fallthrough;
case ACPI_TYPE_INTEGER:
element++;
count++;
@@ -1256,17 +1446,8 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
return count ? count : -ENOENT;
}
-bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
-{
- /* Never allow fallback if the device has properties */
- if (acpi_dev_has_props(adev) || adev->driver_gpios)
- return false;
-
- return con_id == NULL;
-}
-
/* Run deferred acpi_gpiochip_request_irqs() */
-static int acpi_gpio_handle_deferred_request_irqs(void)
+static int __init acpi_gpio_handle_deferred_request_irqs(void)
{
struct acpi_gpio_chip *acpi_gpio, *tmp;
@@ -1283,3 +1464,126 @@ static int acpi_gpio_handle_deferred_request_irqs(void)
}
/* We must use _sync so that this runs after the first deferred_probe run */
late_initcall_sync(acpi_gpio_handle_deferred_request_irqs);
+
+static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
+ {
+ /*
+ * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for
+ * a non existing micro-USB-B connector which puts the HDMI
+ * DDC pins in GPIO mode, breaking HDMI support.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MINIX"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .no_edge_events_on_boot = true,
+ },
+ },
+ {
+ /*
+ * The Terra Pad 1061 has a micro-USB-B id-pin handler, which
+ * instead of controlling the actual micro-USB-B turns the 5V
+ * boost for its USB-A connector off. The actual micro-USB-B
+ * connector is wired for charging only.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .no_edge_events_on_boot = true,
+ },
+ },
+ {
+ /*
+ * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an
+ * external embedded-controller connected via I2C + an ACPI GPIO
+ * event handler on INT33FFC:02 pin 12, causing spurious wakeups.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_wake = "INT33FC:02@12",
+ },
+ },
+ {
+ /*
+ * HP X2 10 models with Cherry Trail SoC + TI PMIC use an
+ * external embedded-controller connected via I2C + an ACPI GPIO
+ * event handler on INT33FF:01 pin 0, causing spurious wakeups.
+ * When suspending by closing the LID, the power to the USB
+ * keyboard is turned off, causing INT0002 ACPI events to
+ * trigger once the XHCI controller notices the keyboard is
+ * gone. So INT0002 events cause spurious wakeups too. Ignoring
+ * EC wakes breaks wakeup when opening the lid, the user needs
+ * to press the power-button to wakeup the system. The
+ * alternative is suspend simply not working, which is worse.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_wake = "INT33FF:01@0,INT0002:00@2",
+ },
+ },
+ {
+ /*
+ * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an
+ * external embedded-controller connected via I2C + an ACPI GPIO
+ * event handler on INT33FC:02 pin 28, causing spurious wakeups.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
+ DMI_MATCH(DMI_BOARD_NAME, "815D"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_wake = "INT33FC:02@28",
+ },
+ },
+ {
+ /*
+ * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an
+ * external embedded-controller connected via I2C + an ACPI GPIO
+ * event handler on INT33FF:01 pin 0, causing spurious wakeups.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
+ DMI_MATCH(DMI_BOARD_NAME, "813E"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_wake = "INT33FF:01@0",
+ },
+ },
+ {} /* Terminating entry */
+};
+
+static int __init acpi_gpio_setup_params(void)
+{
+ const struct acpi_gpiolib_dmi_quirk *quirk = NULL;
+ const struct dmi_system_id *id;
+
+ id = dmi_first_match(gpiolib_acpi_quirks);
+ if (id)
+ quirk = id->driver_data;
+
+ if (run_edge_events_on_boot < 0) {
+ if (quirk && quirk->no_edge_events_on_boot)
+ run_edge_events_on_boot = 0;
+ else
+ run_edge_events_on_boot = 1;
+ }
+
+ if (ignore_wake == NULL && quirk && quirk->ignore_wake)
+ ignore_wake = quirk->ignore_wake;
+
+ return 0;
+}
+
+/* Directly after dmi_setup() which runs as core_initcall() */
+postcore_initcall(acpi_gpio_setup_params);
diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h
new file mode 100644
index 000000000000..e476558d9471
--- /dev/null
+++ b/drivers/gpio/gpiolib-acpi.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ACPI helpers for GPIO API
+ *
+ * Copyright (C) 2012,2019 Intel Corporation
+ */
+
+#ifndef GPIOLIB_ACPI_H
+#define GPIOLIB_ACPI_H
+
+struct acpi_device;
+
+/**
+ * struct acpi_gpio_info - ACPI GPIO specific information
+ * @adev: reference to ACPI device which consumes GPIO resource
+ * @flags: GPIO initialization flags
+ * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
+ * @pin_config: pin bias as provided by ACPI
+ * @polarity: interrupt polarity as provided by ACPI
+ * @triggering: triggering type as provided by ACPI
+ * @debounce: debounce timeout as provided by ACPI
+ * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
+ */
+struct acpi_gpio_info {
+ struct acpi_device *adev;
+ enum gpiod_flags flags;
+ bool gpioint;
+ int pin_config;
+ int polarity;
+ int triggering;
+ unsigned int debounce;
+ unsigned int quirks;
+};
+
+#ifdef CONFIG_ACPI
+void acpi_gpiochip_add(struct gpio_chip *chip);
+void acpi_gpiochip_remove(struct gpio_chip *chip);
+
+void acpi_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev);
+
+void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
+void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
+
+int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
+ struct acpi_gpio_info *info);
+int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
+ struct acpi_gpio_info *info);
+
+struct gpio_desc *acpi_find_gpio(struct device *dev,
+ const char *con_id,
+ unsigned int idx,
+ enum gpiod_flags *dflags,
+ unsigned long *lookupflags);
+struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
+ const char *propname, int index,
+ struct acpi_gpio_info *info);
+
+int acpi_gpio_count(struct device *dev, const char *con_id);
+#else
+static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
+static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
+
+static inline void acpi_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev) { }
+
+static inline void
+acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
+
+static inline void
+acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
+
+static inline int
+acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info)
+{
+ return 0;
+}
+static inline int
+acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
+ struct acpi_gpio_info *info)
+{
+ return 0;
+}
+
+static inline struct gpio_desc *
+acpi_find_gpio(struct device *dev, const char *con_id,
+ unsigned int idx, enum gpiod_flags *dflags,
+ unsigned long *lookupflags)
+{
+ return ERR_PTR(-ENOENT);
+}
+static inline struct gpio_desc *
+acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname,
+ int index, struct acpi_gpio_info *info)
+{
+ return ERR_PTR(-ENXIO);
+}
+static inline int acpi_gpio_count(struct device *dev, const char *con_id)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif /* GPIOLIB_ACPI_H */
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
new file mode 100644
index 000000000000..f5aa5f93342a
--- /dev/null
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -0,0 +1,2568 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/anon_inodes.h>
+#include <linux/atomic.h>
+#include <linux/bitmap.h>
+#include <linux/build_bug.h>
+#include <linux/cdev.h>
+#include <linux/compat.h>
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/file.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/timekeeping.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/hte.h>
+#include <uapi/linux/gpio.h>
+
+#include "gpiolib.h"
+#include "gpiolib-cdev.h"
+
+/*
+ * Array sizes must ensure 64-bit alignment and not create holes in the
+ * struct packing.
+ */
+static_assert(IS_ALIGNED(GPIO_V2_LINES_MAX, 2));
+static_assert(IS_ALIGNED(GPIO_MAX_NAME_SIZE, 8));
+
+/*
+ * Check that uAPI structs are 64-bit aligned for 32/64-bit compatibility
+ */
+static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_attribute), 8));
+static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_config_attribute), 8));
+static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_config), 8));
+static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_request), 8));
+static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_info), 8));
+static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_info_changed), 8));
+static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_event), 8));
+static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_values), 8));
+
+/* Character device interface to GPIO.
+ *
+ * The GPIO character device, /dev/gpiochipN, provides userspace an
+ * interface to gpiolib GPIOs via ioctl()s.
+ */
+
+/*
+ * GPIO line handle management
+ */
+
+#ifdef CONFIG_GPIO_CDEV_V1
+/**
+ * struct linehandle_state - contains the state of a userspace handle
+ * @gdev: the GPIO device the handle pertains to
+ * @label: consumer label used to tag descriptors
+ * @descs: the GPIO descriptors held by this handle
+ * @num_descs: the number of descriptors held in the descs array
+ */
+struct linehandle_state {
+ struct gpio_device *gdev;
+ const char *label;
+ struct gpio_desc *descs[GPIOHANDLES_MAX];
+ u32 num_descs;
+};
+
+#define GPIOHANDLE_REQUEST_VALID_FLAGS \
+ (GPIOHANDLE_REQUEST_INPUT | \
+ GPIOHANDLE_REQUEST_OUTPUT | \
+ GPIOHANDLE_REQUEST_ACTIVE_LOW | \
+ GPIOHANDLE_REQUEST_BIAS_PULL_UP | \
+ GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \
+ GPIOHANDLE_REQUEST_BIAS_DISABLE | \
+ GPIOHANDLE_REQUEST_OPEN_DRAIN | \
+ GPIOHANDLE_REQUEST_OPEN_SOURCE)
+
+static int linehandle_validate_flags(u32 flags)
+{
+ /* Return an error if an unknown flag is set */
+ if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS)
+ return -EINVAL;
+
+ /*
+ * Do not allow both INPUT & OUTPUT flags to be set as they are
+ * contradictory.
+ */
+ if ((flags & GPIOHANDLE_REQUEST_INPUT) &&
+ (flags & GPIOHANDLE_REQUEST_OUTPUT))
+ return -EINVAL;
+
+ /*
+ * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If
+ * the hardware actually supports enabling both at the same time the
+ * electrical result would be disastrous.
+ */
+ if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) &&
+ (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))
+ return -EINVAL;
+
+ /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */
+ if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) &&
+ ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
+ (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)))
+ return -EINVAL;
+
+ /* Bias flags only allowed for input or output mode. */
+ if (!((flags & GPIOHANDLE_REQUEST_INPUT) ||
+ (flags & GPIOHANDLE_REQUEST_OUTPUT)) &&
+ ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) ||
+ (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) ||
+ (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN)))
+ return -EINVAL;
+
+ /* Only one bias flag can be set. */
+ if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) &&
+ (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN |
+ GPIOHANDLE_REQUEST_BIAS_PULL_UP))) ||
+ ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) &&
+ (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void linehandle_flags_to_desc_flags(u32 lflags, unsigned long *flagsp)
+{
+ assign_bit(FLAG_ACTIVE_LOW, flagsp,
+ lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW);
+ assign_bit(FLAG_OPEN_DRAIN, flagsp,
+ lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN);
+ assign_bit(FLAG_OPEN_SOURCE, flagsp,
+ lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE);
+ assign_bit(FLAG_PULL_UP, flagsp,
+ lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP);
+ assign_bit(FLAG_PULL_DOWN, flagsp,
+ lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN);
+ assign_bit(FLAG_BIAS_DISABLE, flagsp,
+ lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE);
+}
+
+static long linehandle_set_config(struct linehandle_state *lh,
+ void __user *ip)
+{
+ struct gpiohandle_config gcnf;
+ struct gpio_desc *desc;
+ int i, ret;
+ u32 lflags;
+
+ if (copy_from_user(&gcnf, ip, sizeof(gcnf)))
+ return -EFAULT;
+
+ lflags = gcnf.flags;
+ ret = linehandle_validate_flags(lflags);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < lh->num_descs; i++) {
+ desc = lh->descs[i];
+ linehandle_flags_to_desc_flags(gcnf.flags, &desc->flags);
+
+ /*
+ * Lines have to be requested explicitly for input
+ * or output, else the line will be treated "as is".
+ */
+ if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
+ int val = !!gcnf.default_values[i];
+
+ ret = gpiod_direction_output(desc, val);
+ if (ret)
+ return ret;
+ } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ return ret;
+ }
+
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIO_V2_LINE_CHANGED_CONFIG,
+ desc);
+ }
+ return 0;
+}
+
+static long linehandle_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct linehandle_state *lh = file->private_data;
+ void __user *ip = (void __user *)arg;
+ struct gpiohandle_data ghd;
+ DECLARE_BITMAP(vals, GPIOHANDLES_MAX);
+ unsigned int i;
+ int ret;
+
+ switch (cmd) {
+ case GPIOHANDLE_GET_LINE_VALUES_IOCTL:
+ /* NOTE: It's okay to read values of output lines */
+ ret = gpiod_get_array_value_complex(false, true,
+ lh->num_descs, lh->descs,
+ NULL, vals);
+ if (ret)
+ return ret;
+
+ memset(&ghd, 0, sizeof(ghd));
+ for (i = 0; i < lh->num_descs; i++)
+ ghd.values[i] = test_bit(i, vals);
+
+ if (copy_to_user(ip, &ghd, sizeof(ghd)))
+ return -EFAULT;
+
+ return 0;
+ case GPIOHANDLE_SET_LINE_VALUES_IOCTL:
+ /*
+ * All line descriptors were created at once with the same
+ * flags so just check if the first one is really output.
+ */
+ if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags))
+ return -EPERM;
+
+ if (copy_from_user(&ghd, ip, sizeof(ghd)))
+ return -EFAULT;
+
+ /* Clamp all values to [0,1] */
+ for (i = 0; i < lh->num_descs; i++)
+ __assign_bit(i, vals, ghd.values[i]);
+
+ /* Reuse the array setting function */
+ return gpiod_set_array_value_complex(false,
+ true,
+ lh->num_descs,
+ lh->descs,
+ NULL,
+ vals);
+ case GPIOHANDLE_SET_CONFIG_IOCTL:
+ return linehandle_set_config(lh, ip);
+ default:
+ return -EINVAL;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long linehandle_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return linehandle_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static void linehandle_free(struct linehandle_state *lh)
+{
+ int i;
+
+ for (i = 0; i < lh->num_descs; i++)
+ if (lh->descs[i])
+ gpiod_free(lh->descs[i]);
+ kfree(lh->label);
+ put_device(&lh->gdev->dev);
+ kfree(lh);
+}
+
+static int linehandle_release(struct inode *inode, struct file *file)
+{
+ linehandle_free(file->private_data);
+ return 0;
+}
+
+static const struct file_operations linehandle_fileops = {
+ .release = linehandle_release,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = linehandle_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = linehandle_ioctl_compat,
+#endif
+};
+
+static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+{
+ struct gpiohandle_request handlereq;
+ struct linehandle_state *lh;
+ struct file *file;
+ int fd, i, ret;
+ u32 lflags;
+
+ if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
+ return -EFAULT;
+ if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX))
+ return -EINVAL;
+
+ lflags = handlereq.flags;
+
+ ret = linehandle_validate_flags(lflags);
+ if (ret)
+ return ret;
+
+ lh = kzalloc(sizeof(*lh), GFP_KERNEL);
+ if (!lh)
+ return -ENOMEM;
+ lh->gdev = gdev;
+ get_device(&gdev->dev);
+
+ if (handlereq.consumer_label[0] != '\0') {
+ /* label is only initialized if consumer_label is set */
+ lh->label = kstrndup(handlereq.consumer_label,
+ sizeof(handlereq.consumer_label) - 1,
+ GFP_KERNEL);
+ if (!lh->label) {
+ ret = -ENOMEM;
+ goto out_free_lh;
+ }
+ }
+
+ lh->num_descs = handlereq.lines;
+
+ /* Request each GPIO */
+ for (i = 0; i < handlereq.lines; i++) {
+ u32 offset = handlereq.lineoffsets[i];
+ struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
+
+ if (IS_ERR(desc)) {
+ ret = PTR_ERR(desc);
+ goto out_free_lh;
+ }
+
+ ret = gpiod_request_user(desc, lh->label);
+ if (ret)
+ goto out_free_lh;
+ lh->descs[i] = desc;
+ linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags);
+
+ ret = gpiod_set_transitory(desc, false);
+ if (ret < 0)
+ goto out_free_lh;
+
+ /*
+ * Lines have to be requested explicitly for input
+ * or output, else the line will be treated "as is".
+ */
+ if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
+ int val = !!handlereq.default_values[i];
+
+ ret = gpiod_direction_output(desc, val);
+ if (ret)
+ goto out_free_lh;
+ } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ goto out_free_lh;
+ }
+
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIO_V2_LINE_CHANGED_REQUESTED, desc);
+
+ dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
+ offset);
+ }
+
+ fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ ret = fd;
+ goto out_free_lh;
+ }
+
+ file = anon_inode_getfile("gpio-linehandle",
+ &linehandle_fileops,
+ lh,
+ O_RDONLY | O_CLOEXEC);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto out_put_unused_fd;
+ }
+
+ handlereq.fd = fd;
+ if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
+ /*
+ * fput() will trigger the release() callback, so do not go onto
+ * the regular error cleanup path here.
+ */
+ fput(file);
+ put_unused_fd(fd);
+ return -EFAULT;
+ }
+
+ fd_install(fd, file);
+
+ dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
+ lh->num_descs);
+
+ return 0;
+
+out_put_unused_fd:
+ put_unused_fd(fd);
+out_free_lh:
+ linehandle_free(lh);
+ return ret;
+}
+#endif /* CONFIG_GPIO_CDEV_V1 */
+
+/**
+ * struct line - contains the state of a requested line
+ * @desc: the GPIO descriptor for this line.
+ * @req: the corresponding line request
+ * @irq: the interrupt triggered in response to events on this GPIO
+ * @eflags: the edge flags, GPIO_V2_LINE_FLAG_EDGE_RISING and/or
+ * GPIO_V2_LINE_FLAG_EDGE_FALLING, indicating the edge detection applied
+ * @timestamp_ns: cache for the timestamp storing it between hardirq and
+ * IRQ thread, used to bring the timestamp close to the actual event
+ * @req_seqno: the seqno for the current edge event in the sequence of
+ * events for the corresponding line request. This is drawn from the @req.
+ * @line_seqno: the seqno for the current edge event in the sequence of
+ * events for this line.
+ * @work: the worker that implements software debouncing
+ * @sw_debounced: flag indicating if the software debouncer is active
+ * @level: the current debounced physical level of the line
+ */
+struct line {
+ struct gpio_desc *desc;
+ /*
+ * -- edge detector specific fields --
+ */
+ struct linereq *req;
+ unsigned int irq;
+ /*
+ * eflags is set by edge_detector_setup(), edge_detector_stop() and
+ * edge_detector_update(), which are themselves mutually exclusive,
+ * and is accessed by edge_irq_thread() and debounce_work_func(),
+ * which can both live with a slightly stale value.
+ */
+ u64 eflags;
+ /*
+ * timestamp_ns and req_seqno are accessed only by
+ * edge_irq_handler() and edge_irq_thread(), which are themselves
+ * mutually exclusive, so no additional protection is necessary.
+ */
+ u64 timestamp_ns;
+ u32 req_seqno;
+ /*
+ * line_seqno is accessed by either edge_irq_thread() or
+ * debounce_work_func(), which are themselves mutually exclusive,
+ * so no additional protection is necessary.
+ */
+ u32 line_seqno;
+ /*
+ * -- debouncer specific fields --
+ */
+ struct delayed_work work;
+ /*
+ * sw_debounce is accessed by linereq_set_config(), which is the
+ * only setter, and linereq_get_values(), which can live with a
+ * slightly stale value.
+ */
+ unsigned int sw_debounced;
+ /*
+ * level is accessed by debounce_work_func(), which is the only
+ * setter, and linereq_get_values() which can live with a slightly
+ * stale value.
+ */
+ unsigned int level;
+ /*
+ * -- hte specific fields --
+ */
+ struct hte_ts_desc hdesc;
+ /*
+ * HTE provider sets line level at the time of event. The valid
+ * value is 0 or 1 and negative value for an error.
+ */
+ int raw_level;
+ /*
+ * when sw_debounce is set on HTE enabled line, this is running
+ * counter of the discarded events.
+ */
+ u32 total_discard_seq;
+ /*
+ * when sw_debounce is set on HTE enabled line, this variable records
+ * last sequence number before debounce period expires.
+ */
+ u32 last_seqno;
+};
+
+/**
+ * struct linereq - contains the state of a userspace line request
+ * @gdev: the GPIO device the line request pertains to
+ * @label: consumer label used to tag GPIO descriptors
+ * @num_lines: the number of lines in the lines array
+ * @wait: wait queue that handles blocking reads of events
+ * @event_buffer_size: the number of elements allocated in @events
+ * @events: KFIFO for the GPIO events
+ * @seqno: the sequence number for edge events generated on all lines in
+ * this line request. Note that this is not used when @num_lines is 1, as
+ * the line_seqno is then the same and is cheaper to calculate.
+ * @config_mutex: mutex for serializing ioctl() calls to ensure consistency
+ * of configuration, particularly multi-step accesses to desc flags.
+ * @lines: the lines held by this line request, with @num_lines elements.
+ */
+struct linereq {
+ struct gpio_device *gdev;
+ const char *label;
+ u32 num_lines;
+ wait_queue_head_t wait;
+ u32 event_buffer_size;
+ DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event);
+ atomic_t seqno;
+ struct mutex config_mutex;
+ struct line lines[];
+};
+
+#define GPIO_V2_LINE_BIAS_FLAGS \
+ (GPIO_V2_LINE_FLAG_BIAS_PULL_UP | \
+ GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN | \
+ GPIO_V2_LINE_FLAG_BIAS_DISABLED)
+
+#define GPIO_V2_LINE_DIRECTION_FLAGS \
+ (GPIO_V2_LINE_FLAG_INPUT | \
+ GPIO_V2_LINE_FLAG_OUTPUT)
+
+#define GPIO_V2_LINE_DRIVE_FLAGS \
+ (GPIO_V2_LINE_FLAG_OPEN_DRAIN | \
+ GPIO_V2_LINE_FLAG_OPEN_SOURCE)
+
+#define GPIO_V2_LINE_EDGE_FLAGS \
+ (GPIO_V2_LINE_FLAG_EDGE_RISING | \
+ GPIO_V2_LINE_FLAG_EDGE_FALLING)
+
+#define GPIO_V2_LINE_FLAG_EDGE_BOTH GPIO_V2_LINE_EDGE_FLAGS
+
+#define GPIO_V2_LINE_VALID_FLAGS \
+ (GPIO_V2_LINE_FLAG_ACTIVE_LOW | \
+ GPIO_V2_LINE_DIRECTION_FLAGS | \
+ GPIO_V2_LINE_DRIVE_FLAGS | \
+ GPIO_V2_LINE_EDGE_FLAGS | \
+ GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME | \
+ GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \
+ GPIO_V2_LINE_BIAS_FLAGS)
+
+static void linereq_put_event(struct linereq *lr,
+ struct gpio_v2_line_event *le)
+{
+ bool overflow = false;
+
+ spin_lock(&lr->wait.lock);
+ if (kfifo_is_full(&lr->events)) {
+ overflow = true;
+ kfifo_skip(&lr->events);
+ }
+ kfifo_in(&lr->events, le, 1);
+ spin_unlock(&lr->wait.lock);
+ if (!overflow)
+ wake_up_poll(&lr->wait, EPOLLIN);
+ else
+ pr_debug_ratelimited("event FIFO is full - event dropped\n");
+}
+
+static u64 line_event_timestamp(struct line *line)
+{
+ if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags))
+ return ktime_get_real_ns();
+ else if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))
+ return line->timestamp_ns;
+
+ return ktime_get_ns();
+}
+
+static enum hte_return process_hw_ts_thread(void *p)
+{
+ struct line *line;
+ struct linereq *lr;
+ struct gpio_v2_line_event le;
+ int level;
+ u64 eflags;
+
+ if (!p)
+ return HTE_CB_HANDLED;
+
+ line = p;
+ lr = line->req;
+
+ memset(&le, 0, sizeof(le));
+
+ le.timestamp_ns = line->timestamp_ns;
+ eflags = READ_ONCE(line->eflags);
+
+ if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) {
+ if (line->raw_level >= 0) {
+ if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags))
+ level = !line->raw_level;
+ else
+ level = line->raw_level;
+ } else {
+ level = gpiod_get_value_cansleep(line->desc);
+ }
+
+ if (level)
+ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
+ else
+ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
+ } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) {
+ /* Emit low-to-high event */
+ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
+ } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) {
+ /* Emit high-to-low event */
+ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
+ } else {
+ return HTE_CB_HANDLED;
+ }
+ le.line_seqno = line->line_seqno;
+ le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
+ le.offset = gpio_chip_hwgpio(line->desc);
+
+ linereq_put_event(lr, &le);
+
+ return HTE_CB_HANDLED;
+}
+
+static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
+{
+ struct line *line;
+ struct linereq *lr;
+ int diff_seqno = 0;
+
+ if (!ts || !p)
+ return HTE_CB_HANDLED;
+
+ line = p;
+ line->timestamp_ns = ts->tsc;
+ line->raw_level = ts->raw_level;
+ lr = line->req;
+
+ if (READ_ONCE(line->sw_debounced)) {
+ line->total_discard_seq++;
+ line->last_seqno = ts->seq;
+ mod_delayed_work(system_wq, &line->work,
+ usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
+ } else {
+ if (unlikely(ts->seq < line->line_seqno))
+ return HTE_CB_HANDLED;
+
+ diff_seqno = ts->seq - line->line_seqno;
+ line->line_seqno = ts->seq;
+ if (lr->num_lines != 1)
+ line->req_seqno = atomic_add_return(diff_seqno,
+ &lr->seqno);
+
+ return HTE_RUN_SECOND_CB;
+ }
+
+ return HTE_CB_HANDLED;
+}
+
+static irqreturn_t edge_irq_thread(int irq, void *p)
+{
+ struct line *line = p;
+ struct linereq *lr = line->req;
+ struct gpio_v2_line_event le;
+ u64 eflags;
+
+ /* Do not leak kernel stack to userspace */
+ memset(&le, 0, sizeof(le));
+
+ if (line->timestamp_ns) {
+ le.timestamp_ns = line->timestamp_ns;
+ } else {
+ /*
+ * We may be running from a nested threaded interrupt in
+ * which case we didn't get the timestamp from
+ * edge_irq_handler().
+ */
+ le.timestamp_ns = line_event_timestamp(line);
+ if (lr->num_lines != 1)
+ line->req_seqno = atomic_inc_return(&lr->seqno);
+ }
+ line->timestamp_ns = 0;
+
+ eflags = READ_ONCE(line->eflags);
+ if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) {
+ int level = gpiod_get_value_cansleep(line->desc);
+
+ if (level)
+ /* Emit low-to-high event */
+ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
+ else
+ /* Emit high-to-low event */
+ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
+ } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) {
+ /* Emit low-to-high event */
+ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
+ } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) {
+ /* Emit high-to-low event */
+ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
+ } else {
+ return IRQ_NONE;
+ }
+ line->line_seqno++;
+ le.line_seqno = line->line_seqno;
+ le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
+ le.offset = gpio_chip_hwgpio(line->desc);
+
+ linereq_put_event(lr, &le);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t edge_irq_handler(int irq, void *p)
+{
+ struct line *line = p;
+ struct linereq *lr = line->req;
+
+ /*
+ * Just store the timestamp in hardirq context so we get it as
+ * close in time as possible to the actual event.
+ */
+ line->timestamp_ns = line_event_timestamp(line);
+
+ if (lr->num_lines != 1)
+ line->req_seqno = atomic_inc_return(&lr->seqno);
+
+ return IRQ_WAKE_THREAD;
+}
+
+/*
+ * returns the current debounced logical value.
+ */
+static bool debounced_value(struct line *line)
+{
+ bool value;
+
+ /*
+ * minor race - debouncer may be stopped here, so edge_detector_stop()
+ * must leave the value unchanged so the following will read the level
+ * from when the debouncer was last running.
+ */
+ value = READ_ONCE(line->level);
+
+ if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags))
+ value = !value;
+
+ return value;
+}
+
+static irqreturn_t debounce_irq_handler(int irq, void *p)
+{
+ struct line *line = p;
+
+ mod_delayed_work(system_wq, &line->work,
+ usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
+
+ return IRQ_HANDLED;
+}
+
+static void debounce_work_func(struct work_struct *work)
+{
+ struct gpio_v2_line_event le;
+ struct line *line = container_of(work, struct line, work.work);
+ struct linereq *lr;
+ int level, diff_seqno;
+ u64 eflags;
+
+ if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) {
+ level = line->raw_level;
+ if (level < 0)
+ level = gpiod_get_raw_value_cansleep(line->desc);
+ } else {
+ level = gpiod_get_raw_value_cansleep(line->desc);
+ }
+ if (level < 0) {
+ pr_debug_ratelimited("debouncer failed to read line value\n");
+ return;
+ }
+
+ if (READ_ONCE(line->level) == level)
+ return;
+
+ WRITE_ONCE(line->level, level);
+
+ /* -- edge detection -- */
+ eflags = READ_ONCE(line->eflags);
+ if (!eflags)
+ return;
+
+ /* switch from physical level to logical - if they differ */
+ if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags))
+ level = !level;
+
+ /* ignore edges that are not being monitored */
+ if (((eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) && !level) ||
+ ((eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) && level))
+ return;
+
+ /* Do not leak kernel stack to userspace */
+ memset(&le, 0, sizeof(le));
+
+ lr = line->req;
+ le.timestamp_ns = line_event_timestamp(line);
+ le.offset = gpio_chip_hwgpio(line->desc);
+ if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) {
+ /* discard events except the last one */
+ line->total_discard_seq -= 1;
+ diff_seqno = line->last_seqno - line->total_discard_seq -
+ line->line_seqno;
+ line->line_seqno = line->last_seqno - line->total_discard_seq;
+ le.line_seqno = line->line_seqno;
+ le.seqno = (lr->num_lines == 1) ?
+ le.line_seqno : atomic_add_return(diff_seqno, &lr->seqno);
+ } else {
+ line->line_seqno++;
+ le.line_seqno = line->line_seqno;
+ le.seqno = (lr->num_lines == 1) ?
+ le.line_seqno : atomic_inc_return(&lr->seqno);
+ }
+
+ if (level)
+ /* Emit low-to-high event */
+ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
+ else
+ /* Emit high-to-low event */
+ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
+
+ linereq_put_event(lr, &le);
+}
+
+static int hte_edge_setup(struct line *line, u64 eflags)
+{
+ int ret;
+ unsigned long flags = 0;
+ struct hte_ts_desc *hdesc = &line->hdesc;
+
+ if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING)
+ flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
+ HTE_FALLING_EDGE_TS : HTE_RISING_EDGE_TS;
+ if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING)
+ flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
+ HTE_RISING_EDGE_TS : HTE_FALLING_EDGE_TS;
+
+ line->total_discard_seq = 0;
+
+ hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags,
+ NULL, line->desc);
+
+ ret = hte_ts_get(NULL, hdesc, 0);
+ if (ret)
+ return ret;
+
+ return hte_request_ts_ns(hdesc, process_hw_ts,
+ process_hw_ts_thread, line);
+}
+
+static int debounce_setup(struct line *line,
+ unsigned int debounce_period_us, bool hte_req)
+{
+ unsigned long irqflags;
+ int ret, level, irq;
+
+ /* try hardware */
+ ret = gpiod_set_debounce(line->desc, debounce_period_us);
+ if (!ret) {
+ WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
+ return ret;
+ }
+ if (ret != -ENOTSUPP)
+ return ret;
+
+ if (debounce_period_us) {
+ /* setup software debounce */
+ level = gpiod_get_raw_value_cansleep(line->desc);
+ if (level < 0)
+ return level;
+
+ if (!hte_req) {
+ irq = gpiod_to_irq(line->desc);
+ if (irq < 0)
+ return -ENXIO;
+
+ irqflags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
+ ret = request_irq(irq, debounce_irq_handler, irqflags,
+ line->req->label, line);
+ if (ret)
+ return ret;
+ line->irq = irq;
+ } else {
+ ret = hte_edge_setup(line,
+ GPIO_V2_LINE_FLAG_EDGE_RISING |
+ GPIO_V2_LINE_FLAG_EDGE_FALLING);
+ if (ret)
+ return ret;
+ }
+
+ WRITE_ONCE(line->level, level);
+ WRITE_ONCE(line->sw_debounced, 1);
+ }
+ return 0;
+}
+
+static bool gpio_v2_line_config_debounced(struct gpio_v2_line_config *lc,
+ unsigned int line_idx)
+{
+ unsigned int i;
+ u64 mask = BIT_ULL(line_idx);
+
+ for (i = 0; i < lc->num_attrs; i++) {
+ if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) &&
+ (lc->attrs[i].mask & mask))
+ return true;
+ }
+ return false;
+}
+
+static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc,
+ unsigned int line_idx)
+{
+ unsigned int i;
+ u64 mask = BIT_ULL(line_idx);
+
+ for (i = 0; i < lc->num_attrs; i++) {
+ if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) &&
+ (lc->attrs[i].mask & mask))
+ return lc->attrs[i].attr.debounce_period_us;
+ }
+ return 0;
+}
+
+static void edge_detector_stop(struct line *line, bool hte_en)
+{
+ if (line->irq && !hte_en) {
+ free_irq(line->irq, line);
+ line->irq = 0;
+ }
+
+ if (hte_en)
+ hte_ts_put(&line->hdesc);
+
+ cancel_delayed_work_sync(&line->work);
+ WRITE_ONCE(line->sw_debounced, 0);
+ WRITE_ONCE(line->eflags, 0);
+ if (line->desc)
+ WRITE_ONCE(line->desc->debounce_period_us, 0);
+ /* do not change line->level - see comment in debounced_value() */
+}
+
+static int edge_detector_setup(struct line *line,
+ struct gpio_v2_line_config *lc,
+ unsigned int line_idx,
+ u64 eflags, bool hte_req)
+{
+ u32 debounce_period_us;
+ unsigned long irqflags = 0;
+ int irq, ret;
+
+ if (eflags && !kfifo_initialized(&line->req->events)) {
+ ret = kfifo_alloc(&line->req->events,
+ line->req->event_buffer_size, GFP_KERNEL);
+ if (ret)
+ return ret;
+ }
+ WRITE_ONCE(line->eflags, eflags);
+ if (gpio_v2_line_config_debounced(lc, line_idx)) {
+ debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx);
+ ret = debounce_setup(line, debounce_period_us, hte_req);
+ if (ret)
+ return ret;
+ WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
+ }
+
+ /* detection disabled or sw debouncer will provide edge detection */
+ if (!eflags || READ_ONCE(line->sw_debounced))
+ return 0;
+
+ if (hte_req)
+ return hte_edge_setup(line, eflags);
+
+ irq = gpiod_to_irq(line->desc);
+ if (irq < 0)
+ return -ENXIO;
+
+ if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING)
+ irqflags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+ if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING)
+ irqflags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ irqflags |= IRQF_ONESHOT;
+
+ /* Request a thread to read the events */
+ ret = request_threaded_irq(irq, edge_irq_handler, edge_irq_thread,
+ irqflags, line->req->label, line);
+ if (ret)
+ return ret;
+
+ line->irq = irq;
+ return 0;
+}
+
+static int edge_detector_update(struct line *line,
+ struct gpio_v2_line_config *lc,
+ unsigned int line_idx,
+ u64 flags, bool polarity_change,
+ bool prev_hte_flag)
+{
+ u64 eflags = flags & GPIO_V2_LINE_EDGE_FLAGS;
+ unsigned int debounce_period_us =
+ gpio_v2_line_config_debounce_period(lc, line_idx);
+ bool hte_change = (prev_hte_flag !=
+ ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) != 0));
+
+ if ((READ_ONCE(line->eflags) == eflags) && !polarity_change &&
+ (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)
+ && !hte_change)
+ return 0;
+
+ /* sw debounced and still will be...*/
+ if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
+ WRITE_ONCE(line->eflags, eflags);
+ WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
+ return 0;
+ }
+
+ /* reconfiguring edge detection or sw debounce being disabled */
+ if ((line->irq && !READ_ONCE(line->sw_debounced)) || prev_hte_flag ||
+ (!debounce_period_us && READ_ONCE(line->sw_debounced)))
+ edge_detector_stop(line, prev_hte_flag);
+
+ return edge_detector_setup(line, lc, line_idx, eflags,
+ flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE);
+}
+
+static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc,
+ unsigned int line_idx)
+{
+ unsigned int i;
+ u64 mask = BIT_ULL(line_idx);
+
+ for (i = 0; i < lc->num_attrs; i++) {
+ if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_FLAGS) &&
+ (lc->attrs[i].mask & mask))
+ return lc->attrs[i].attr.flags;
+ }
+ return lc->flags;
+}
+
+static int gpio_v2_line_config_output_value(struct gpio_v2_line_config *lc,
+ unsigned int line_idx)
+{
+ unsigned int i;
+ u64 mask = BIT_ULL(line_idx);
+
+ for (i = 0; i < lc->num_attrs; i++) {
+ if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES) &&
+ (lc->attrs[i].mask & mask))
+ return !!(lc->attrs[i].attr.values & mask);
+ }
+ return 0;
+}
+
+static int gpio_v2_line_flags_validate(u64 flags)
+{
+ /* Return an error if an unknown flag is set */
+ if (flags & ~GPIO_V2_LINE_VALID_FLAGS)
+ return -EINVAL;
+ /*
+ * Do not allow both INPUT and OUTPUT flags to be set as they are
+ * contradictory.
+ */
+ if ((flags & GPIO_V2_LINE_FLAG_INPUT) &&
+ (flags & GPIO_V2_LINE_FLAG_OUTPUT))
+ return -EINVAL;
+
+ /* Only allow one event clock source */
+ if ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) &&
+ (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE))
+ return -EINVAL;
+
+ /* Edge detection requires explicit input. */
+ if ((flags & GPIO_V2_LINE_EDGE_FLAGS) &&
+ !(flags & GPIO_V2_LINE_FLAG_INPUT))
+ return -EINVAL;
+
+ /*
+ * Do not allow OPEN_SOURCE and OPEN_DRAIN flags in a single
+ * request. If the hardware actually supports enabling both at the
+ * same time the electrical result would be disastrous.
+ */
+ if ((flags & GPIO_V2_LINE_FLAG_OPEN_DRAIN) &&
+ (flags & GPIO_V2_LINE_FLAG_OPEN_SOURCE))
+ return -EINVAL;
+
+ /* Drive requires explicit output direction. */
+ if ((flags & GPIO_V2_LINE_DRIVE_FLAGS) &&
+ !(flags & GPIO_V2_LINE_FLAG_OUTPUT))
+ return -EINVAL;
+
+ /* Bias requires explicit direction. */
+ if ((flags & GPIO_V2_LINE_BIAS_FLAGS) &&
+ !(flags & GPIO_V2_LINE_DIRECTION_FLAGS))
+ return -EINVAL;
+
+ /* Only one bias flag can be set. */
+ if (((flags & GPIO_V2_LINE_FLAG_BIAS_DISABLED) &&
+ (flags & (GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN |
+ GPIO_V2_LINE_FLAG_BIAS_PULL_UP))) ||
+ ((flags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN) &&
+ (flags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP)))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc,
+ unsigned int num_lines)
+{
+ unsigned int i;
+ u64 flags;
+ int ret;
+
+ if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX)
+ return -EINVAL;
+
+ if (memchr_inv(lc->padding, 0, sizeof(lc->padding)))
+ return -EINVAL;
+
+ for (i = 0; i < num_lines; i++) {
+ flags = gpio_v2_line_config_flags(lc, i);
+ ret = gpio_v2_line_flags_validate(flags);
+ if (ret)
+ return ret;
+
+ /* debounce requires explicit input */
+ if (gpio_v2_line_config_debounced(lc, i) &&
+ !(flags & GPIO_V2_LINE_FLAG_INPUT))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void gpio_v2_line_config_flags_to_desc_flags(u64 flags,
+ unsigned long *flagsp)
+{
+ assign_bit(FLAG_ACTIVE_LOW, flagsp,
+ flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
+
+ if (flags & GPIO_V2_LINE_FLAG_OUTPUT)
+ set_bit(FLAG_IS_OUT, flagsp);
+ else if (flags & GPIO_V2_LINE_FLAG_INPUT)
+ clear_bit(FLAG_IS_OUT, flagsp);
+
+ assign_bit(FLAG_EDGE_RISING, flagsp,
+ flags & GPIO_V2_LINE_FLAG_EDGE_RISING);
+ assign_bit(FLAG_EDGE_FALLING, flagsp,
+ flags & GPIO_V2_LINE_FLAG_EDGE_FALLING);
+
+ assign_bit(FLAG_OPEN_DRAIN, flagsp,
+ flags & GPIO_V2_LINE_FLAG_OPEN_DRAIN);
+ assign_bit(FLAG_OPEN_SOURCE, flagsp,
+ flags & GPIO_V2_LINE_FLAG_OPEN_SOURCE);
+
+ assign_bit(FLAG_PULL_UP, flagsp,
+ flags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP);
+ assign_bit(FLAG_PULL_DOWN, flagsp,
+ flags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN);
+ assign_bit(FLAG_BIAS_DISABLE, flagsp,
+ flags & GPIO_V2_LINE_FLAG_BIAS_DISABLED);
+
+ assign_bit(FLAG_EVENT_CLOCK_REALTIME, flagsp,
+ flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME);
+ assign_bit(FLAG_EVENT_CLOCK_HTE, flagsp,
+ flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE);
+}
+
+static long linereq_get_values(struct linereq *lr, void __user *ip)
+{
+ struct gpio_v2_line_values lv;
+ DECLARE_BITMAP(vals, GPIO_V2_LINES_MAX);
+ struct gpio_desc **descs;
+ unsigned int i, didx, num_get;
+ bool val;
+ int ret;
+
+ /* NOTE: It's ok to read values of output lines. */
+ if (copy_from_user(&lv, ip, sizeof(lv)))
+ return -EFAULT;
+
+ for (num_get = 0, i = 0; i < lr->num_lines; i++) {
+ if (lv.mask & BIT_ULL(i)) {
+ num_get++;
+ descs = &lr->lines[i].desc;
+ }
+ }
+
+ if (num_get == 0)
+ return -EINVAL;
+
+ if (num_get != 1) {
+ descs = kmalloc_array(num_get, sizeof(*descs), GFP_KERNEL);
+ if (!descs)
+ return -ENOMEM;
+ for (didx = 0, i = 0; i < lr->num_lines; i++) {
+ if (lv.mask & BIT_ULL(i)) {
+ descs[didx] = lr->lines[i].desc;
+ didx++;
+ }
+ }
+ }
+ ret = gpiod_get_array_value_complex(false, true, num_get,
+ descs, NULL, vals);
+
+ if (num_get != 1)
+ kfree(descs);
+ if (ret)
+ return ret;
+
+ lv.bits = 0;
+ for (didx = 0, i = 0; i < lr->num_lines; i++) {
+ if (lv.mask & BIT_ULL(i)) {
+ if (lr->lines[i].sw_debounced)
+ val = debounced_value(&lr->lines[i]);
+ else
+ val = test_bit(didx, vals);
+ if (val)
+ lv.bits |= BIT_ULL(i);
+ didx++;
+ }
+ }
+
+ if (copy_to_user(ip, &lv, sizeof(lv)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long linereq_set_values_unlocked(struct linereq *lr,
+ struct gpio_v2_line_values *lv)
+{
+ DECLARE_BITMAP(vals, GPIO_V2_LINES_MAX);
+ struct gpio_desc **descs;
+ unsigned int i, didx, num_set;
+ int ret;
+
+ bitmap_zero(vals, GPIO_V2_LINES_MAX);
+ for (num_set = 0, i = 0; i < lr->num_lines; i++) {
+ if (lv->mask & BIT_ULL(i)) {
+ if (!test_bit(FLAG_IS_OUT, &lr->lines[i].desc->flags))
+ return -EPERM;
+ if (lv->bits & BIT_ULL(i))
+ __set_bit(num_set, vals);
+ num_set++;
+ descs = &lr->lines[i].desc;
+ }
+ }
+ if (num_set == 0)
+ return -EINVAL;
+
+ if (num_set != 1) {
+ /* build compacted desc array and values */
+ descs = kmalloc_array(num_set, sizeof(*descs), GFP_KERNEL);
+ if (!descs)
+ return -ENOMEM;
+ for (didx = 0, i = 0; i < lr->num_lines; i++) {
+ if (lv->mask & BIT_ULL(i)) {
+ descs[didx] = lr->lines[i].desc;
+ didx++;
+ }
+ }
+ }
+ ret = gpiod_set_array_value_complex(false, true, num_set,
+ descs, NULL, vals);
+
+ if (num_set != 1)
+ kfree(descs);
+ return ret;
+}
+
+static long linereq_set_values(struct linereq *lr, void __user *ip)
+{
+ struct gpio_v2_line_values lv;
+ int ret;
+
+ if (copy_from_user(&lv, ip, sizeof(lv)))
+ return -EFAULT;
+
+ mutex_lock(&lr->config_mutex);
+
+ ret = linereq_set_values_unlocked(lr, &lv);
+
+ mutex_unlock(&lr->config_mutex);
+
+ return ret;
+}
+
+static long linereq_set_config_unlocked(struct linereq *lr,
+ struct gpio_v2_line_config *lc)
+{
+ struct gpio_desc *desc;
+ unsigned int i;
+ u64 flags;
+ bool polarity_change;
+ bool prev_hte_flag;
+ int ret;
+
+ for (i = 0; i < lr->num_lines; i++) {
+ desc = lr->lines[i].desc;
+ flags = gpio_v2_line_config_flags(lc, i);
+ polarity_change =
+ (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) !=
+ ((flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) != 0));
+
+ prev_hte_flag = !!test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags);
+
+ gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
+ /*
+ * Lines have to be requested explicitly for input
+ * or output, else the line will be treated "as is".
+ */
+ if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
+ int val = gpio_v2_line_config_output_value(lc, i);
+
+ edge_detector_stop(&lr->lines[i], prev_hte_flag);
+ ret = gpiod_direction_output(desc, val);
+ if (ret)
+ return ret;
+ } else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ return ret;
+
+ ret = edge_detector_update(&lr->lines[i], lc, i,
+ flags, polarity_change, prev_hte_flag);
+ if (ret)
+ return ret;
+ }
+
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIO_V2_LINE_CHANGED_CONFIG,
+ desc);
+ }
+ return 0;
+}
+
+static long linereq_set_config(struct linereq *lr, void __user *ip)
+{
+ struct gpio_v2_line_config lc;
+ int ret;
+
+ if (copy_from_user(&lc, ip, sizeof(lc)))
+ return -EFAULT;
+
+ ret = gpio_v2_line_config_validate(&lc, lr->num_lines);
+ if (ret)
+ return ret;
+
+ mutex_lock(&lr->config_mutex);
+
+ ret = linereq_set_config_unlocked(lr, &lc);
+
+ mutex_unlock(&lr->config_mutex);
+
+ return ret;
+}
+
+static long linereq_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct linereq *lr = file->private_data;
+ void __user *ip = (void __user *)arg;
+
+ switch (cmd) {
+ case GPIO_V2_LINE_GET_VALUES_IOCTL:
+ return linereq_get_values(lr, ip);
+ case GPIO_V2_LINE_SET_VALUES_IOCTL:
+ return linereq_set_values(lr, ip);
+ case GPIO_V2_LINE_SET_CONFIG_IOCTL:
+ return linereq_set_config(lr, ip);
+ default:
+ return -EINVAL;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long linereq_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return linereq_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static __poll_t linereq_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct linereq *lr = file->private_data;
+ __poll_t events = 0;
+
+ poll_wait(file, &lr->wait, wait);
+
+ if (!kfifo_is_empty_spinlocked_noirqsave(&lr->events,
+ &lr->wait.lock))
+ events = EPOLLIN | EPOLLRDNORM;
+
+ return events;
+}
+
+static ssize_t linereq_read(struct file *file,
+ char __user *buf,
+ size_t count,
+ loff_t *f_ps)
+{
+ struct linereq *lr = file->private_data;
+ struct gpio_v2_line_event le;
+ ssize_t bytes_read = 0;
+ int ret;
+
+ if (count < sizeof(le))
+ return -EINVAL;
+
+ do {
+ spin_lock(&lr->wait.lock);
+ if (kfifo_is_empty(&lr->events)) {
+ if (bytes_read) {
+ spin_unlock(&lr->wait.lock);
+ return bytes_read;
+ }
+
+ if (file->f_flags & O_NONBLOCK) {
+ spin_unlock(&lr->wait.lock);
+ return -EAGAIN;
+ }
+
+ ret = wait_event_interruptible_locked(lr->wait,
+ !kfifo_is_empty(&lr->events));
+ if (ret) {
+ spin_unlock(&lr->wait.lock);
+ return ret;
+ }
+ }
+
+ ret = kfifo_out(&lr->events, &le, 1);
+ spin_unlock(&lr->wait.lock);
+ if (ret != 1) {
+ /*
+ * This should never happen - we were holding the
+ * lock from the moment we learned the fifo is no
+ * longer empty until now.
+ */
+ ret = -EIO;
+ break;
+ }
+
+ if (copy_to_user(buf + bytes_read, &le, sizeof(le)))
+ return -EFAULT;
+ bytes_read += sizeof(le);
+ } while (count >= bytes_read + sizeof(le));
+
+ return bytes_read;
+}
+
+static void linereq_free(struct linereq *lr)
+{
+ unsigned int i;
+ bool hte;
+
+ for (i = 0; i < lr->num_lines; i++) {
+ hte = !!test_bit(FLAG_EVENT_CLOCK_HTE,
+ &lr->lines[i].desc->flags);
+ edge_detector_stop(&lr->lines[i], hte);
+ if (lr->lines[i].desc)
+ gpiod_free(lr->lines[i].desc);
+ }
+ kfifo_free(&lr->events);
+ kfree(lr->label);
+ put_device(&lr->gdev->dev);
+ kfree(lr);
+}
+
+static int linereq_release(struct inode *inode, struct file *file)
+{
+ struct linereq *lr = file->private_data;
+
+ linereq_free(lr);
+ return 0;
+}
+
+static const struct file_operations line_fileops = {
+ .release = linereq_release,
+ .read = linereq_read,
+ .poll = linereq_poll,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = linereq_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = linereq_ioctl_compat,
+#endif
+};
+
+static int linereq_create(struct gpio_device *gdev, void __user *ip)
+{
+ struct gpio_v2_line_request ulr;
+ struct gpio_v2_line_config *lc;
+ struct linereq *lr;
+ struct file *file;
+ u64 flags;
+ unsigned int i;
+ int fd, ret;
+
+ if (copy_from_user(&ulr, ip, sizeof(ulr)))
+ return -EFAULT;
+
+ if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX))
+ return -EINVAL;
+
+ if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding)))
+ return -EINVAL;
+
+ lc = &ulr.config;
+ ret = gpio_v2_line_config_validate(lc, ulr.num_lines);
+ if (ret)
+ return ret;
+
+ lr = kzalloc(struct_size(lr, lines, ulr.num_lines), GFP_KERNEL);
+ if (!lr)
+ return -ENOMEM;
+
+ lr->gdev = gdev;
+ get_device(&gdev->dev);
+
+ for (i = 0; i < ulr.num_lines; i++) {
+ lr->lines[i].req = lr;
+ WRITE_ONCE(lr->lines[i].sw_debounced, 0);
+ INIT_DELAYED_WORK(&lr->lines[i].work, debounce_work_func);
+ }
+
+ if (ulr.consumer[0] != '\0') {
+ /* label is only initialized if consumer is set */
+ lr->label = kstrndup(ulr.consumer, sizeof(ulr.consumer) - 1,
+ GFP_KERNEL);
+ if (!lr->label) {
+ ret = -ENOMEM;
+ goto out_free_linereq;
+ }
+ }
+
+ mutex_init(&lr->config_mutex);
+ init_waitqueue_head(&lr->wait);
+ lr->event_buffer_size = ulr.event_buffer_size;
+ if (lr->event_buffer_size == 0)
+ lr->event_buffer_size = ulr.num_lines * 16;
+ else if (lr->event_buffer_size > GPIO_V2_LINES_MAX * 16)
+ lr->event_buffer_size = GPIO_V2_LINES_MAX * 16;
+
+ atomic_set(&lr->seqno, 0);
+ lr->num_lines = ulr.num_lines;
+
+ /* Request each GPIO */
+ for (i = 0; i < ulr.num_lines; i++) {
+ u32 offset = ulr.offsets[i];
+ struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
+
+ if (IS_ERR(desc)) {
+ ret = PTR_ERR(desc);
+ goto out_free_linereq;
+ }
+
+ ret = gpiod_request_user(desc, lr->label);
+ if (ret)
+ goto out_free_linereq;
+
+ lr->lines[i].desc = desc;
+ flags = gpio_v2_line_config_flags(lc, i);
+ gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
+
+ ret = gpiod_set_transitory(desc, false);
+ if (ret < 0)
+ goto out_free_linereq;
+
+ /*
+ * Lines have to be requested explicitly for input
+ * or output, else the line will be treated "as is".
+ */
+ if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
+ int val = gpio_v2_line_config_output_value(lc, i);
+
+ ret = gpiod_direction_output(desc, val);
+ if (ret)
+ goto out_free_linereq;
+ } else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ goto out_free_linereq;
+
+ ret = edge_detector_setup(&lr->lines[i], lc, i,
+ flags & GPIO_V2_LINE_EDGE_FLAGS,
+ flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE);
+ if (ret)
+ goto out_free_linereq;
+ }
+
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIO_V2_LINE_CHANGED_REQUESTED, desc);
+
+ dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
+ offset);
+ }
+
+ fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ ret = fd;
+ goto out_free_linereq;
+ }
+
+ file = anon_inode_getfile("gpio-line", &line_fileops, lr,
+ O_RDONLY | O_CLOEXEC);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto out_put_unused_fd;
+ }
+
+ ulr.fd = fd;
+ if (copy_to_user(ip, &ulr, sizeof(ulr))) {
+ /*
+ * fput() will trigger the release() callback, so do not go onto
+ * the regular error cleanup path here.
+ */
+ fput(file);
+ put_unused_fd(fd);
+ return -EFAULT;
+ }
+
+ fd_install(fd, file);
+
+ dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
+ lr->num_lines);
+
+ return 0;
+
+out_put_unused_fd:
+ put_unused_fd(fd);
+out_free_linereq:
+ linereq_free(lr);
+ return ret;
+}
+
+#ifdef CONFIG_GPIO_CDEV_V1
+
+/*
+ * GPIO line event management
+ */
+
+/**
+ * struct lineevent_state - contains the state of a userspace event
+ * @gdev: the GPIO device the event pertains to
+ * @label: consumer label used to tag descriptors
+ * @desc: the GPIO descriptor held by this event
+ * @eflags: the event flags this line was requested with
+ * @irq: the interrupt that trigger in response to events on this GPIO
+ * @wait: wait queue that handles blocking reads of events
+ * @events: KFIFO for the GPIO events
+ * @timestamp: cache for the timestamp storing it between hardirq
+ * and IRQ thread, used to bring the timestamp close to the actual
+ * event
+ */
+struct lineevent_state {
+ struct gpio_device *gdev;
+ const char *label;
+ struct gpio_desc *desc;
+ u32 eflags;
+ int irq;
+ wait_queue_head_t wait;
+ DECLARE_KFIFO(events, struct gpioevent_data, 16);
+ u64 timestamp;
+};
+
+#define GPIOEVENT_REQUEST_VALID_FLAGS \
+ (GPIOEVENT_REQUEST_RISING_EDGE | \
+ GPIOEVENT_REQUEST_FALLING_EDGE)
+
+static __poll_t lineevent_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct lineevent_state *le = file->private_data;
+ __poll_t events = 0;
+
+ poll_wait(file, &le->wait, wait);
+
+ if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock))
+ events = EPOLLIN | EPOLLRDNORM;
+
+ return events;
+}
+
+struct compat_gpioeevent_data {
+ compat_u64 timestamp;
+ u32 id;
+};
+
+static ssize_t lineevent_read(struct file *file,
+ char __user *buf,
+ size_t count,
+ loff_t *f_ps)
+{
+ struct lineevent_state *le = file->private_data;
+ struct gpioevent_data ge;
+ ssize_t bytes_read = 0;
+ ssize_t ge_size;
+ int ret;
+
+ /*
+ * When compatible system call is being used the struct gpioevent_data,
+ * in case of at least ia32, has different size due to the alignment
+ * differences. Because we have first member 64 bits followed by one of
+ * 32 bits there is no gap between them. The only difference is the
+ * padding at the end of the data structure. Hence, we calculate the
+ * actual sizeof() and pass this as an argument to copy_to_user() to
+ * drop unneeded bytes from the output.
+ */
+ if (compat_need_64bit_alignment_fixup())
+ ge_size = sizeof(struct compat_gpioeevent_data);
+ else
+ ge_size = sizeof(struct gpioevent_data);
+ if (count < ge_size)
+ return -EINVAL;
+
+ do {
+ spin_lock(&le->wait.lock);
+ if (kfifo_is_empty(&le->events)) {
+ if (bytes_read) {
+ spin_unlock(&le->wait.lock);
+ return bytes_read;
+ }
+
+ if (file->f_flags & O_NONBLOCK) {
+ spin_unlock(&le->wait.lock);
+ return -EAGAIN;
+ }
+
+ ret = wait_event_interruptible_locked(le->wait,
+ !kfifo_is_empty(&le->events));
+ if (ret) {
+ spin_unlock(&le->wait.lock);
+ return ret;
+ }
+ }
+
+ ret = kfifo_out(&le->events, &ge, 1);
+ spin_unlock(&le->wait.lock);
+ if (ret != 1) {
+ /*
+ * This should never happen - we were holding the lock
+ * from the moment we learned the fifo is no longer
+ * empty until now.
+ */
+ ret = -EIO;
+ break;
+ }
+
+ if (copy_to_user(buf + bytes_read, &ge, ge_size))
+ return -EFAULT;
+ bytes_read += ge_size;
+ } while (count >= bytes_read + ge_size);
+
+ return bytes_read;
+}
+
+static void lineevent_free(struct lineevent_state *le)
+{
+ if (le->irq)
+ free_irq(le->irq, le);
+ if (le->desc)
+ gpiod_free(le->desc);
+ kfree(le->label);
+ put_device(&le->gdev->dev);
+ kfree(le);
+}
+
+static int lineevent_release(struct inode *inode, struct file *file)
+{
+ lineevent_free(file->private_data);
+ return 0;
+}
+
+static long lineevent_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct lineevent_state *le = file->private_data;
+ void __user *ip = (void __user *)arg;
+ struct gpiohandle_data ghd;
+
+ /*
+ * We can get the value for an event line but not set it,
+ * because it is input by definition.
+ */
+ if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
+ int val;
+
+ memset(&ghd, 0, sizeof(ghd));
+
+ val = gpiod_get_value_cansleep(le->desc);
+ if (val < 0)
+ return val;
+ ghd.values[0] = val;
+
+ if (copy_to_user(ip, &ghd, sizeof(ghd)))
+ return -EFAULT;
+
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_COMPAT
+static long lineevent_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return lineevent_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations lineevent_fileops = {
+ .release = lineevent_release,
+ .read = lineevent_read,
+ .poll = lineevent_poll,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = lineevent_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lineevent_ioctl_compat,
+#endif
+};
+
+static irqreturn_t lineevent_irq_thread(int irq, void *p)
+{
+ struct lineevent_state *le = p;
+ struct gpioevent_data ge;
+ int ret;
+
+ /* Do not leak kernel stack to userspace */
+ memset(&ge, 0, sizeof(ge));
+
+ /*
+ * We may be running from a nested threaded interrupt in which case
+ * we didn't get the timestamp from lineevent_irq_handler().
+ */
+ if (!le->timestamp)
+ ge.timestamp = ktime_get_ns();
+ else
+ ge.timestamp = le->timestamp;
+
+ if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
+ && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
+ int level = gpiod_get_value_cansleep(le->desc);
+
+ if (level)
+ /* Emit low-to-high event */
+ ge.id = GPIOEVENT_EVENT_RISING_EDGE;
+ else
+ /* Emit high-to-low event */
+ ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
+ } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) {
+ /* Emit low-to-high event */
+ ge.id = GPIOEVENT_EVENT_RISING_EDGE;
+ } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
+ /* Emit high-to-low event */
+ ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
+ } else {
+ return IRQ_NONE;
+ }
+
+ ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge,
+ 1, &le->wait.lock);
+ if (ret)
+ wake_up_poll(&le->wait, EPOLLIN);
+ else
+ pr_debug_ratelimited("event FIFO is full - event dropped\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lineevent_irq_handler(int irq, void *p)
+{
+ struct lineevent_state *le = p;
+
+ /*
+ * Just store the timestamp in hardirq context so we get it as
+ * close in time as possible to the actual event.
+ */
+ le->timestamp = ktime_get_ns();
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int lineevent_create(struct gpio_device *gdev, void __user *ip)
+{
+ struct gpioevent_request eventreq;
+ struct lineevent_state *le;
+ struct gpio_desc *desc;
+ struct file *file;
+ u32 offset;
+ u32 lflags;
+ u32 eflags;
+ int fd;
+ int ret;
+ int irq, irqflags = 0;
+
+ if (copy_from_user(&eventreq, ip, sizeof(eventreq)))
+ return -EFAULT;
+
+ offset = eventreq.lineoffset;
+ lflags = eventreq.handleflags;
+ eflags = eventreq.eventflags;
+
+ desc = gpiochip_get_desc(gdev->chip, offset);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ /* Return an error if a unknown flag is set */
+ if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) ||
+ (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS))
+ return -EINVAL;
+
+ /* This is just wrong: we don't look for events on output lines */
+ if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) ||
+ (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
+ (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))
+ return -EINVAL;
+
+ /* Only one bias flag can be set. */
+ if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) &&
+ (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN |
+ GPIOHANDLE_REQUEST_BIAS_PULL_UP))) ||
+ ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) &&
+ (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)))
+ return -EINVAL;
+
+ le = kzalloc(sizeof(*le), GFP_KERNEL);
+ if (!le)
+ return -ENOMEM;
+ le->gdev = gdev;
+ get_device(&gdev->dev);
+
+ if (eventreq.consumer_label[0] != '\0') {
+ /* label is only initialized if consumer_label is set */
+ le->label = kstrndup(eventreq.consumer_label,
+ sizeof(eventreq.consumer_label) - 1,
+ GFP_KERNEL);
+ if (!le->label) {
+ ret = -ENOMEM;
+ goto out_free_le;
+ }
+ }
+
+ ret = gpiod_request_user(desc, le->label);
+ if (ret)
+ goto out_free_le;
+ le->desc = desc;
+ le->eflags = eflags;
+
+ linehandle_flags_to_desc_flags(lflags, &desc->flags);
+
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ goto out_free_le;
+
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIO_V2_LINE_CHANGED_REQUESTED, desc);
+
+ irq = gpiod_to_irq(desc);
+ if (irq <= 0) {
+ ret = -ENODEV;
+ goto out_free_le;
+ }
+ le->irq = irq;
+
+ if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
+ irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+ if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE)
+ irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ irqflags |= IRQF_ONESHOT;
+
+ INIT_KFIFO(le->events);
+ init_waitqueue_head(&le->wait);
+
+ /* Request a thread to read the events */
+ ret = request_threaded_irq(le->irq,
+ lineevent_irq_handler,
+ lineevent_irq_thread,
+ irqflags,
+ le->label,
+ le);
+ if (ret)
+ goto out_free_le;
+
+ fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ ret = fd;
+ goto out_free_le;
+ }
+
+ file = anon_inode_getfile("gpio-event",
+ &lineevent_fileops,
+ le,
+ O_RDONLY | O_CLOEXEC);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto out_put_unused_fd;
+ }
+
+ eventreq.fd = fd;
+ if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
+ /*
+ * fput() will trigger the release() callback, so do not go onto
+ * the regular error cleanup path here.
+ */
+ fput(file);
+ put_unused_fd(fd);
+ return -EFAULT;
+ }
+
+ fd_install(fd, file);
+
+ return 0;
+
+out_put_unused_fd:
+ put_unused_fd(fd);
+out_free_le:
+ lineevent_free(le);
+ return ret;
+}
+
+static void gpio_v2_line_info_to_v1(struct gpio_v2_line_info *info_v2,
+ struct gpioline_info *info_v1)
+{
+ u64 flagsv2 = info_v2->flags;
+
+ memcpy(info_v1->name, info_v2->name, sizeof(info_v1->name));
+ memcpy(info_v1->consumer, info_v2->consumer, sizeof(info_v1->consumer));
+ info_v1->line_offset = info_v2->offset;
+ info_v1->flags = 0;
+
+ if (flagsv2 & GPIO_V2_LINE_FLAG_USED)
+ info_v1->flags |= GPIOLINE_FLAG_KERNEL;
+
+ if (flagsv2 & GPIO_V2_LINE_FLAG_OUTPUT)
+ info_v1->flags |= GPIOLINE_FLAG_IS_OUT;
+
+ if (flagsv2 & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
+ info_v1->flags |= GPIOLINE_FLAG_ACTIVE_LOW;
+
+ if (flagsv2 & GPIO_V2_LINE_FLAG_OPEN_DRAIN)
+ info_v1->flags |= GPIOLINE_FLAG_OPEN_DRAIN;
+ if (flagsv2 & GPIO_V2_LINE_FLAG_OPEN_SOURCE)
+ info_v1->flags |= GPIOLINE_FLAG_OPEN_SOURCE;
+
+ if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_PULL_UP)
+ info_v1->flags |= GPIOLINE_FLAG_BIAS_PULL_UP;
+ if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN)
+ info_v1->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
+ if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_DISABLED)
+ info_v1->flags |= GPIOLINE_FLAG_BIAS_DISABLE;
+}
+
+static void gpio_v2_line_info_changed_to_v1(
+ struct gpio_v2_line_info_changed *lic_v2,
+ struct gpioline_info_changed *lic_v1)
+{
+ memset(lic_v1, 0, sizeof(*lic_v1));
+ gpio_v2_line_info_to_v1(&lic_v2->info, &lic_v1->info);
+ lic_v1->timestamp = lic_v2->timestamp_ns;
+ lic_v1->event_type = lic_v2->event_type;
+}
+
+#endif /* CONFIG_GPIO_CDEV_V1 */
+
+static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
+ struct gpio_v2_line_info *info)
+{
+ struct gpio_chip *gc = desc->gdev->chip;
+ bool ok_for_pinctrl;
+ unsigned long flags;
+ u32 debounce_period_us;
+ unsigned int num_attrs = 0;
+
+ memset(info, 0, sizeof(*info));
+ info->offset = gpio_chip_hwgpio(desc);
+
+ /*
+ * This function takes a mutex so we must check this before taking
+ * the spinlock.
+ *
+ * FIXME: find a non-racy way to retrieve this information. Maybe a
+ * lock common to both frameworks?
+ */
+ ok_for_pinctrl =
+ pinctrl_gpio_can_use_line(gc->base + info->offset);
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if (desc->name)
+ strscpy(info->name, desc->name, sizeof(info->name));
+
+ if (desc->label)
+ strscpy(info->consumer, desc->label, sizeof(info->consumer));
+
+ /*
+ * Userspace only need to know that the kernel is using this GPIO so
+ * it can't use it.
+ */
+ info->flags = 0;
+ if (test_bit(FLAG_REQUESTED, &desc->flags) ||
+ test_bit(FLAG_IS_HOGGED, &desc->flags) ||
+ test_bit(FLAG_USED_AS_IRQ, &desc->flags) ||
+ test_bit(FLAG_EXPORT, &desc->flags) ||
+ test_bit(FLAG_SYSFS, &desc->flags) ||
+ !gpiochip_line_is_valid(gc, info->offset) ||
+ !ok_for_pinctrl)
+ info->flags |= GPIO_V2_LINE_FLAG_USED;
+
+ if (test_bit(FLAG_IS_OUT, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_OUTPUT;
+ else
+ info->flags |= GPIO_V2_LINE_FLAG_INPUT;
+
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN;
+ if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE;
+
+ if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED;
+ if (test_bit(FLAG_PULL_DOWN, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN;
+ if (test_bit(FLAG_PULL_UP, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
+
+ if (test_bit(FLAG_EDGE_RISING, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_EDGE_RISING;
+ if (test_bit(FLAG_EDGE_FALLING, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING;
+
+ if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME;
+ else if (test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE;
+
+ debounce_period_us = READ_ONCE(desc->debounce_period_us);
+ if (debounce_period_us) {
+ info->attrs[num_attrs].id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE;
+ info->attrs[num_attrs].debounce_period_us = debounce_period_us;
+ num_attrs++;
+ }
+ info->num_attrs = num_attrs;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+struct gpio_chardev_data {
+ struct gpio_device *gdev;
+ wait_queue_head_t wait;
+ DECLARE_KFIFO(events, struct gpio_v2_line_info_changed, 32);
+ struct notifier_block lineinfo_changed_nb;
+ unsigned long *watched_lines;
+#ifdef CONFIG_GPIO_CDEV_V1
+ atomic_t watch_abi_version;
+#endif
+};
+
+static int chipinfo_get(struct gpio_chardev_data *cdev, void __user *ip)
+{
+ struct gpio_device *gdev = cdev->gdev;
+ struct gpiochip_info chipinfo;
+
+ memset(&chipinfo, 0, sizeof(chipinfo));
+
+ strscpy(chipinfo.name, dev_name(&gdev->dev), sizeof(chipinfo.name));
+ strscpy(chipinfo.label, gdev->label, sizeof(chipinfo.label));
+ chipinfo.lines = gdev->ngpio;
+ if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+#ifdef CONFIG_GPIO_CDEV_V1
+/*
+ * returns 0 if the versions match, else the previously selected ABI version
+ */
+static int lineinfo_ensure_abi_version(struct gpio_chardev_data *cdata,
+ unsigned int version)
+{
+ int abiv = atomic_cmpxchg(&cdata->watch_abi_version, 0, version);
+
+ if (abiv == version)
+ return 0;
+
+ return abiv;
+}
+
+static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
+ bool watch)
+{
+ struct gpio_desc *desc;
+ struct gpioline_info lineinfo;
+ struct gpio_v2_line_info lineinfo_v2;
+
+ if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
+ return -EFAULT;
+
+ /* this doubles as a range check on line_offset */
+ desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ if (watch) {
+ if (lineinfo_ensure_abi_version(cdev, 1))
+ return -EPERM;
+
+ if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines))
+ return -EBUSY;
+ }
+
+ gpio_desc_to_lineinfo(desc, &lineinfo_v2);
+ gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
+
+ if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
+ if (watch)
+ clear_bit(lineinfo.line_offset, cdev->watched_lines);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+#endif
+
+static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
+ bool watch)
+{
+ struct gpio_desc *desc;
+ struct gpio_v2_line_info lineinfo;
+
+ if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
+ return -EFAULT;
+
+ if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding)))
+ return -EINVAL;
+
+ desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ if (watch) {
+#ifdef CONFIG_GPIO_CDEV_V1
+ if (lineinfo_ensure_abi_version(cdev, 2))
+ return -EPERM;
+#endif
+ if (test_and_set_bit(lineinfo.offset, cdev->watched_lines))
+ return -EBUSY;
+ }
+ gpio_desc_to_lineinfo(desc, &lineinfo);
+
+ if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
+ if (watch)
+ clear_bit(lineinfo.offset, cdev->watched_lines);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip)
+{
+ __u32 offset;
+
+ if (copy_from_user(&offset, ip, sizeof(offset)))
+ return -EFAULT;
+
+ if (offset >= cdev->gdev->ngpio)
+ return -EINVAL;
+
+ if (!test_and_clear_bit(offset, cdev->watched_lines))
+ return -EBUSY;
+
+ return 0;
+}
+
+/*
+ * gpio_ioctl() - ioctl handler for the GPIO chardev
+ */
+static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct gpio_chardev_data *cdev = file->private_data;
+ struct gpio_device *gdev = cdev->gdev;
+ void __user *ip = (void __user *)arg;
+
+ /* We fail any subsequent ioctl():s when the chip is gone */
+ if (!gdev->chip)
+ return -ENODEV;
+
+ /* Fill in the struct and pass to userspace */
+ switch (cmd) {
+ case GPIO_GET_CHIPINFO_IOCTL:
+ return chipinfo_get(cdev, ip);
+#ifdef CONFIG_GPIO_CDEV_V1
+ case GPIO_GET_LINEHANDLE_IOCTL:
+ return linehandle_create(gdev, ip);
+ case GPIO_GET_LINEEVENT_IOCTL:
+ return lineevent_create(gdev, ip);
+ case GPIO_GET_LINEINFO_IOCTL:
+ return lineinfo_get_v1(cdev, ip, false);
+ case GPIO_GET_LINEINFO_WATCH_IOCTL:
+ return lineinfo_get_v1(cdev, ip, true);
+#endif /* CONFIG_GPIO_CDEV_V1 */
+ case GPIO_V2_GET_LINEINFO_IOCTL:
+ return lineinfo_get(cdev, ip, false);
+ case GPIO_V2_GET_LINEINFO_WATCH_IOCTL:
+ return lineinfo_get(cdev, ip, true);
+ case GPIO_V2_GET_LINE_IOCTL:
+ return linereq_create(gdev, ip);
+ case GPIO_GET_LINEINFO_UNWATCH_IOCTL:
+ return lineinfo_unwatch(cdev, ip);
+ default:
+ return -EINVAL;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long gpio_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return gpio_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static struct gpio_chardev_data *
+to_gpio_chardev_data(struct notifier_block *nb)
+{
+ return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb);
+}
+
+static int lineinfo_changed_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct gpio_chardev_data *cdev = to_gpio_chardev_data(nb);
+ struct gpio_v2_line_info_changed chg;
+ struct gpio_desc *desc = data;
+ int ret;
+
+ if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines))
+ return NOTIFY_DONE;
+
+ memset(&chg, 0, sizeof(chg));
+ chg.event_type = action;
+ chg.timestamp_ns = ktime_get_ns();
+ gpio_desc_to_lineinfo(desc, &chg.info);
+
+ ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock);
+ if (ret)
+ wake_up_poll(&cdev->wait, EPOLLIN);
+ else
+ pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n");
+
+ return NOTIFY_OK;
+}
+
+static __poll_t lineinfo_watch_poll(struct file *file,
+ struct poll_table_struct *pollt)
+{
+ struct gpio_chardev_data *cdev = file->private_data;
+ __poll_t events = 0;
+
+ poll_wait(file, &cdev->wait, pollt);
+
+ if (!kfifo_is_empty_spinlocked_noirqsave(&cdev->events,
+ &cdev->wait.lock))
+ events = EPOLLIN | EPOLLRDNORM;
+
+ return events;
+}
+
+static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct gpio_chardev_data *cdev = file->private_data;
+ struct gpio_v2_line_info_changed event;
+ ssize_t bytes_read = 0;
+ int ret;
+ size_t event_size;
+
+#ifndef CONFIG_GPIO_CDEV_V1
+ event_size = sizeof(struct gpio_v2_line_info_changed);
+ if (count < event_size)
+ return -EINVAL;
+#endif
+
+ do {
+ spin_lock(&cdev->wait.lock);
+ if (kfifo_is_empty(&cdev->events)) {
+ if (bytes_read) {
+ spin_unlock(&cdev->wait.lock);
+ return bytes_read;
+ }
+
+ if (file->f_flags & O_NONBLOCK) {
+ spin_unlock(&cdev->wait.lock);
+ return -EAGAIN;
+ }
+
+ ret = wait_event_interruptible_locked(cdev->wait,
+ !kfifo_is_empty(&cdev->events));
+ if (ret) {
+ spin_unlock(&cdev->wait.lock);
+ return ret;
+ }
+ }
+#ifdef CONFIG_GPIO_CDEV_V1
+ /* must be after kfifo check so watch_abi_version is set */
+ if (atomic_read(&cdev->watch_abi_version) == 2)
+ event_size = sizeof(struct gpio_v2_line_info_changed);
+ else
+ event_size = sizeof(struct gpioline_info_changed);
+ if (count < event_size) {
+ spin_unlock(&cdev->wait.lock);
+ return -EINVAL;
+ }
+#endif
+ ret = kfifo_out(&cdev->events, &event, 1);
+ spin_unlock(&cdev->wait.lock);
+ if (ret != 1) {
+ ret = -EIO;
+ break;
+ /* We should never get here. See lineevent_read(). */
+ }
+
+#ifdef CONFIG_GPIO_CDEV_V1
+ if (event_size == sizeof(struct gpio_v2_line_info_changed)) {
+ if (copy_to_user(buf + bytes_read, &event, event_size))
+ return -EFAULT;
+ } else {
+ struct gpioline_info_changed event_v1;
+
+ gpio_v2_line_info_changed_to_v1(&event, &event_v1);
+ if (copy_to_user(buf + bytes_read, &event_v1,
+ event_size))
+ return -EFAULT;
+ }
+#else
+ if (copy_to_user(buf + bytes_read, &event, event_size))
+ return -EFAULT;
+#endif
+ bytes_read += event_size;
+ } while (count >= bytes_read + sizeof(event));
+
+ return bytes_read;
+}
+
+/**
+ * gpio_chrdev_open() - open the chardev for ioctl operations
+ * @inode: inode for this chardev
+ * @file: file struct for storing private data
+ * Returns 0 on success
+ */
+static int gpio_chrdev_open(struct inode *inode, struct file *file)
+{
+ struct gpio_device *gdev = container_of(inode->i_cdev,
+ struct gpio_device, chrdev);
+ struct gpio_chardev_data *cdev;
+ int ret = -ENOMEM;
+
+ /* Fail on open if the backing gpiochip is gone */
+ if (!gdev->chip)
+ return -ENODEV;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL);
+ if (!cdev->watched_lines)
+ goto out_free_cdev;
+
+ init_waitqueue_head(&cdev->wait);
+ INIT_KFIFO(cdev->events);
+ cdev->gdev = gdev;
+
+ cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify;
+ ret = blocking_notifier_chain_register(&gdev->notifier,
+ &cdev->lineinfo_changed_nb);
+ if (ret)
+ goto out_free_bitmap;
+
+ get_device(&gdev->dev);
+ file->private_data = cdev;
+
+ ret = nonseekable_open(inode, file);
+ if (ret)
+ goto out_unregister_notifier;
+
+ return ret;
+
+out_unregister_notifier:
+ blocking_notifier_chain_unregister(&gdev->notifier,
+ &cdev->lineinfo_changed_nb);
+out_free_bitmap:
+ bitmap_free(cdev->watched_lines);
+out_free_cdev:
+ kfree(cdev);
+ return ret;
+}
+
+/**
+ * gpio_chrdev_release() - close chardev after ioctl operations
+ * @inode: inode for this chardev
+ * @file: file struct for storing private data
+ * Returns 0 on success
+ */
+static int gpio_chrdev_release(struct inode *inode, struct file *file)
+{
+ struct gpio_chardev_data *cdev = file->private_data;
+ struct gpio_device *gdev = cdev->gdev;
+
+ bitmap_free(cdev->watched_lines);
+ blocking_notifier_chain_unregister(&gdev->notifier,
+ &cdev->lineinfo_changed_nb);
+ put_device(&gdev->dev);
+ kfree(cdev);
+
+ return 0;
+}
+
+static const struct file_operations gpio_fileops = {
+ .release = gpio_chrdev_release,
+ .open = gpio_chrdev_open,
+ .poll = lineinfo_watch_poll,
+ .read = lineinfo_watch_read,
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = gpio_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = gpio_ioctl_compat,
+#endif
+};
+
+int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
+{
+ int ret;
+
+ cdev_init(&gdev->chrdev, &gpio_fileops);
+ gdev->chrdev.owner = THIS_MODULE;
+ gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id);
+
+ ret = cdev_device_add(&gdev->chrdev, &gdev->dev);
+ if (ret)
+ return ret;
+
+ chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
+ MAJOR(devt), gdev->id);
+
+ return 0;
+}
+
+void gpiolib_cdev_unregister(struct gpio_device *gdev)
+{
+ cdev_device_del(&gdev->chrdev, &gdev->dev);
+}
diff --git a/drivers/gpio/gpiolib-cdev.h b/drivers/gpio/gpiolib-cdev.h
new file mode 100644
index 000000000000..b42644cbffb8
--- /dev/null
+++ b/drivers/gpio/gpiolib-cdev.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef GPIOLIB_CDEV_H
+#define GPIOLIB_CDEV_H
+
+#include <linux/types.h>
+
+struct gpio_device;
+
+int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt);
+void gpiolib_cdev_unregister(struct gpio_device *gdev);
+
+#endif /* GPIOLIB_CDEV_H */
diff --git a/drivers/gpio/gpiolib-devprop.c b/drivers/gpio/gpiolib-devprop.c
deleted file mode 100644
index 53781b253986..000000000000
--- a/drivers/gpio/gpiolib-devprop.c
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Device property helpers for GPIO chips.
- *
- * Copyright (C) 2016, Intel Corporation
- * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
- */
-
-#include <linux/property.h>
-#include <linux/slab.h>
-#include <linux/gpio/consumer.h>
-#include <linux/gpio/driver.h>
-#include <linux/export.h>
-
-#include "gpiolib.h"
-
-/**
- * devprop_gpiochip_set_names - Set GPIO line names using device properties
- * @chip: GPIO chip whose lines should be named, if possible
- * @fwnode: Property Node containing the gpio-line-names property
- *
- * Looks for device property "gpio-line-names" and if it exists assigns
- * GPIO line names for the chip. The memory allocated for the assigned
- * names belong to the underlying firmware node and should not be released
- * by the caller.
- */
-void devprop_gpiochip_set_names(struct gpio_chip *chip,
- const struct fwnode_handle *fwnode)
-{
- struct gpio_device *gdev = chip->gpiodev;
- const char **names;
- int ret, i;
- int count;
-
- count = fwnode_property_read_string_array(fwnode, "gpio-line-names",
- NULL, 0);
- if (count < 0)
- return;
-
- if (count > gdev->ngpio)
- count = gdev->ngpio;
-
- names = kcalloc(count, sizeof(*names), GFP_KERNEL);
- if (!names)
- return;
-
- ret = fwnode_property_read_string_array(fwnode, "gpio-line-names",
- names, count);
- if (ret < 0) {
- dev_warn(&gdev->dev, "failed to read GPIO line names\n");
- kfree(names);
- return;
- }
-
- for (i = 0; i < count; i++)
- gdev->descs[i].name = names[i];
-
- kfree(names);
-}
-EXPORT_SYMBOL_GPL(devprop_gpiochip_set_names);
diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c
index 0acc2cc6e868..79da85d17b71 100644
--- a/drivers/gpio/gpiolib-devres.c
+++ b/drivers/gpio/gpiolib-devres.c
@@ -59,7 +59,7 @@ struct gpio_desc *__must_check devm_gpiod_get(struct device *dev,
{
return devm_gpiod_get_index(dev, con_id, 0, flags);
}
-EXPORT_SYMBOL(devm_gpiod_get);
+EXPORT_SYMBOL_GPL(devm_gpiod_get);
/**
* devm_gpiod_get_optional - Resource-managed gpiod_get_optional()
@@ -77,7 +77,7 @@ struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev,
{
return devm_gpiod_get_index_optional(dev, con_id, 0, flags);
}
-EXPORT_SYMBOL(devm_gpiod_get_optional);
+EXPORT_SYMBOL_GPL(devm_gpiod_get_optional);
/**
* devm_gpiod_get_index - Resource-managed gpiod_get_index()
@@ -127,7 +127,7 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev,
return desc;
}
-EXPORT_SYMBOL(devm_gpiod_get_index);
+EXPORT_SYMBOL_GPL(devm_gpiod_get_index);
/**
* devm_gpiod_get_from_of_node() - obtain a GPIO from an OF node
@@ -145,7 +145,7 @@ EXPORT_SYMBOL(devm_gpiod_get_index);
* In case of error an ERR_PTR() is returned.
*/
struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev,
- struct device_node *node,
+ const struct device_node *node,
const char *propname, int index,
enum gpiod_flags dflags,
const char *label)
@@ -182,15 +182,14 @@ struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev,
return desc;
}
-EXPORT_SYMBOL(devm_gpiod_get_from_of_node);
+EXPORT_SYMBOL_GPL(devm_gpiod_get_from_of_node);
/**
- * devm_fwnode_get_index_gpiod_from_child - get a GPIO descriptor from a
- * device's child node
+ * devm_fwnode_gpiod_get_index - get a GPIO descriptor from a given node
* @dev: GPIO consumer
+ * @fwnode: firmware node containing GPIO reference
* @con_id: function within the GPIO consumer
* @index: index of the GPIO to obtain in the consumer
- * @child: firmware node (child of @dev)
* @flags: GPIO initialization flags
* @label: label to attach to the requested GPIO
*
@@ -200,35 +199,21 @@ EXPORT_SYMBOL(devm_gpiod_get_from_of_node);
* On successful request the GPIO pin is configured in accordance with
* provided @flags.
*/
-struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev,
- const char *con_id, int index,
- struct fwnode_handle *child,
- enum gpiod_flags flags,
- const char *label)
+struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *con_id, int index,
+ enum gpiod_flags flags,
+ const char *label)
{
- char prop_name[32]; /* 32 is max size of property name */
struct gpio_desc **dr;
struct gpio_desc *desc;
- unsigned int i;
dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *),
GFP_KERNEL);
if (!dr)
return ERR_PTR(-ENOMEM);
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
- if (con_id)
- snprintf(prop_name, sizeof(prop_name), "%s-%s",
- con_id, gpio_suffixes[i]);
- else
- snprintf(prop_name, sizeof(prop_name), "%s",
- gpio_suffixes[i]);
-
- desc = fwnode_get_named_gpiod(child, prop_name, index, flags,
- label);
- if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT))
- break;
- }
+ desc = fwnode_gpiod_get_index(fwnode, con_id, index, flags, label);
if (IS_ERR(desc)) {
devres_free(dr);
return desc;
@@ -239,7 +224,7 @@ struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev,
return desc;
}
-EXPORT_SYMBOL(devm_fwnode_get_index_gpiod_from_child);
+EXPORT_SYMBOL_GPL(devm_fwnode_gpiod_get_index);
/**
* devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional()
@@ -261,14 +246,12 @@ struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev,
struct gpio_desc *desc;
desc = devm_gpiod_get_index(dev, con_id, index, flags);
- if (IS_ERR(desc)) {
- if (PTR_ERR(desc) == -ENOENT)
- return NULL;
- }
+ if (gpiod_not_found(desc))
+ return NULL;
return desc;
}
-EXPORT_SYMBOL(devm_gpiod_get_index_optional);
+EXPORT_SYMBOL_GPL(devm_gpiod_get_index_optional);
/**
* devm_gpiod_get_array - Resource-managed gpiod_get_array()
@@ -303,7 +286,7 @@ struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
return descs;
}
-EXPORT_SYMBOL(devm_gpiod_get_array);
+EXPORT_SYMBOL_GPL(devm_gpiod_get_array);
/**
* devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional()
@@ -323,12 +306,12 @@ devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
struct gpio_descs *descs;
descs = devm_gpiod_get_array(dev, con_id, flags);
- if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT))
+ if (gpiod_not_found(descs))
return NULL;
return descs;
}
-EXPORT_SYMBOL(devm_gpiod_get_array_optional);
+EXPORT_SYMBOL_GPL(devm_gpiod_get_array_optional);
/**
* devm_gpiod_put - Resource-managed gpiod_put()
@@ -344,7 +327,7 @@ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
WARN_ON(devres_release(dev, devm_gpiod_release, devm_gpiod_match,
&desc));
}
-EXPORT_SYMBOL(devm_gpiod_put);
+EXPORT_SYMBOL_GPL(devm_gpiod_put);
/**
* devm_gpiod_unhinge - Remove resource management from a gpio descriptor
@@ -374,7 +357,7 @@ void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc)
/* Anything else we should warn about */
WARN_ON(ret);
}
-EXPORT_SYMBOL(devm_gpiod_unhinge);
+EXPORT_SYMBOL_GPL(devm_gpiod_unhinge);
/**
* devm_gpiod_put_array - Resource-managed gpiod_put_array()
@@ -390,7 +373,7 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)
WARN_ON(devres_release(dev, devm_gpiod_release_array,
devm_gpiod_match_array, &descs));
}
-EXPORT_SYMBOL(devm_gpiod_put_array);
+EXPORT_SYMBOL_GPL(devm_gpiod_put_array);
@@ -444,7 +427,7 @@ int devm_gpio_request(struct device *dev, unsigned gpio, const char *label)
return 0;
}
-EXPORT_SYMBOL(devm_gpio_request);
+EXPORT_SYMBOL_GPL(devm_gpio_request);
/**
* devm_gpio_request_one - request a single GPIO with initial setup
@@ -474,7 +457,7 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio,
return 0;
}
-EXPORT_SYMBOL(devm_gpio_request_one);
+EXPORT_SYMBOL_GPL(devm_gpio_request_one);
/**
* devm_gpio_free - free a GPIO
@@ -492,4 +475,42 @@ void devm_gpio_free(struct device *dev, unsigned int gpio)
WARN_ON(devres_release(dev, devm_gpio_release, devm_gpio_match,
&gpio));
}
-EXPORT_SYMBOL(devm_gpio_free);
+EXPORT_SYMBOL_GPL(devm_gpio_free);
+
+static void devm_gpio_chip_release(void *data)
+{
+ struct gpio_chip *gc = data;
+
+ gpiochip_remove(gc);
+}
+
+/**
+ * devm_gpiochip_add_data_with_key() - Resource managed gpiochip_add_data_with_key()
+ * @dev: pointer to the device that gpio_chip belongs to.
+ * @gc: the GPIO chip to register
+ * @data: driver-private data associated with this chip
+ * @lock_key: lockdep class for IRQ lock
+ * @request_key: lockdep class for IRQ request
+ *
+ * Context: potentially before irqs will work
+ *
+ * The gpio chip automatically be released when the device is unbound.
+ *
+ * Returns:
+ * A negative errno if the chip can't be registered, such as because the
+ * gc->base is invalid or already associated with a different chip.
+ * Otherwise it returns zero as a success code.
+ */
+int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, void *data,
+ struct lock_class_key *lock_key,
+ struct lock_class_key *request_key)
+{
+ int ret;
+
+ ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key);
+ if (ret < 0)
+ return ret;
+
+ return devm_add_action_or_reset(dev, devm_gpio_chip_release, gc);
+}
+EXPORT_SYMBOL_GPL(devm_gpiochip_add_data_with_key);
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 567fb98c0892..3d6c3ffd5576 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -21,6 +21,65 @@
#include <linux/gpio/machine.h>
#include "gpiolib.h"
+#include "gpiolib-of.h"
+
+/**
+ * of_gpio_spi_cs_get_count() - special GPIO counting for SPI
+ * @dev: Consuming device
+ * @con_id: Function within the GPIO consumer
+ *
+ * Some elder GPIO controllers need special quirks. Currently we handle
+ * the Freescale and PPC GPIO controller with bindings that doesn't use the
+ * established "cs-gpios" for chip selects but instead rely on
+ * "gpios" for the chip select lines. If we detect this, we redirect
+ * the counting of "cs-gpios" to count "gpios" transparent to the
+ * driver.
+ */
+static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id)
+{
+ struct device_node *np = dev->of_node;
+
+ if (!IS_ENABLED(CONFIG_SPI_MASTER))
+ return 0;
+ if (!con_id || strcmp(con_id, "cs"))
+ return 0;
+ if (!of_device_is_compatible(np, "fsl,spi") &&
+ !of_device_is_compatible(np, "aeroflexgaisler,spictrl") &&
+ !of_device_is_compatible(np, "ibm,ppc4xx-spi"))
+ return 0;
+ return of_gpio_named_count(np, "gpios");
+}
+
+/*
+ * This is used by external users of of_gpio_count() from <linux/of_gpio.h>
+ *
+ * FIXME: get rid of those external users by converting them to GPIO
+ * descriptors and let them all use gpiod_count()
+ */
+int of_gpio_get_count(struct device *dev, const char *con_id)
+{
+ int ret;
+ char propname[32];
+ unsigned int i;
+
+ ret = of_gpio_spi_cs_get_count(dev, con_id);
+ if (ret > 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
+ if (con_id)
+ snprintf(propname, sizeof(propname), "%s-%s",
+ con_id, gpio_suffixes[i]);
+ else
+ snprintf(propname, sizeof(propname), "%s",
+ gpio_suffixes[i]);
+
+ ret = of_gpio_named_count(dev->of_node, propname);
+ if (ret > 0)
+ break;
+ }
+ return ret ? ret : -ENOENT;
+}
static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
{
@@ -53,33 +112,30 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
return gpiochip_get_desc(chip, ret);
}
-static void of_gpio_flags_quirks(struct device_node *np,
+/**
+ * of_gpio_need_valid_mask() - figure out if the OF GPIO driver needs
+ * to set the .valid_mask
+ * @gc: the target gpio_chip
+ *
+ * Return: true if the valid mask needs to be set
+ */
+bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
+{
+ int size;
+ const struct device_node *np = gc->of_node;
+
+ size = of_property_count_u32_elems(np, "gpio-reserved-ranges");
+ if (size > 0 && size % 2 == 0)
+ return true;
+ return false;
+}
+
+static void of_gpio_flags_quirks(const struct device_node *np,
const char *propname,
enum of_gpio_flags *flags,
int index)
{
/*
- * Handle MMC "cd-inverted" and "wp-inverted" semantics.
- */
- if (IS_ENABLED(CONFIG_MMC)) {
- /*
- * Active low is the default according to the
- * SDHCI specification and the device tree
- * bindings. However the code in the current
- * kernel was written such that the phandle
- * flags were always respected, and "cd-inverted"
- * would invert the flag from the device phandle.
- */
- if (!strcmp(propname, "cd-gpios")) {
- if (of_property_read_bool(np, "cd-inverted"))
- *flags ^= OF_GPIO_ACTIVE_LOW;
- }
- if (!strcmp(propname, "wp-gpios")) {
- if (of_property_read_bool(np, "wp-inverted"))
- *flags ^= OF_GPIO_ACTIVE_LOW;
- }
- }
- /*
* Some GPIO fixed regulator quirks.
* Note that active low is the default.
*/
@@ -89,18 +145,20 @@ static void of_gpio_flags_quirks(struct device_node *np,
(!(strcmp(propname, "enable-gpio") &&
strcmp(propname, "enable-gpios")) &&
of_device_is_compatible(np, "regulator-gpio")))) {
+ bool active_low = !of_property_read_bool(np,
+ "enable-active-high");
/*
* The regulator GPIO handles are specified such that the
* presence or absence of "enable-active-high" solely controls
* the polarity of the GPIO line. Any phandle flags must
* be actively ignored.
*/
- if (*flags & OF_GPIO_ACTIVE_LOW) {
+ if ((*flags & OF_GPIO_ACTIVE_LOW) && !active_low) {
pr_warn("%s GPIO handle specifies active low - ignored\n",
of_node_full_name(np));
*flags &= ~OF_GPIO_ACTIVE_LOW;
}
- if (!of_property_read_bool(np, "enable-active-high"))
+ if (active_low)
*flags |= OF_GPIO_ACTIVE_LOW;
}
/*
@@ -178,7 +236,7 @@ static void of_gpio_flags_quirks(struct device_node *np,
* value on the error condition. If @flags is not NULL the function also fills
* in flags for the GPIO.
*/
-struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
+static struct gpio_desc *of_get_named_gpiod_flags(const struct device_node *np,
const char *propname, int index, enum of_gpio_flags *flags)
{
struct of_phandle_args gpiospec;
@@ -217,7 +275,7 @@ out:
return desc;
}
-int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
+int of_get_named_gpio_flags(const struct device_node *np, const char *list_name,
int index, enum of_gpio_flags *flags)
{
struct gpio_desc *desc;
@@ -229,7 +287,82 @@ int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
else
return desc_to_gpio(desc);
}
-EXPORT_SYMBOL(of_get_named_gpio_flags);
+EXPORT_SYMBOL_GPL(of_get_named_gpio_flags);
+
+/**
+ * gpiod_get_from_of_node() - obtain a GPIO from an OF node
+ * @node: handle of the OF node
+ * @propname: name of the DT property representing the GPIO
+ * @index: index of the GPIO to obtain for the consumer
+ * @dflags: GPIO initialization flags
+ * @label: label to attach to the requested GPIO
+ *
+ * Returns:
+ * On successful request the GPIO pin is configured in accordance with
+ * provided @dflags.
+ *
+ * In case of error an ERR_PTR() is returned.
+ */
+struct gpio_desc *gpiod_get_from_of_node(const struct device_node *node,
+ const char *propname, int index,
+ enum gpiod_flags dflags,
+ const char *label)
+{
+ unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
+ struct gpio_desc *desc;
+ enum of_gpio_flags flags;
+ bool active_low = false;
+ bool single_ended = false;
+ bool open_drain = false;
+ bool transitory = false;
+ int ret;
+
+ desc = of_get_named_gpiod_flags(node, propname,
+ index, &flags);
+
+ if (!desc || IS_ERR(desc)) {
+ return desc;
+ }
+
+ active_low = flags & OF_GPIO_ACTIVE_LOW;
+ single_ended = flags & OF_GPIO_SINGLE_ENDED;
+ open_drain = flags & OF_GPIO_OPEN_DRAIN;
+ transitory = flags & OF_GPIO_TRANSITORY;
+
+ ret = gpiod_request(desc, label);
+ if (ret == -EBUSY && (dflags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
+ return desc;
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (active_low)
+ lflags |= GPIO_ACTIVE_LOW;
+
+ if (single_ended) {
+ if (open_drain)
+ lflags |= GPIO_OPEN_DRAIN;
+ else
+ lflags |= GPIO_OPEN_SOURCE;
+ }
+
+ if (transitory)
+ lflags |= GPIO_TRANSITORY;
+
+ if (flags & OF_GPIO_PULL_UP)
+ lflags |= GPIO_PULL_UP;
+
+ if (flags & OF_GPIO_PULL_DOWN)
+ lflags |= GPIO_PULL_DOWN;
+
+ ret = gpiod_configure_flags(desc, propname, lflags, dflags);
+ if (ret < 0) {
+ gpiod_put(desc);
+ return ERR_PTR(ret);
+ }
+
+ return desc;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_from_of_node);
/*
* The SPI GPIO bindings happened before we managed to establish that GPIO
@@ -240,7 +373,7 @@ static struct gpio_desc *of_find_spi_gpio(struct device *dev, const char *con_id
enum of_gpio_flags *of_flags)
{
char prop_name[32]; /* 32 is max size of property name */
- struct device_node *np = dev->of_node;
+ const struct device_node *np = dev->of_node;
struct gpio_desc *desc;
/*
@@ -271,14 +404,15 @@ static struct gpio_desc *of_find_spi_cs_gpio(struct device *dev,
unsigned int idx,
unsigned long *flags)
{
- struct device_node *np = dev->of_node;
+ const struct device_node *np = dev->of_node;
if (!IS_ENABLED(CONFIG_SPI_MASTER))
return ERR_PTR(-ENOENT);
- /* Allow this specifically for Freescale devices */
+ /* Allow this specifically for Freescale and PPC devices */
if (!of_device_is_compatible(np, "fsl,spi") &&
- !of_device_is_compatible(np, "aeroflexgaisler,spictrl"))
+ !of_device_is_compatible(np, "aeroflexgaisler,spictrl") &&
+ !of_device_is_compatible(np, "ibm,ppc4xx-spi"))
return ERR_PTR(-ENOENT);
/* Allow only if asking for "cs-gpios" */
if (!con_id || strcmp(con_id, "cs"))
@@ -306,7 +440,7 @@ static struct gpio_desc *of_find_regulator_gpio(struct device *dev, const char *
"wlf,ldo1ena", /* WM8994 */
"wlf,ldo2ena", /* WM8994 */
};
- struct device_node *np = dev->of_node;
+ const struct device_node *np = dev->of_node;
struct gpio_desc *desc;
int i;
@@ -324,6 +458,37 @@ static struct gpio_desc *of_find_regulator_gpio(struct device *dev, const char *
return desc;
}
+static struct gpio_desc *of_find_arizona_gpio(struct device *dev,
+ const char *con_id,
+ enum of_gpio_flags *of_flags)
+{
+ if (!IS_ENABLED(CONFIG_MFD_ARIZONA))
+ return ERR_PTR(-ENOENT);
+
+ if (!con_id || strcmp(con_id, "wlf,reset"))
+ return ERR_PTR(-ENOENT);
+
+ return of_get_named_gpiod_flags(dev->of_node, con_id, 0, of_flags);
+}
+
+static struct gpio_desc *of_find_usb_gpio(struct device *dev,
+ const char *con_id,
+ enum of_gpio_flags *of_flags)
+{
+ /*
+ * Currently this USB quirk is only for the Fairchild FUSB302 host which is using
+ * an undocumented DT GPIO line named "fcs,int_n" without the compulsory "-gpios"
+ * suffix.
+ */
+ if (!IS_ENABLED(CONFIG_TYPEC_FUSB302))
+ return ERR_PTR(-ENOENT);
+
+ if (!con_id || strcmp(con_id, "fcs,int_n"))
+ return ERR_PTR(-ENOENT);
+
+ return of_get_named_gpiod_flags(dev->of_node, con_id, 0, of_flags);
+}
+
struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
unsigned int idx, unsigned long *flags)
{
@@ -343,36 +508,33 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
&of_flags);
- /*
- * -EPROBE_DEFER in our case means that we found a
- * valid GPIO property, but no controller has been
- * registered so far.
- *
- * This means we don't need to look any further for
- * alternate name conventions, and we should really
- * preserve the return code for our user to be able to
- * retry probing later.
- */
- if (IS_ERR(desc) && PTR_ERR(desc) == -EPROBE_DEFER)
- return desc;
- if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT))
+ if (!gpiod_not_found(desc))
break;
}
- /* Special handling for SPI GPIOs if used */
- if (IS_ERR(desc))
+ if (gpiod_not_found(desc)) {
+ /* Special handling for SPI GPIOs if used */
desc = of_find_spi_gpio(dev, con_id, &of_flags);
- if (IS_ERR(desc)) {
+ }
+
+ if (gpiod_not_found(desc)) {
/* This quirk looks up flags and all */
desc = of_find_spi_cs_gpio(dev, con_id, idx, flags);
if (!IS_ERR(desc))
return desc;
}
- /* Special handling for regulator GPIOs if used */
- if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER)
+ if (gpiod_not_found(desc)) {
+ /* Special handling for regulator GPIOs if used */
desc = of_find_regulator_gpio(dev, con_id, &of_flags);
+ }
+
+ if (gpiod_not_found(desc))
+ desc = of_find_arizona_gpio(dev, con_id, &of_flags);
+
+ if (gpiod_not_found(desc))
+ desc = of_find_usb_gpio(dev, con_id, &of_flags);
if (IS_ERR(desc))
return desc;
@@ -431,7 +593,7 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
xlate_flags = 0;
*lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- *dflags = 0;
+ *dflags = GPIOD_ASIS;
ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp);
if (ret)
@@ -455,6 +617,10 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
*lflags |= GPIO_ACTIVE_LOW;
if (xlate_flags & OF_GPIO_TRANSITORY)
*lflags |= GPIO_TRANSITORY;
+ if (xlate_flags & OF_GPIO_PULL_UP)
+ *lflags |= GPIO_PULL_UP;
+ if (xlate_flags & OF_GPIO_PULL_DOWN)
+ *lflags |= GPIO_PULL_DOWN;
if (of_property_read_bool(np, "input"))
*dflags |= GPIOD_IN;
@@ -475,6 +641,39 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
}
/**
+ * of_gpiochip_add_hog - Add all hogs in a hog device node
+ * @chip: gpio chip to act on
+ * @hog: device node describing the hogs
+ *
+ * Returns error if it fails otherwise 0 on success.
+ */
+static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
+{
+ enum gpiod_flags dflags;
+ struct gpio_desc *desc;
+ unsigned long lflags;
+ const char *name;
+ unsigned int i;
+ int ret;
+
+ for (i = 0;; i++) {
+ desc = of_parse_own_gpio(hog, chip, i, &name, &lflags, &dflags);
+ if (IS_ERR(desc))
+ break;
+
+ ret = gpiod_hog(desc, name, lflags, dflags);
+ if (ret < 0)
+ return ret;
+
+#ifdef CONFIG_OF_DYNAMIC
+ desc->hog = hog;
+#endif
+ }
+
+ return 0;
+}
+
+/**
* of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions
* @chip: gpio chip to act on
*
@@ -484,35 +683,106 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
*/
static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
{
- struct gpio_desc *desc = NULL;
struct device_node *np;
- const char *name;
- unsigned long lflags;
- enum gpiod_flags dflags;
- unsigned int i;
int ret;
for_each_available_child_of_node(chip->of_node, np) {
if (!of_property_read_bool(np, "gpio-hog"))
continue;
- for (i = 0;; i++) {
- desc = of_parse_own_gpio(np, chip, i, &name, &lflags,
- &dflags);
- if (IS_ERR(desc))
- break;
-
- ret = gpiod_hog(desc, name, lflags, dflags);
- if (ret < 0) {
- of_node_put(np);
- return ret;
- }
+ ret = of_gpiochip_add_hog(chip, np);
+ if (ret < 0) {
+ of_node_put(np);
+ return ret;
}
+
+ of_node_set_flag(np, OF_POPULATED);
}
return 0;
}
+#ifdef CONFIG_OF_DYNAMIC
+/**
+ * of_gpiochip_remove_hog - Remove all hogs in a hog device node
+ * @chip: gpio chip to act on
+ * @hog: device node describing the hogs
+ */
+static void of_gpiochip_remove_hog(struct gpio_chip *chip,
+ struct device_node *hog)
+{
+ struct gpio_desc *desc;
+
+ for_each_gpio_desc_with_flag(chip, desc, FLAG_IS_HOGGED)
+ if (desc->hog == hog)
+ gpiochip_free_own_desc(desc);
+}
+
+static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
+{
+ return chip->gpiodev->dev.of_node == data;
+}
+
+static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
+{
+ return gpiochip_find(np, of_gpiochip_match_node);
+}
+
+static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
+ void *arg)
+{
+ struct of_reconfig_data *rd = arg;
+ struct gpio_chip *chip;
+ int ret;
+
+ /*
+ * This only supports adding and removing complete gpio-hog nodes.
+ * Modifying an existing gpio-hog node is not supported (except for
+ * changing its "status" property, which is treated the same as
+ * addition/removal).
+ */
+ switch (of_reconfig_get_state_change(action, arg)) {
+ case OF_RECONFIG_CHANGE_ADD:
+ if (!of_property_read_bool(rd->dn, "gpio-hog"))
+ return NOTIFY_OK; /* not for us */
+
+ if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK;
+
+ chip = of_find_gpiochip_by_node(rd->dn->parent);
+ if (chip == NULL)
+ return NOTIFY_OK; /* not for us */
+
+ ret = of_gpiochip_add_hog(chip, rd->dn);
+ if (ret < 0) {
+ pr_err("%s: failed to add hogs for %pOF\n", __func__,
+ rd->dn);
+ of_node_clear_flag(rd->dn, OF_POPULATED);
+ return notifier_from_errno(ret);
+ }
+ break;
+
+ case OF_RECONFIG_CHANGE_REMOVE:
+ if (!of_node_check_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK; /* already depopulated */
+
+ chip = of_find_gpiochip_by_node(rd->dn->parent);
+ if (chip == NULL)
+ return NOTIFY_OK; /* not for us */
+
+ of_gpiochip_remove_hog(chip, rd->dn);
+ of_node_clear_flag(rd->dn, OF_POPULATED);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+struct notifier_block gpio_of_notifier = {
+ .notifier_call = of_gpio_notify,
+};
+#endif /* CONFIG_OF_DYNAMIC */
+
/**
* of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags
* @gc: pointer to the gpio_chip structure
@@ -523,8 +793,9 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
* GPIO chips. This function performs only one sanity check: whether GPIO
* is less than ngpios (that is specified in the gpio_chip).
*/
-int of_gpio_simple_xlate(struct gpio_chip *gc,
- const struct of_phandle_args *gpiospec, u32 *flags)
+static int of_gpio_simple_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
{
/*
* We're discouraging gpio_cells < 2, since that way you'll have to
@@ -548,7 +819,6 @@ int of_gpio_simple_xlate(struct gpio_chip *gc,
return gpiospec->args[0];
}
-EXPORT_SYMBOL(of_gpio_simple_xlate);
/**
* of_mm_gpiochip_add_data - Add memory mapped GPIO chip (bank)
@@ -605,7 +875,7 @@ err0:
pr_err("%pOF: GPIO chip registration failed with status %d\n", np, ret);
return ret;
}
-EXPORT_SYMBOL(of_mm_gpiochip_add_data);
+EXPORT_SYMBOL_GPL(of_mm_gpiochip_add_data);
/**
* of_mm_gpiochip_remove - Remove memory mapped GPIO chip (bank)
@@ -622,7 +892,7 @@ void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
iounmap(mm_gc->regs);
kfree(gc->label);
}
-EXPORT_SYMBOL(of_mm_gpiochip_remove);
+EXPORT_SYMBOL_GPL(of_mm_gpiochip_remove);
static void of_gpiochip_init_valid_mask(struct gpio_chip *chip)
{
@@ -639,7 +909,7 @@ static void of_gpiochip_init_valid_mask(struct gpio_chip *chip)
i, &start);
of_property_read_u32_index(np, "gpio-reserved-ranges",
i + 1, &count);
- if (start >= chip->ngpio || start + count >= chip->ngpio)
+ if (start >= chip->ngpio || start + count > chip->ngpio)
continue;
bitmap_clear(chip->valid_mask, start, count);
@@ -660,6 +930,11 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (!np)
return 0;
+ if (!of_property_read_bool(np, "gpio-ranges") &&
+ chip->of_gpio_ranges_fallback) {
+ return chip->of_gpio_ranges_fallback(chip, np);
+ }
+
group_names = of_find_property(np, group_names_propname, NULL);
for (;; index++) {
@@ -734,7 +1009,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { return 0; }
int of_gpiochip_add(struct gpio_chip *chip)
{
- int status;
+ int ret;
if (!chip->of_node)
return 0;
@@ -749,28 +1024,36 @@ int of_gpiochip_add(struct gpio_chip *chip)
of_gpiochip_init_valid_mask(chip);
- status = of_gpiochip_add_pin_range(chip);
- if (status)
- return status;
-
- /* If the chip defines names itself, these take precedence */
- if (!chip->names)
- devprop_gpiochip_set_names(chip,
- of_fwnode_handle(chip->of_node));
+ ret = of_gpiochip_add_pin_range(chip);
+ if (ret)
+ return ret;
of_node_get(chip->of_node);
- status = of_gpiochip_scan_gpios(chip);
- if (status) {
+ ret = of_gpiochip_scan_gpios(chip);
+ if (ret)
of_node_put(chip->of_node);
- gpiochip_remove_pin_ranges(chip);
- }
- return status;
+ return ret;
}
void of_gpiochip_remove(struct gpio_chip *chip)
{
- gpiochip_remove_pin_ranges(chip);
of_node_put(chip->of_node);
}
+
+void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev)
+{
+ /* Set default OF node to parent's one if present */
+ if (gc->parent)
+ gdev->dev.of_node = gc->parent->of_node;
+
+ if (gc->fwnode)
+ gc->of_node = to_of_node(gc->fwnode);
+
+ /* If the gpiochip has an assigned OF node this takes precedence */
+ if (gc->of_node)
+ gdev->dev.of_node = gc->of_node;
+ else
+ gc->of_node = gdev->dev.of_node;
+}
diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
new file mode 100644
index 000000000000..8af2bc899aab
--- /dev/null
+++ b/drivers/gpio/gpiolib-of.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef GPIOLIB_OF_H
+#define GPIOLIB_OF_H
+
+struct gpio_chip;
+enum of_gpio_flags;
+
+#ifdef CONFIG_OF_GPIO
+struct gpio_desc *of_find_gpio(struct device *dev,
+ const char *con_id,
+ unsigned int idx,
+ unsigned long *lookupflags);
+int of_gpiochip_add(struct gpio_chip *gc);
+void of_gpiochip_remove(struct gpio_chip *gc);
+int of_gpio_get_count(struct device *dev, const char *con_id);
+bool of_gpio_need_valid_mask(const struct gpio_chip *gc);
+void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev);
+#else
+static inline struct gpio_desc *of_find_gpio(struct device *dev,
+ const char *con_id,
+ unsigned int idx,
+ unsigned long *lookupflags)
+{
+ return ERR_PTR(-ENOENT);
+}
+static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; }
+static inline void of_gpiochip_remove(struct gpio_chip *gc) { }
+static inline int of_gpio_get_count(struct device *dev, const char *con_id)
+{
+ return 0;
+}
+static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
+{
+ return false;
+}
+static inline void of_gpio_dev_init(struct gpio_chip *gc,
+ struct gpio_device *gdev)
+{
+}
+#endif /* CONFIG_OF_GPIO */
+
+extern struct notifier_block gpio_of_notifier;
+
+#endif /* GPIOLIB_OF_H */
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index fbf6b1a0a4fa..cd27bf173dec 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -11,7 +11,9 @@
#include <linux/ctype.h>
#include "gpiolib.h"
+#include "gpiolib-sysfs.h"
+#define GPIO_IRQF_TRIGGER_NONE 0
#define GPIO_IRQF_TRIGGER_FALLING BIT(0)
#define GPIO_IRQF_TRIGGER_RISING BIT(1)
#define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \
@@ -60,18 +62,16 @@ static ssize_t direction_show(struct device *dev,
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
- ssize_t status;
+ int value;
mutex_lock(&data->mutex);
gpiod_get_direction(desc);
- status = sprintf(buf, "%s\n",
- test_bit(FLAG_IS_OUT, &desc->flags)
- ? "out" : "in");
+ value = !!test_bit(FLAG_IS_OUT, &desc->flags);
mutex_unlock(&data->mutex);
- return status;
+ return sysfs_emit(buf, "%s\n", value ? "out" : "in");
}
static ssize_t direction_store(struct device *dev,
@@ -108,16 +108,13 @@ static ssize_t value_show(struct device *dev,
mutex_lock(&data->mutex);
status = gpiod_get_value_cansleep(desc);
- if (status < 0)
- goto err;
- buf[0] = '0' + status;
- buf[1] = '\n';
- status = 2;
-err:
mutex_unlock(&data->mutex);
- return status;
+ if (status < 0)
+ return status;
+
+ return sysfs_emit(buf, "%zd\n", status);
}
static ssize_t value_store(struct device *dev,
@@ -125,24 +122,18 @@ static ssize_t value_store(struct device *dev,
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
- ssize_t status = 0;
+ ssize_t status;
+ long value;
+
+ status = kstrtol(buf, 0, &value);
mutex_lock(&data->mutex);
if (!test_bit(FLAG_IS_OUT, &desc->flags)) {
status = -EPERM;
- } else {
- long value;
-
- if (size <= 2 && isdigit(buf[0]) &&
- (size == 1 || buf[1] == '\n'))
- value = buf[0] - '0';
- else
- status = kstrtol(buf, 0, &value);
- if (status == 0) {
- gpiod_set_value_cansleep(desc, value);
- status = size;
- }
+ } else if (status == 0) {
+ gpiod_set_value_cansleep(desc, value);
+ status = size;
}
mutex_unlock(&data->mutex);
@@ -228,54 +219,41 @@ static void gpio_sysfs_free_irq(struct device *dev)
sysfs_put(data->value_kn);
}
-static const struct {
- const char *name;
- unsigned char flags;
-} trigger_types[] = {
- { "none", 0 },
- { "falling", GPIO_IRQF_TRIGGER_FALLING },
- { "rising", GPIO_IRQF_TRIGGER_RISING },
- { "both", GPIO_IRQF_TRIGGER_BOTH },
+static const char * const trigger_names[] = {
+ [GPIO_IRQF_TRIGGER_NONE] = "none",
+ [GPIO_IRQF_TRIGGER_FALLING] = "falling",
+ [GPIO_IRQF_TRIGGER_RISING] = "rising",
+ [GPIO_IRQF_TRIGGER_BOTH] = "both",
};
static ssize_t edge_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpiod_data *data = dev_get_drvdata(dev);
- ssize_t status = 0;
- int i;
+ int flags;
mutex_lock(&data->mutex);
- for (i = 0; i < ARRAY_SIZE(trigger_types); i++) {
- if (data->irq_flags == trigger_types[i].flags) {
- status = sprintf(buf, "%s\n", trigger_types[i].name);
- break;
- }
- }
+ flags = data->irq_flags;
mutex_unlock(&data->mutex);
- return status;
+ if (flags >= ARRAY_SIZE(trigger_names))
+ return 0;
+
+ return sysfs_emit(buf, "%s\n", trigger_names[flags]);
}
static ssize_t edge_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct gpiod_data *data = dev_get_drvdata(dev);
- unsigned char flags;
ssize_t status = size;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(trigger_types); i++) {
- if (sysfs_streq(trigger_types[i].name, buf))
- break;
- }
+ int flags;
- if (i == ARRAY_SIZE(trigger_types))
- return -EINVAL;
-
- flags = trigger_types[i].flags;
+ flags = sysfs_match_string(trigger_names, buf);
+ if (flags < 0)
+ return flags;
mutex_lock(&data->mutex);
@@ -311,10 +289,7 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value)
if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)
return 0;
- if (value)
- set_bit(FLAG_ACTIVE_LOW, &desc->flags);
- else
- clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ assign_bit(FLAG_ACTIVE_LOW, &desc->flags, value);
/* reconfigure poll(2) support if enabled on one edge only */
if (flags == GPIO_IRQF_TRIGGER_FALLING ||
@@ -331,16 +306,15 @@ static ssize_t active_low_show(struct device *dev,
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
- ssize_t status;
+ int value;
mutex_lock(&data->mutex);
- status = sprintf(buf, "%d\n",
- !!test_bit(FLAG_ACTIVE_LOW, &desc->flags));
+ value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags);
mutex_unlock(&data->mutex);
- return status;
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t active_low_store(struct device *dev,
@@ -350,11 +324,13 @@ static ssize_t active_low_store(struct device *dev,
ssize_t status;
long value;
+ status = kstrtol(buf, 0, &value);
+ if (status)
+ return status;
+
mutex_lock(&data->mutex);
- status = kstrtol(buf, 0, &value);
- if (status == 0)
- status = gpio_sysfs_set_active_low(dev, value);
+ status = gpio_sysfs_set_active_low(dev, value);
mutex_unlock(&data->mutex);
@@ -365,7 +341,7 @@ static DEVICE_ATTR_RW(active_low);
static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
int n)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
umode_t mode = attr->mode;
@@ -414,7 +390,7 @@ static ssize_t base_show(struct device *dev,
{
const struct gpio_chip *chip = dev_get_drvdata(dev);
- return sprintf(buf, "%d\n", chip->base);
+ return sysfs_emit(buf, "%d\n", chip->base);
}
static DEVICE_ATTR_RO(base);
@@ -423,7 +399,7 @@ static ssize_t label_show(struct device *dev,
{
const struct gpio_chip *chip = dev_get_drvdata(dev);
- return sprintf(buf, "%s\n", chip->label ? : "");
+ return sysfs_emit(buf, "%s\n", chip->label ?: "");
}
static DEVICE_ATTR_RO(label);
@@ -432,7 +408,7 @@ static ssize_t ngpio_show(struct device *dev,
{
const struct gpio_chip *chip = dev_get_drvdata(dev);
- return sprintf(buf, "%u\n", chip->ngpio);
+ return sysfs_emit(buf, "%u\n", chip->ngpio);
}
static DEVICE_ATTR_RO(ngpio);
@@ -457,6 +433,8 @@ static ssize_t export_store(struct class *class,
long gpio;
struct gpio_desc *desc;
int status;
+ struct gpio_chip *gc;
+ int offset;
status = kstrtol(buf, 0, &gpio);
if (status < 0)
@@ -468,18 +446,21 @@ static ssize_t export_store(struct class *class,
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
return -EINVAL;
}
+ gc = desc->gdev->chip;
+ offset = gpio_chip_hwgpio(desc);
+ if (!gpiochip_line_is_valid(gc, offset)) {
+ pr_warn("%s: GPIO %ld masked\n", __func__, gpio);
+ return -EINVAL;
+ }
/* No extra locking here; FLAG_SYSFS just signifies that the
* request and export were done by on behalf of userspace, so
* they may be undone on its behalf too.
*/
- status = gpiod_request(desc, "sysfs");
- if (status < 0) {
- if (status == -EPROBE_DEFER)
- status = -ENODEV;
+ status = gpiod_request_user(desc, "sysfs");
+ if (status)
goto done;
- }
status = gpiod_set_transitory(desc, false);
if (!status) {
@@ -762,10 +743,9 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
parent = &gdev->dev;
/* use chip->base for the ID; it's already known to be unique */
- dev = device_create_with_groups(&gpio_class, parent,
- MKDEV(0, 0),
- chip, gpiochip_groups,
- "gpiochip%d", chip->base);
+ dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip,
+ gpiochip_groups, GPIOCHIP_NAME "%d",
+ chip->base);
if (IS_ERR(dev))
return PTR_ERR(dev);
@@ -780,7 +760,6 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{
struct gpio_desc *desc;
struct gpio_chip *chip = gdev->chip;
- unsigned int i;
if (!gdev->mockdev)
return;
@@ -793,11 +772,8 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
mutex_unlock(&sysfs_lock);
/* unregister gpiod class devices owned by sysfs */
- for (i = 0; i < chip->ngpio; i++) {
- desc = &gdev->descs[i];
- if (test_and_clear_bit(FLAG_SYSFS, &desc->flags))
- gpiod_free(desc);
- }
+ for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS)
+ gpiod_free(desc);
}
static int __init gpiolib_sysfs_init(void)
diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h
new file mode 100644
index 000000000000..ddd0e503f8eb
--- /dev/null
+++ b/drivers/gpio/gpiolib-sysfs.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef GPIOLIB_SYSFS_H
+#define GPIOLIB_SYSFS_H
+
+#ifdef CONFIG_GPIO_SYSFS
+
+int gpiochip_sysfs_register(struct gpio_device *gdev);
+void gpiochip_sysfs_unregister(struct gpio_device *gdev);
+
+#else
+
+static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
+{
+ return 0;
+}
+
+static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
+{
+}
+
+#endif /* CONFIG_GPIO_SYSFS */
+
+#endif /* GPIOLIB_SYSFS_H */
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 3ee99d070608..9535f48e18d1 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+
#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -11,25 +12,22 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/pinctrl/consumer.h>
-#include <linux/cdev.h>
#include <linux/fs.h>
-#include <linux/uaccess.h>
#include <linux/compat.h>
-#include <linux/anon_inodes.h>
#include <linux/file.h>
-#include <linux/kfifo.h>
-#include <linux/poll.h>
-#include <linux/timekeeping.h>
#include <uapi/linux/gpio.h>
#include "gpiolib.h"
+#include "gpiolib-of.h"
+#include "gpiolib-acpi.h"
+#include "gpiolib-cdev.h"
+#include "gpiolib-sysfs.h"
#define CREATE_TRACE_POINTS
#include <trace/events/gpio.h>
@@ -58,8 +56,10 @@
static DEFINE_IDA(gpio_ida);
static dev_t gpio_devt;
#define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */
+static int gpio_bus_match(struct device *dev, struct device_driver *drv);
static struct bus_type gpio_bus_type = {
.name = "gpio",
+ .match = gpio_bus_match,
};
/*
@@ -80,13 +80,14 @@ LIST_HEAD(gpio_devices);
static DEFINE_MUTEX(gpio_machine_hogs_mutex);
static LIST_HEAD(gpio_machine_hogs);
-static void gpiochip_free_hogs(struct gpio_chip *chip);
-static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
+static void gpiochip_free_hogs(struct gpio_chip *gc);
+static int gpiochip_add_irqchip(struct gpio_chip *gc,
struct lock_class_key *lock_key,
struct lock_class_key *request_key);
-static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
-static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip);
-static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip);
+static void gpiochip_irqchip_remove(struct gpio_chip *gc);
+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;
@@ -121,7 +122,7 @@ struct gpio_desc *gpio_to_desc(unsigned gpio)
spin_unlock_irqrestore(&gpio_lock, flags);
if (!gpio_is_valid(gpio))
- WARN(1, "invalid GPIO %d\n", gpio);
+ pr_warn("invalid GPIO %d\n", gpio);
return NULL;
}
@@ -130,23 +131,24 @@ EXPORT_SYMBOL_GPL(gpio_to_desc);
/**
* gpiochip_get_desc - get the GPIO descriptor corresponding to the given
* hardware number for this chip
- * @chip: GPIO chip
+ * @gc: GPIO chip
* @hwnum: hardware number of the GPIO for this chip
*
* Returns:
- * A pointer to the GPIO descriptor or %ERR_PTR(-EINVAL) if no GPIO exists
+ * A pointer to the GPIO descriptor or ``ERR_PTR(-EINVAL)`` if no GPIO exists
* in the given chip for the specified hardware number.
*/
-struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
- u16 hwnum)
+struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc,
+ unsigned int hwnum)
{
- struct gpio_device *gdev = chip->gpiodev;
+ struct gpio_device *gdev = gc->gpiodev;
if (hwnum >= gdev->ngpio)
return ERR_PTR(-EINVAL);
return &gdev->descs[hwnum];
}
+EXPORT_SYMBOL_GPL(gpiochip_get_desc);
/**
* desc_to_gpio - convert a GPIO descriptor to the integer namespace
@@ -187,9 +189,8 @@ static int gpiochip_find_base(int ngpio)
/* found a free space? */
if (gdev->base + gdev->ngpio <= base)
break;
- else
- /* nope, check the space right before the chip */
- base = gdev->base - ngpio;
+ /* nope, check the space right before the chip */
+ base = gdev->base - ngpio;
}
if (gpio_is_valid(base)) {
@@ -211,27 +212,35 @@ static int gpiochip_find_base(int ngpio)
*/
int gpiod_get_direction(struct gpio_desc *desc)
{
- struct gpio_chip *chip;
- unsigned offset;
- int status;
+ struct gpio_chip *gc;
+ unsigned int offset;
+ int ret;
- chip = gpiod_to_chip(desc);
+ gc = gpiod_to_chip(desc);
offset = gpio_chip_hwgpio(desc);
- if (!chip->get_direction)
+ /*
+ * Open drain emulation using input mode may incorrectly report
+ * input here, fix that up.
+ */
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags) &&
+ test_bit(FLAG_IS_OUT, &desc->flags))
+ return 0;
+
+ if (!gc->get_direction)
return -ENOTSUPP;
- status = chip->get_direction(chip, offset);
- if (status > 0) {
- /* GPIOF_DIR_IN, or other positive */
- status = 1;
- clear_bit(FLAG_IS_OUT, &desc->flags);
- }
- if (status == 0) {
- /* GPIOF_DIR_OUT */
- set_bit(FLAG_IS_OUT, &desc->flags);
- }
- return status;
+ ret = gc->get_direction(gc, offset);
+ if (ret < 0)
+ return ret;
+
+ /* GPIOF_DIR_IN or other positive, otherwise GPIOF_DIR_OUT */
+ if (ret > 0)
+ ret = 1;
+
+ assign_bit(FLAG_IS_OUT, &desc->flags, !ret);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(gpiod_get_direction);
@@ -252,14 +261,14 @@ static int gpiodev_add_to_list(struct gpio_device *gdev)
return 0;
}
- next = list_entry(gpio_devices.next, struct gpio_device, list);
+ next = list_first_entry(&gpio_devices, struct gpio_device, list);
if (gdev->base + gdev->ngpio <= next->base) {
/* add before first entry */
list_add(&gdev->list, &gpio_devices);
return 0;
}
- prev = list_entry(gpio_devices.prev, struct gpio_device, list);
+ prev = list_last_entry(&gpio_devices, struct gpio_device, list);
if (prev->base + prev->ngpio <= gdev->base) {
/* add behind last entry */
list_add_tail(&gdev->list, &gpio_devices);
@@ -279,30 +288,30 @@ static int gpiodev_add_to_list(struct gpio_device *gdev)
}
}
- dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chip\n");
return -EBUSY;
}
/*
* Convert a GPIO name to its descriptor
+ * Note that there is no guarantee that GPIO names are globally unique!
+ * Hence this function will return, if it exists, a reference to the first GPIO
+ * line found that matches the given name.
*/
static struct gpio_desc *gpio_name_to_desc(const char * const name)
{
struct gpio_device *gdev;
unsigned long flags;
+ if (!name)
+ return NULL;
+
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(gdev, &gpio_devices, list) {
- int i;
-
- for (i = 0; i != gdev->ngpio; ++i) {
- struct gpio_desc *desc = &gdev->descs[i];
-
- if (!desc->name || !name)
- continue;
+ struct gpio_desc *desc;
- if (!strcmp(desc->name, name)) {
+ for_each_gpio_desc(gdev->chip, desc) {
+ if (desc->name && !strcmp(desc->name, name)) {
spin_unlock_irqrestore(&gpio_lock, flags);
return desc;
}
@@ -315,19 +324,18 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
}
/*
- * Takes the names from gc->names and checks if they are all unique. If they
- * are, they are assigned to their gpio descriptors.
+ * Take the names from gc->names and assign them to their GPIO descriptors.
+ * Warn if a name is already used for a GPIO line on a different GPIO chip.
*
- * Warning if one of the names is already used for a different GPIO.
+ * Note that:
+ * 1. Non-unique names are still accepted,
+ * 2. Name collisions within the same GPIO chip are not reported.
*/
static int gpiochip_set_desc_names(struct gpio_chip *gc)
{
struct gpio_device *gdev = gc->gpiodev;
int i;
- if (!gc->names)
- return 0;
-
/* First check all names if they are unique */
for (i = 0; i != gc->ngpio; ++i) {
struct gpio_desc *gpio;
@@ -346,871 +354,203 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
return 0;
}
-static unsigned long *gpiochip_allocate_mask(struct gpio_chip *chip)
-{
- unsigned long *p;
-
- p = kmalloc_array(BITS_TO_LONGS(chip->ngpio), sizeof(*p), GFP_KERNEL);
- if (!p)
- return NULL;
-
- /* Assume by default all GPIOs are valid */
- bitmap_fill(p, chip->ngpio);
-
- return p;
-}
-
-static int gpiochip_alloc_valid_mask(struct gpio_chip *gpiochip)
-{
-#ifdef CONFIG_OF_GPIO
- int size;
- struct device_node *np = gpiochip->of_node;
-
- size = of_property_count_u32_elems(np, "gpio-reserved-ranges");
- if (size > 0 && size % 2 == 0)
- gpiochip->need_valid_mask = true;
-#endif
-
- if (!gpiochip->need_valid_mask)
- return 0;
-
- gpiochip->valid_mask = gpiochip_allocate_mask(gpiochip);
- if (!gpiochip->valid_mask)
- return -ENOMEM;
-
- return 0;
-}
-
-static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip)
-{
- if (gpiochip->init_valid_mask)
- return gpiochip->init_valid_mask(gpiochip);
-
- return 0;
-}
-
-static void gpiochip_free_valid_mask(struct gpio_chip *gpiochip)
-{
- kfree(gpiochip->valid_mask);
- gpiochip->valid_mask = NULL;
-}
-
-bool gpiochip_line_is_valid(const struct gpio_chip *gpiochip,
- unsigned int offset)
-{
- /* No mask means all valid */
- if (likely(!gpiochip->valid_mask))
- return true;
- return test_bit(offset, gpiochip->valid_mask);
-}
-EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
-
/*
- * GPIO line handle management
- */
-
-/**
- * struct linehandle_state - contains the state of a userspace handle
- * @gdev: the GPIO device the handle pertains to
- * @label: consumer label used to tag descriptors
- * @descs: the GPIO descriptors held by this handle
- * @numdescs: the number of descriptors held in the descs array
+ * devprop_gpiochip_set_names - Set GPIO line names using device properties
+ * @chip: GPIO chip whose lines should be named, if possible
+ *
+ * Looks for device property "gpio-line-names" and if it exists assigns
+ * GPIO line names for the chip. The memory allocated for the assigned
+ * names belong to the underlying firmware node and should not be released
+ * by the caller.
*/
-struct linehandle_state {
- struct gpio_device *gdev;
- const char *label;
- struct gpio_desc *descs[GPIOHANDLES_MAX];
- u32 numdescs;
-};
-
-#define GPIOHANDLE_REQUEST_VALID_FLAGS \
- (GPIOHANDLE_REQUEST_INPUT | \
- GPIOHANDLE_REQUEST_OUTPUT | \
- GPIOHANDLE_REQUEST_ACTIVE_LOW | \
- GPIOHANDLE_REQUEST_OPEN_DRAIN | \
- GPIOHANDLE_REQUEST_OPEN_SOURCE)
-
-static long linehandle_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg)
+static int devprop_gpiochip_set_names(struct gpio_chip *chip)
{
- struct linehandle_state *lh = filep->private_data;
- void __user *ip = (void __user *)arg;
- struct gpiohandle_data ghd;
- DECLARE_BITMAP(vals, GPIOHANDLES_MAX);
- int i;
-
- if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
- /* NOTE: It's ok to read values of output lines. */
- int ret = gpiod_get_array_value_complex(false,
- true,
- lh->numdescs,
- lh->descs,
- NULL,
- vals);
- if (ret)
- return ret;
-
- memset(&ghd, 0, sizeof(ghd));
- for (i = 0; i < lh->numdescs; i++)
- ghd.values[i] = test_bit(i, vals);
-
- if (copy_to_user(ip, &ghd, sizeof(ghd)))
- return -EFAULT;
+ struct gpio_device *gdev = chip->gpiodev;
+ struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
+ const char **names;
+ int ret, i;
+ int count;
+ count = fwnode_property_string_array_count(fwnode, "gpio-line-names");
+ if (count < 0)
return 0;
- } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) {
- /*
- * All line descriptors were created at once with the same
- * flags so just check if the first one is really output.
- */
- if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags))
- return -EPERM;
-
- if (copy_from_user(&ghd, ip, sizeof(ghd)))
- return -EFAULT;
-
- /* Clamp all values to [0,1] */
- for (i = 0; i < lh->numdescs; i++)
- __assign_bit(i, vals, ghd.values[i]);
-
- /* Reuse the array setting function */
- return gpiod_set_array_value_complex(false,
- true,
- lh->numdescs,
- lh->descs,
- NULL,
- vals);
- }
- return -EINVAL;
-}
-
-#ifdef CONFIG_COMPAT
-static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd,
- unsigned long arg)
-{
- return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
-}
-#endif
-
-static int linehandle_release(struct inode *inode, struct file *filep)
-{
- struct linehandle_state *lh = filep->private_data;
- struct gpio_device *gdev = lh->gdev;
- int i;
-
- for (i = 0; i < lh->numdescs; i++)
- gpiod_free(lh->descs[i]);
- kfree(lh->label);
- kfree(lh);
- put_device(&gdev->dev);
- return 0;
-}
-
-static const struct file_operations linehandle_fileops = {
- .release = linehandle_release,
- .owner = THIS_MODULE,
- .llseek = noop_llseek,
- .unlocked_ioctl = linehandle_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = linehandle_ioctl_compat,
-#endif
-};
-
-static int linehandle_create(struct gpio_device *gdev, void __user *ip)
-{
- struct gpiohandle_request handlereq;
- struct linehandle_state *lh;
- struct file *file;
- int fd, i, count = 0, ret;
- u32 lflags;
-
- if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
- return -EFAULT;
- if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX))
- return -EINVAL;
-
- lflags = handlereq.flags;
-
- /* Return an error if an unknown flag is set */
- if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS)
- return -EINVAL;
/*
- * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If
- * the hardware actually supports enabling both at the same time the
- * electrical result would be disastrous.
+ * When offset is set in the driver side we assume the driver internally
+ * is using more than one gpiochip per the same device. We have to stop
+ * setting friendly names if the specified ones with 'gpio-line-names'
+ * are less than the offset in the device itself. This means all the
+ * lines are not present for every single pin within all the internal
+ * gpiochips.
*/
- if ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) &&
- (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))
- return -EINVAL;
-
- /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */
- if (!(lflags & GPIOHANDLE_REQUEST_OUTPUT) &&
- ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
- (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)))
- return -EINVAL;
-
- lh = kzalloc(sizeof(*lh), GFP_KERNEL);
- if (!lh)
- return -ENOMEM;
- lh->gdev = gdev;
- get_device(&gdev->dev);
-
- /* Make sure this is terminated */
- handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0';
- if (strlen(handlereq.consumer_label)) {
- lh->label = kstrdup(handlereq.consumer_label,
- GFP_KERNEL);
- if (!lh->label) {
- ret = -ENOMEM;
- goto out_free_lh;
- }
+ if (count <= chip->offset) {
+ dev_warn(&gdev->dev, "gpio-line-names too short (length %d), cannot map names for the gpiochip at offset %u\n",
+ count, chip->offset);
+ return 0;
}
- /* Request each GPIO */
- for (i = 0; i < handlereq.lines; i++) {
- u32 offset = handlereq.lineoffsets[i];
- struct gpio_desc *desc;
-
- if (offset >= gdev->ngpio) {
- ret = -EINVAL;
- goto out_free_descs;
- }
-
- desc = &gdev->descs[offset];
- ret = gpiod_request(desc, lh->label);
- if (ret)
- goto out_free_descs;
- lh->descs[i] = desc;
- count = i + 1;
-
- if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
- set_bit(FLAG_ACTIVE_LOW, &desc->flags);
- if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN)
- set_bit(FLAG_OPEN_DRAIN, &desc->flags);
- if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
- set_bit(FLAG_OPEN_SOURCE, &desc->flags);
-
- ret = gpiod_set_transitory(desc, false);
- if (ret < 0)
- goto out_free_descs;
-
- /*
- * Lines have to be requested explicitly for input
- * or output, else the line will be treated "as is".
- */
- if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
- int val = !!handlereq.default_values[i];
+ names = kcalloc(count, sizeof(*names), GFP_KERNEL);
+ if (!names)
+ return -ENOMEM;
- ret = gpiod_direction_output(desc, val);
- if (ret)
- goto out_free_descs;
- } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
- ret = gpiod_direction_input(desc);
- if (ret)
- goto out_free_descs;
- }
- dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
- offset);
- }
- /* Let i point at the last handle */
- i--;
- lh->numdescs = handlereq.lines;
-
- fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
- ret = fd;
- goto out_free_descs;
+ ret = fwnode_property_read_string_array(fwnode, "gpio-line-names",
+ names, count);
+ if (ret < 0) {
+ dev_warn(&gdev->dev, "failed to read GPIO line names\n");
+ kfree(names);
+ return ret;
}
- file = anon_inode_getfile("gpio-linehandle",
- &linehandle_fileops,
- lh,
- O_RDONLY | O_CLOEXEC);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto out_put_unused_fd;
- }
+ /*
+ * When more that one gpiochip per device is used, 'count' can
+ * contain at most number gpiochips x chip->ngpio. We have to
+ * correctly distribute all defined lines taking into account
+ * chip->offset as starting point from where we will assign
+ * the names to pins from the 'names' array. Since property
+ * 'gpio-line-names' cannot contains gaps, we have to be sure
+ * we only assign those pins that really exists since chip->ngpio
+ * can be different of the chip->offset.
+ */
+ count = (count > chip->offset) ? count - chip->offset : count;
+ if (count > chip->ngpio)
+ count = chip->ngpio;
- handlereq.fd = fd;
- if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
+ for (i = 0; i < count; i++) {
/*
- * fput() will trigger the release() callback, so do not go onto
- * the regular error cleanup path here.
+ * Allow overriding "fixed" names provided by the GPIO
+ * provider. The "fixed" names are more often than not
+ * generic and less informative than the names given in
+ * device properties.
*/
- fput(file);
- put_unused_fd(fd);
- return -EFAULT;
+ if (names[chip->offset + i] && names[chip->offset + i][0])
+ gdev->descs[i].name = names[chip->offset + i];
}
- fd_install(fd, file);
-
- dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
- lh->numdescs);
+ kfree(names);
return 0;
-
-out_put_unused_fd:
- put_unused_fd(fd);
-out_free_descs:
- for (i = 0; i < count; i++)
- gpiod_free(lh->descs[i]);
- kfree(lh->label);
-out_free_lh:
- kfree(lh);
- put_device(&gdev->dev);
- return ret;
-}
-
-/*
- * GPIO line event management
- */
-
-/**
- * struct lineevent_state - contains the state of a userspace event
- * @gdev: the GPIO device the event pertains to
- * @label: consumer label used to tag descriptors
- * @desc: the GPIO descriptor held by this event
- * @eflags: the event flags this line was requested with
- * @irq: the interrupt that trigger in response to events on this GPIO
- * @wait: wait queue that handles blocking reads of events
- * @events: KFIFO for the GPIO events
- * @read_lock: mutex lock to protect reads from colliding with adding
- * new events to the FIFO
- * @timestamp: cache for the timestamp storing it between hardirq
- * and IRQ thread, used to bring the timestamp close to the actual
- * event
- */
-struct lineevent_state {
- struct gpio_device *gdev;
- const char *label;
- struct gpio_desc *desc;
- u32 eflags;
- int irq;
- wait_queue_head_t wait;
- DECLARE_KFIFO(events, struct gpioevent_data, 16);
- struct mutex read_lock;
- u64 timestamp;
-};
-
-#define GPIOEVENT_REQUEST_VALID_FLAGS \
- (GPIOEVENT_REQUEST_RISING_EDGE | \
- GPIOEVENT_REQUEST_FALLING_EDGE)
-
-static __poll_t lineevent_poll(struct file *filep,
- struct poll_table_struct *wait)
-{
- struct lineevent_state *le = filep->private_data;
- __poll_t events = 0;
-
- poll_wait(filep, &le->wait, wait);
-
- if (!kfifo_is_empty(&le->events))
- events = EPOLLIN | EPOLLRDNORM;
-
- return events;
}
-
-static ssize_t lineevent_read(struct file *filep,
- char __user *buf,
- size_t count,
- loff_t *f_ps)
+static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc)
{
- struct lineevent_state *le = filep->private_data;
- unsigned int copied;
- int ret;
-
- if (count < sizeof(struct gpioevent_data))
- return -EINVAL;
-
- do {
- if (kfifo_is_empty(&le->events)) {
- if (filep->f_flags & O_NONBLOCK)
- return -EAGAIN;
-
- ret = wait_event_interruptible(le->wait,
- !kfifo_is_empty(&le->events));
- if (ret)
- return ret;
- }
-
- if (mutex_lock_interruptible(&le->read_lock))
- return -ERESTARTSYS;
- ret = kfifo_to_user(&le->events, buf, count, &copied);
- mutex_unlock(&le->read_lock);
-
- if (ret)
- return ret;
-
- /*
- * If we couldn't read anything from the fifo (a different
- * thread might have been faster) we either return -EAGAIN if
- * the file descriptor is non-blocking, otherwise we go back to
- * sleep and wait for more data to arrive.
- */
- if (copied == 0 && (filep->f_flags & O_NONBLOCK))
- return -EAGAIN;
-
- } while (copied == 0);
+ unsigned long *p;
- return copied;
-}
+ p = bitmap_alloc(gc->ngpio, GFP_KERNEL);
+ if (!p)
+ return NULL;
-static int lineevent_release(struct inode *inode, struct file *filep)
-{
- struct lineevent_state *le = filep->private_data;
- struct gpio_device *gdev = le->gdev;
+ /* Assume by default all GPIOs are valid */
+ bitmap_fill(p, gc->ngpio);
- free_irq(le->irq, le);
- gpiod_free(le->desc);
- kfree(le->label);
- kfree(le);
- put_device(&gdev->dev);
- return 0;
+ return p;
}
-static long lineevent_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg)
+static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
{
- struct lineevent_state *le = filep->private_data;
- void __user *ip = (void __user *)arg;
- struct gpiohandle_data ghd;
-
- /*
- * We can get the value for an event line but not set it,
- * because it is input by definition.
- */
- if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
- int val;
-
- memset(&ghd, 0, sizeof(ghd));
-
- val = gpiod_get_value_cansleep(le->desc);
- if (val < 0)
- return val;
- ghd.values[0] = val;
-
- if (copy_to_user(ip, &ghd, sizeof(ghd)))
- return -EFAULT;
-
+ if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask))
return 0;
- }
- return -EINVAL;
-}
-
-#ifdef CONFIG_COMPAT
-static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd,
- unsigned long arg)
-{
- return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
-}
-#endif
-
-static const struct file_operations lineevent_fileops = {
- .release = lineevent_release,
- .read = lineevent_read,
- .poll = lineevent_poll,
- .owner = THIS_MODULE,
- .llseek = noop_llseek,
- .unlocked_ioctl = lineevent_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = lineevent_ioctl_compat,
-#endif
-};
-
-static irqreturn_t lineevent_irq_thread(int irq, void *p)
-{
- struct lineevent_state *le = p;
- struct gpioevent_data ge;
- int ret;
-
- /* Do not leak kernel stack to userspace */
- memset(&ge, 0, sizeof(ge));
-
- /*
- * We may be running from a nested threaded interrupt in which case
- * we didn't get the timestamp from lineevent_irq_handler().
- */
- if (!le->timestamp)
- ge.timestamp = ktime_get_real_ns();
- else
- ge.timestamp = le->timestamp;
-
- if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
- && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
- int level = gpiod_get_value_cansleep(le->desc);
- if (level)
- /* Emit low-to-high event */
- ge.id = GPIOEVENT_EVENT_RISING_EDGE;
- else
- /* Emit high-to-low event */
- ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
- } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) {
- /* Emit low-to-high event */
- ge.id = GPIOEVENT_EVENT_RISING_EDGE;
- } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
- /* Emit high-to-low event */
- ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
- } else {
- return IRQ_NONE;
- }
-
- ret = kfifo_put(&le->events, ge);
- if (ret != 0)
- wake_up_poll(&le->wait, EPOLLIN);
-
- return IRQ_HANDLED;
-}
-static irqreturn_t lineevent_irq_handler(int irq, void *p)
-{
- struct lineevent_state *le = p;
-
- /*
- * Just store the timestamp in hardirq context so we get it as
- * close in time as possible to the actual event.
- */
- le->timestamp = ktime_get_real_ns();
-
- return IRQ_WAKE_THREAD;
-}
-
-static int lineevent_create(struct gpio_device *gdev, void __user *ip)
-{
- struct gpioevent_request eventreq;
- struct lineevent_state *le;
- struct gpio_desc *desc;
- struct file *file;
- u32 offset;
- u32 lflags;
- u32 eflags;
- int fd;
- int ret;
- int irqflags = 0;
-
- if (copy_from_user(&eventreq, ip, sizeof(eventreq)))
- return -EFAULT;
-
- le = kzalloc(sizeof(*le), GFP_KERNEL);
- if (!le)
+ gc->valid_mask = gpiochip_allocate_mask(gc);
+ if (!gc->valid_mask)
return -ENOMEM;
- le->gdev = gdev;
- get_device(&gdev->dev);
-
- /* Make sure this is terminated */
- eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0';
- if (strlen(eventreq.consumer_label)) {
- le->label = kstrdup(eventreq.consumer_label,
- GFP_KERNEL);
- if (!le->label) {
- ret = -ENOMEM;
- goto out_free_le;
- }
- }
-
- offset = eventreq.lineoffset;
- lflags = eventreq.handleflags;
- eflags = eventreq.eventflags;
-
- if (offset >= gdev->ngpio) {
- ret = -EINVAL;
- goto out_free_label;
- }
-
- /* Return an error if a unknown flag is set */
- if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) ||
- (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) {
- ret = -EINVAL;
- goto out_free_label;
- }
-
- /* This is just wrong: we don't look for events on output lines */
- if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
- ret = -EINVAL;
- goto out_free_label;
- }
-
- desc = &gdev->descs[offset];
- ret = gpiod_request(desc, le->label);
- if (ret)
- goto out_free_label;
- le->desc = desc;
- le->eflags = eflags;
-
- if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
- set_bit(FLAG_ACTIVE_LOW, &desc->flags);
- if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN)
- set_bit(FLAG_OPEN_DRAIN, &desc->flags);
- if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
- set_bit(FLAG_OPEN_SOURCE, &desc->flags);
-
- ret = gpiod_direction_input(desc);
- if (ret)
- goto out_free_desc;
-
- le->irq = gpiod_to_irq(desc);
- if (le->irq <= 0) {
- ret = -ENODEV;
- goto out_free_desc;
- }
-
- if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
- irqflags |= IRQF_TRIGGER_RISING;
- if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE)
- irqflags |= IRQF_TRIGGER_FALLING;
- irqflags |= IRQF_ONESHOT;
-
- INIT_KFIFO(le->events);
- init_waitqueue_head(&le->wait);
- mutex_init(&le->read_lock);
-
- /* Request a thread to read the events */
- ret = request_threaded_irq(le->irq,
- lineevent_irq_handler,
- lineevent_irq_thread,
- irqflags,
- le->label,
- le);
- if (ret)
- goto out_free_desc;
-
- fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
- ret = fd;
- goto out_free_irq;
- }
-
- file = anon_inode_getfile("gpio-event",
- &lineevent_fileops,
- le,
- O_RDONLY | O_CLOEXEC);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto out_put_unused_fd;
- }
-
- eventreq.fd = fd;
- if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
- /*
- * fput() will trigger the release() callback, so do not go onto
- * the regular error cleanup path here.
- */
- fput(file);
- put_unused_fd(fd);
- return -EFAULT;
- }
-
- fd_install(fd, file);
return 0;
-
-out_put_unused_fd:
- put_unused_fd(fd);
-out_free_irq:
- free_irq(le->irq, le);
-out_free_desc:
- gpiod_free(le->desc);
-out_free_label:
- kfree(le->label);
-out_free_le:
- kfree(le);
- put_device(&gdev->dev);
- return ret;
}
-/*
- * gpio_ioctl() - ioctl handler for the GPIO chardev
- */
-static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+static int gpiochip_init_valid_mask(struct gpio_chip *gc)
{
- struct gpio_device *gdev = filp->private_data;
- struct gpio_chip *chip = gdev->chip;
- void __user *ip = (void __user *)arg;
-
- /* We fail any subsequent ioctl():s when the chip is gone */
- if (!chip)
- return -ENODEV;
-
- /* Fill in the struct and pass to userspace */
- if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
- struct gpiochip_info chipinfo;
-
- memset(&chipinfo, 0, sizeof(chipinfo));
-
- strncpy(chipinfo.name, dev_name(&gdev->dev),
- sizeof(chipinfo.name));
- chipinfo.name[sizeof(chipinfo.name)-1] = '\0';
- strncpy(chipinfo.label, gdev->label,
- sizeof(chipinfo.label));
- chipinfo.label[sizeof(chipinfo.label)-1] = '\0';
- chipinfo.lines = gdev->ngpio;
- if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
- return -EFAULT;
- return 0;
- } else if (cmd == GPIO_GET_LINEINFO_IOCTL) {
- struct gpioline_info lineinfo;
- struct gpio_desc *desc;
-
- if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
- return -EFAULT;
- if (lineinfo.line_offset >= gdev->ngpio)
- return -EINVAL;
-
- desc = &gdev->descs[lineinfo.line_offset];
- if (desc->name) {
- strncpy(lineinfo.name, desc->name,
- sizeof(lineinfo.name));
- lineinfo.name[sizeof(lineinfo.name)-1] = '\0';
- } else {
- lineinfo.name[0] = '\0';
- }
- if (desc->label) {
- strncpy(lineinfo.consumer, desc->label,
- sizeof(lineinfo.consumer));
- lineinfo.consumer[sizeof(lineinfo.consumer)-1] = '\0';
- } else {
- lineinfo.consumer[0] = '\0';
- }
+ if (gc->init_valid_mask)
+ return gc->init_valid_mask(gc,
+ gc->valid_mask,
+ gc->ngpio);
- /*
- * Userspace only need to know that the kernel is using
- * this GPIO so it can't use it.
- */
- lineinfo.flags = 0;
- if (test_bit(FLAG_REQUESTED, &desc->flags) ||
- test_bit(FLAG_IS_HOGGED, &desc->flags) ||
- test_bit(FLAG_USED_AS_IRQ, &desc->flags) ||
- test_bit(FLAG_EXPORT, &desc->flags) ||
- test_bit(FLAG_SYSFS, &desc->flags))
- lineinfo.flags |= GPIOLINE_FLAG_KERNEL;
- if (test_bit(FLAG_IS_OUT, &desc->flags))
- lineinfo.flags |= GPIOLINE_FLAG_IS_OUT;
- if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
- lineinfo.flags |= GPIOLINE_FLAG_ACTIVE_LOW;
- if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
- lineinfo.flags |= GPIOLINE_FLAG_OPEN_DRAIN;
- if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
- lineinfo.flags |= GPIOLINE_FLAG_OPEN_SOURCE;
-
- if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
- return -EFAULT;
- return 0;
- } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
- return linehandle_create(gdev, ip);
- } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
- return lineevent_create(gdev, ip);
- }
- return -EINVAL;
+ return 0;
}
-#ifdef CONFIG_COMPAT
-static long gpio_ioctl_compat(struct file *filp, unsigned int cmd,
- unsigned long arg)
+static void gpiochip_free_valid_mask(struct gpio_chip *gc)
{
- return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
+ bitmap_free(gc->valid_mask);
+ gc->valid_mask = NULL;
}
-#endif
-/**
- * gpio_chrdev_open() - open the chardev for ioctl operations
- * @inode: inode for this chardev
- * @filp: file struct for storing private data
- * Returns 0 on success
- */
-static int gpio_chrdev_open(struct inode *inode, struct file *filp)
+static int gpiochip_add_pin_ranges(struct gpio_chip *gc)
{
- struct gpio_device *gdev = container_of(inode->i_cdev,
- struct gpio_device, chrdev);
+ if (gc->add_pin_ranges)
+ return gc->add_pin_ranges(gc);
- /* Fail on open if the backing gpiochip is gone */
- if (!gdev->chip)
- return -ENODEV;
- get_device(&gdev->dev);
- filp->private_data = gdev;
-
- return nonseekable_open(inode, filp);
+ return 0;
}
-/**
- * gpio_chrdev_release() - close chardev after ioctl operations
- * @inode: inode for this chardev
- * @filp: file struct for storing private data
- * Returns 0 on success
- */
-static int gpio_chrdev_release(struct inode *inode, struct file *filp)
+bool gpiochip_line_is_valid(const struct gpio_chip *gc,
+ unsigned int offset)
{
- struct gpio_device *gdev = container_of(inode->i_cdev,
- struct gpio_device, chrdev);
-
- put_device(&gdev->dev);
- return 0;
+ /* No mask means all valid */
+ if (likely(!gc->valid_mask))
+ return true;
+ return test_bit(offset, gc->valid_mask);
}
-
-
-static const struct file_operations gpio_fileops = {
- .release = gpio_chrdev_release,
- .open = gpio_chrdev_open,
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = gpio_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = gpio_ioctl_compat,
-#endif
-};
+EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
static void gpiodevice_release(struct device *dev)
{
- struct gpio_device *gdev = dev_get_drvdata(dev);
+ struct gpio_device *gdev = container_of(dev, struct gpio_device, dev);
+ unsigned long flags;
+ spin_lock_irqsave(&gpio_lock, flags);
list_del(&gdev->list);
- ida_simple_remove(&gpio_ida, gdev->id);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ ida_free(&gpio_ida, gdev->id);
kfree_const(gdev->label);
kfree(gdev->descs);
kfree(gdev);
}
+#ifdef CONFIG_GPIO_CDEV
+#define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt))
+#define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev))
+#else
+/*
+ * gpiolib_cdev_register() indirectly calls device_add(), which is still
+ * required even when cdev is not selected.
+ */
+#define gcdev_register(gdev, devt) device_add(&(gdev)->dev)
+#define gcdev_unregister(gdev) device_del(&(gdev)->dev)
+#endif
+
static int gpiochip_setup_dev(struct gpio_device *gdev)
{
- int status;
-
- cdev_init(&gdev->chrdev, &gpio_fileops);
- gdev->chrdev.owner = THIS_MODULE;
- gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
-
- status = cdev_device_add(&gdev->chrdev, &gdev->dev);
- if (status)
- return status;
+ int ret;
- chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
- MAJOR(gpio_devt), gdev->id);
+ ret = gcdev_register(gdev, gpio_devt);
+ if (ret)
+ return ret;
- status = gpiochip_sysfs_register(gdev);
- if (status)
+ ret = gpiochip_sysfs_register(gdev);
+ if (ret)
goto err_remove_device;
/* From this point, the .release() function cleans up gpio_device */
gdev->dev.release = gpiodevice_release;
- pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n",
- __func__, gdev->base, gdev->base + gdev->ngpio - 1,
- dev_name(&gdev->dev), gdev->chip->label ? : "generic");
+ dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base,
+ gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic");
return 0;
err_remove_device:
- cdev_device_del(&gdev->chrdev, &gdev->dev);
- return status;
+ gcdev_unregister(gdev);
+ return ret;
}
-static void gpiochip_machine_hog(struct gpio_chip *chip, struct gpiod_hog *hog)
+static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
{
struct gpio_desc *desc;
int rv;
- desc = gpiochip_get_desc(chip, hog->chip_hwnum);
+ desc = gpiochip_get_desc(gc, hog->chip_hwnum);
if (IS_ERR(desc)) {
- pr_err("%s: unable to get GPIO desc: %ld\n",
- __func__, PTR_ERR(desc));
+ chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__,
+ PTR_ERR(desc));
return;
}
@@ -1219,19 +559,19 @@ static void gpiochip_machine_hog(struct gpio_chip *chip, struct gpiod_hog *hog)
rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags);
if (rv)
- pr_err("%s: unable to hog GPIO line (%s:%u): %d\n",
- __func__, chip->label, hog->chip_hwnum, rv);
+ gpiod_err(desc, "%s: unable to hog GPIO line (%s:%u): %d\n",
+ __func__, gc->label, hog->chip_hwnum, rv);
}
-static void machine_gpiochip_add(struct gpio_chip *chip)
+static void machine_gpiochip_add(struct gpio_chip *gc)
{
struct gpiod_hog *hog;
mutex_lock(&gpio_machine_hogs_mutex);
list_for_each_entry(hog, &gpio_machine_hogs, list) {
- if (!strcmp(chip->label, hog->chip_label))
- gpiochip_machine_hog(chip, hog);
+ if (!strcmp(gc->label, hog->chip_label))
+ gpiochip_machine_hog(gc, hog);
}
mutex_unlock(&gpio_machine_hogs_mutex);
@@ -1240,25 +580,32 @@ static void machine_gpiochip_add(struct gpio_chip *chip)
static void gpiochip_setup_devs(void)
{
struct gpio_device *gdev;
- int err;
+ int ret;
list_for_each_entry(gdev, &gpio_devices, list) {
- err = gpiochip_setup_dev(gdev);
- if (err)
- pr_err("%s: Failed to initialize gpio device (%d)\n",
- dev_name(&gdev->dev), err);
+ ret = gpiochip_setup_dev(gdev);
+ if (ret)
+ dev_err(&gdev->dev,
+ "Failed to initialize gpio device (%d)\n", ret);
}
}
-int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
+int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct lock_class_key *lock_key,
struct lock_class_key *request_key)
{
- unsigned long flags;
- int status = 0;
- unsigned i;
- int base = chip->base;
+ struct fwnode_handle *fwnode = NULL;
struct gpio_device *gdev;
+ unsigned long flags;
+ int base = gc->base;
+ unsigned int i;
+ int ret = 0;
+ u32 ngpios;
+
+ if (gc->fwnode)
+ fwnode = gc->fwnode;
+ else if (gc->parent)
+ fwnode = dev_fwnode(gc->parent);
/*
* First: allocate and populate the internal stat container, and
@@ -1268,60 +615,81 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
if (!gdev)
return -ENOMEM;
gdev->dev.bus = &gpio_bus_type;
- gdev->chip = chip;
- chip->gpiodev = gdev;
- if (chip->parent) {
- gdev->dev.parent = chip->parent;
- gdev->dev.of_node = chip->parent->of_node;
- }
+ gdev->dev.parent = gc->parent;
+ gdev->chip = gc;
+ gc->gpiodev = gdev;
-#ifdef CONFIG_OF_GPIO
- /* If the gpiochip has an assigned OF node this takes precedence */
- if (chip->of_node)
- gdev->dev.of_node = chip->of_node;
- else
- chip->of_node = gdev->dev.of_node;
-#endif
+ of_gpio_dev_init(gc, gdev);
+ acpi_gpio_dev_init(gc, gdev);
+
+ /*
+ * Assign fwnode depending on the result of the previous calls,
+ * if none of them succeed, assign it to the parent's one.
+ */
+ gdev->dev.fwnode = dev_fwnode(&gdev->dev) ?: fwnode;
- gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
+ gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
if (gdev->id < 0) {
- status = gdev->id;
+ ret = gdev->id;
goto err_free_gdev;
}
- dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
+
+ ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
+ if (ret)
+ goto err_free_ida;
+
device_initialize(&gdev->dev);
- dev_set_drvdata(&gdev->dev, gdev);
- if (chip->parent && chip->parent->driver)
- gdev->owner = chip->parent->driver->owner;
- else if (chip->owner)
+ if (gc->parent && gc->parent->driver)
+ gdev->owner = gc->parent->driver->owner;
+ else if (gc->owner)
/* TODO: remove chip->owner */
- gdev->owner = chip->owner;
+ gdev->owner = gc->owner;
else
gdev->owner = THIS_MODULE;
- gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
+ gdev->descs = kcalloc(gc->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
if (!gdev->descs) {
- status = -ENOMEM;
- goto err_free_ida;
+ ret = -ENOMEM;
+ goto err_free_dev_name;
}
- if (chip->ngpio == 0) {
- chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
- status = -EINVAL;
+ /*
+ * Try the device properties if the driver didn't supply the number
+ * of GPIO lines.
+ */
+ if (gc->ngpio == 0) {
+ ret = device_property_read_u32(&gdev->dev, "ngpios", &ngpios);
+ if (ret == -ENODATA)
+ /*
+ * -ENODATA means that there is no property found and
+ * we want to issue the error message to the user.
+ * Besides that, we want to return different error code
+ * to state that supplied value is not valid.
+ */
+ ngpios = 0;
+ else if (ret)
+ goto err_free_descs;
+
+ gc->ngpio = ngpios;
+ }
+
+ if (gc->ngpio == 0) {
+ chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
+ ret = -EINVAL;
goto err_free_descs;
}
- if (chip->ngpio > FASTPATH_NGPIO)
- chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n",
- chip->ngpio, FASTPATH_NGPIO);
+ if (gc->ngpio > FASTPATH_NGPIO)
+ chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
+ gc->ngpio, FASTPATH_NGPIO);
- gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL);
+ gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) {
- status = -ENOMEM;
+ ret = -ENOMEM;
goto err_free_descs;
}
- gdev->ngpio = chip->ngpio;
+ gdev->ngpio = gc->ngpio;
gdev->data = data;
spin_lock_irqsave(&gpio_lock, flags);
@@ -1334,9 +702,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
* of the sysfs interface anyways.
*/
if (base < 0) {
- base = gpiochip_find_base(chip->ngpio);
+ base = gpiochip_find_base(gc->ngpio);
if (base < 0) {
- status = base;
+ ret = base;
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
@@ -1346,63 +714,80 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
* see if anyone makes use of this, else drop this and assign
* a poison instead.
*/
- chip->base = base;
+ gc->base = base;
}
gdev->base = base;
- status = gpiodev_add_to_list(gdev);
- if (status) {
+ ret = gpiodev_add_to_list(gdev);
+ if (ret) {
spin_unlock_irqrestore(&gpio_lock, flags);
+ chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
goto err_free_label;
}
+ for (i = 0; i < gc->ngpio; i++)
+ gdev->descs[i].gdev = gdev;
+
spin_unlock_irqrestore(&gpio_lock, flags);
- for (i = 0; i < chip->ngpio; i++)
- gdev->descs[i].gdev = gdev;
+ BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier);
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
- status = gpiochip_set_desc_names(chip);
- if (status)
+ if (gc->names) {
+ ret = gpiochip_set_desc_names(gc);
+ if (ret)
+ goto err_remove_from_list;
+ }
+ ret = devprop_gpiochip_set_names(gc);
+ if (ret)
goto err_remove_from_list;
- status = gpiochip_irqchip_init_valid_mask(chip);
- if (status)
+ ret = gpiochip_alloc_valid_mask(gc);
+ if (ret)
goto err_remove_from_list;
- status = gpiochip_alloc_valid_mask(chip);
- if (status)
- goto err_remove_irqchip_mask;
-
- status = gpiochip_add_irqchip(chip, lock_key, request_key);
- if (status)
+ ret = of_gpiochip_add(gc);
+ if (ret)
goto err_free_gpiochip_mask;
- status = of_gpiochip_add(chip);
- if (status)
- goto err_remove_chip;
-
- status = gpiochip_init_valid_mask(chip);
- if (status)
+ ret = gpiochip_init_valid_mask(gc);
+ if (ret)
goto err_remove_of_chip;
- for (i = 0; i < chip->ngpio; i++) {
+ for (i = 0; i < gc->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
- if (chip->get_direction && gpiochip_line_is_valid(chip, i))
- desc->flags = !chip->get_direction(chip, i) ?
- (1 << FLAG_IS_OUT) : 0;
- else
- desc->flags = !chip->direction_input ?
- (1 << FLAG_IS_OUT) : 0;
+ if (gc->get_direction && gpiochip_line_is_valid(gc, i)) {
+ assign_bit(FLAG_IS_OUT,
+ &desc->flags, !gc->get_direction(gc, i));
+ } else {
+ assign_bit(FLAG_IS_OUT,
+ &desc->flags, !gc->direction_input);
+ }
}
- acpi_gpiochip_add(chip);
+ ret = gpiochip_add_pin_ranges(gc);
+ if (ret)
+ goto err_remove_of_chip;
+
+ acpi_gpiochip_add(gc);
- machine_gpiochip_add(chip);
+ machine_gpiochip_add(gc);
+
+ ret = gpiochip_irqchip_init_valid_mask(gc);
+ if (ret)
+ goto err_remove_acpi_chip;
+
+ ret = gpiochip_irqchip_init_hw(gc);
+ if (ret)
+ goto err_remove_acpi_chip;
+
+ ret = gpiochip_add_irqchip(gc, lock_key, request_key);
+ if (ret)
+ goto err_remove_irqchip_mask;
/*
* By first adding the chardev, and then adding the device,
@@ -1413,23 +798,24 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
* Otherwise, defer until later.
*/
if (gpiolib_initialized) {
- status = gpiochip_setup_dev(gdev);
- if (status)
- goto err_remove_acpi_chip;
+ ret = gpiochip_setup_dev(gdev);
+ if (ret)
+ goto err_remove_irqchip;
}
return 0;
+err_remove_irqchip:
+ gpiochip_irqchip_remove(gc);
+err_remove_irqchip_mask:
+ gpiochip_irqchip_free_valid_mask(gc);
err_remove_acpi_chip:
- acpi_gpiochip_remove(chip);
+ acpi_gpiochip_remove(gc);
err_remove_of_chip:
- gpiochip_free_hogs(chip);
- of_gpiochip_remove(chip);
-err_remove_chip:
- gpiochip_irqchip_remove(chip);
+ gpiochip_free_hogs(gc);
+ of_gpiochip_remove(gc);
err_free_gpiochip_mask:
- gpiochip_free_valid_mask(chip);
-err_remove_irqchip_mask:
- gpiochip_irqchip_free_valid_mask(chip);
+ gpiochip_remove_pin_ranges(gc);
+ gpiochip_free_valid_mask(gc);
err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags);
list_del(&gdev->list);
@@ -1438,55 +824,57 @@ err_free_label:
kfree_const(gdev->label);
err_free_descs:
kfree(gdev->descs);
+err_free_dev_name:
+ kfree(dev_name(&gdev->dev));
err_free_ida:
- ida_simple_remove(&gpio_ida, gdev->id);
+ ida_free(&gpio_ida, gdev->id);
err_free_gdev:
/* failures here can mean systems won't boot... */
- pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
- gdev->base, gdev->base + gdev->ngpio - 1,
- chip->label ? : "generic", status);
+ if (ret != -EPROBE_DEFER) {
+ pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
+ gdev->base, gdev->base + gdev->ngpio - 1,
+ gc->label ? : "generic", ret);
+ }
kfree(gdev);
- return status;
+ return ret;
}
EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key);
/**
* gpiochip_get_data() - get per-subdriver data for the chip
- * @chip: GPIO chip
+ * @gc: GPIO chip
*
* Returns:
* The per-subdriver data for the chip.
*/
-void *gpiochip_get_data(struct gpio_chip *chip)
+void *gpiochip_get_data(struct gpio_chip *gc)
{
- return chip->gpiodev->data;
+ return gc->gpiodev->data;
}
EXPORT_SYMBOL_GPL(gpiochip_get_data);
/**
* gpiochip_remove() - unregister a gpio_chip
- * @chip: the chip to unregister
+ * @gc: the chip to unregister
*
* A gpio_chip with any GPIOs still requested may not be removed.
*/
-void gpiochip_remove(struct gpio_chip *chip)
+void gpiochip_remove(struct gpio_chip *gc)
{
- struct gpio_device *gdev = chip->gpiodev;
- struct gpio_desc *desc;
+ struct gpio_device *gdev = gc->gpiodev;
unsigned long flags;
- unsigned i;
- bool requested = false;
+ unsigned int i;
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
gpiochip_sysfs_unregister(gdev);
- gpiochip_free_hogs(chip);
+ gpiochip_free_hogs(gc);
/* Numb the device, cancelling all outstanding operations */
gdev->chip = NULL;
- gpiochip_irqchip_remove(chip);
- acpi_gpiochip_remove(chip);
- gpiochip_remove_pin_ranges(chip);
- of_gpiochip_remove(chip);
- gpiochip_free_valid_mask(chip);
+ gpiochip_irqchip_remove(gc);
+ acpi_gpiochip_remove(gc);
+ of_gpiochip_remove(gc);
+ gpiochip_remove_pin_ranges(gc);
+ gpiochip_free_valid_mask(gc);
/*
* We accept no more calls into the driver from this point, so
* NULL the driver data pointer
@@ -1495,13 +883,12 @@ void gpiochip_remove(struct gpio_chip *chip)
spin_lock_irqsave(&gpio_lock, flags);
for (i = 0; i < gdev->ngpio; i++) {
- desc = &gdev->descs[i];
- if (test_bit(FLAG_REQUESTED, &desc->flags))
- requested = true;
+ if (gpiochip_is_requested(gc, i))
+ break;
}
spin_unlock_irqrestore(&gpio_lock, flags);
- if (requested)
+ if (i != gdev->ngpio)
dev_crit(&gdev->dev,
"REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
@@ -1511,57 +898,11 @@ void gpiochip_remove(struct gpio_chip *chip)
* be removed, else it will be dangling until the last user is
* gone.
*/
- cdev_device_del(&gdev->chrdev, &gdev->dev);
+ gcdev_unregister(gdev);
put_device(&gdev->dev);
}
EXPORT_SYMBOL_GPL(gpiochip_remove);
-static void devm_gpio_chip_release(struct device *dev, void *res)
-{
- struct gpio_chip *chip = *(struct gpio_chip **)res;
-
- gpiochip_remove(chip);
-}
-
-/**
- * devm_gpiochip_add_data() - Resource manager gpiochip_add_data()
- * @dev: pointer to the device that gpio_chip belongs to.
- * @chip: the chip to register, with chip->base initialized
- * @data: driver-private data associated with this chip
- *
- * Context: potentially before irqs will work
- *
- * The gpio chip automatically be released when the device is unbound.
- *
- * Returns:
- * A negative errno if the chip can't be registered, such as because the
- * chip->base is invalid or already associated with a different chip.
- * Otherwise it returns zero as a success code.
- */
-int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
- void *data)
-{
- struct gpio_chip **ptr;
- int ret;
-
- ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr),
- GFP_KERNEL);
- if (!ptr)
- return -ENOMEM;
-
- ret = gpiochip_add_data(chip, data);
- if (ret < 0) {
- devres_free(ptr);
- return ret;
- }
-
- *ptr = chip;
- devres_add(dev, ptr);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
-
/**
* gpiochip_find() - iterator for locating a specific gpio_chip
* @data: data to pass to match function
@@ -1574,31 +915,31 @@ EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
* more gpio_chips.
*/
struct gpio_chip *gpiochip_find(void *data,
- int (*match)(struct gpio_chip *chip,
+ int (*match)(struct gpio_chip *gc,
void *data))
{
struct gpio_device *gdev;
- struct gpio_chip *chip = NULL;
+ struct gpio_chip *gc = NULL;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(gdev, &gpio_devices, list)
if (gdev->chip && match(gdev->chip, data)) {
- chip = gdev->chip;
+ gc = gdev->chip;
break;
}
spin_unlock_irqrestore(&gpio_lock, flags);
- return chip;
+ return gc;
}
EXPORT_SYMBOL_GPL(gpiochip_find);
-static int gpiochip_match_name(struct gpio_chip *chip, void *data)
+static int gpiochip_match_name(struct gpio_chip *gc, void *data)
{
const char *name = data;
- return !strcmp(chip->label, name);
+ return !strcmp(gc->label, name);
}
static struct gpio_chip *find_chip_by_name(const char *name)
@@ -1612,119 +953,336 @@ static struct gpio_chip *find_chip_by_name(const char *name)
* The following is irqchip helper code for gpiochips.
*/
-static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
+static int gpiochip_irqchip_init_hw(struct gpio_chip *gc)
+{
+ struct gpio_irq_chip *girq = &gc->irq;
+
+ if (!girq->init_hw)
+ return 0;
+
+ return girq->init_hw(gc);
+}
+
+static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc)
{
- if (!gpiochip->irq.need_valid_mask)
+ struct gpio_irq_chip *girq = &gc->irq;
+
+ if (!girq->init_valid_mask)
return 0;
- gpiochip->irq.valid_mask = gpiochip_allocate_mask(gpiochip);
- if (!gpiochip->irq.valid_mask)
+ girq->valid_mask = gpiochip_allocate_mask(gc);
+ if (!girq->valid_mask)
return -ENOMEM;
+ girq->init_valid_mask(gc, girq->valid_mask, gc->ngpio);
+
return 0;
}
-static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
+static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc)
{
- kfree(gpiochip->irq.valid_mask);
- gpiochip->irq.valid_mask = NULL;
+ bitmap_free(gc->irq.valid_mask);
+ gc->irq.valid_mask = NULL;
}
-bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
+bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
unsigned int offset)
{
- if (!gpiochip_line_is_valid(gpiochip, offset))
+ if (!gpiochip_line_is_valid(gc, offset))
return false;
/* No mask means all valid */
- if (likely(!gpiochip->irq.valid_mask))
+ if (likely(!gc->irq.valid_mask))
return true;
- return test_bit(offset, gpiochip->irq.valid_mask);
+ return test_bit(offset, gc->irq.valid_mask);
}
EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid);
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+
/**
- * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip
- * @gc: the gpiochip to set the irqchip chain to
- * @parent_irq: the irq number corresponding to the parent IRQ for this
- * chained irqchip
- * @parent_handler: the parent interrupt handler for the accumulated IRQ
- * coming out of the gpiochip. If the interrupt is nested rather than
- * cascaded, pass NULL in this handler argument
+ * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip
+ * to a gpiochip
+ * @gc: the gpiochip to set the irqchip hierarchical handler to
+ * @irqchip: the irqchip to handle this level of the hierarchy, the interrupt
+ * will then percolate up to the parent
*/
-static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc,
- unsigned int parent_irq,
- irq_flow_handler_t parent_handler)
+static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc,
+ struct irq_chip *irqchip)
{
- struct gpio_irq_chip *girq = &gc->irq;
- struct device *dev = &gc->gpiodev->dev;
-
- if (!girq->domain) {
- chip_err(gc, "called %s before setting up irqchip\n",
- __func__);
+ /* DT will deal with mapping each IRQ as we go along */
+ if (is_of_node(gc->irq.fwnode))
return;
- }
- if (parent_handler) {
- if (gc->can_sleep) {
- chip_err(gc,
- "you cannot have chained interrupts on a chip that may sleep\n");
- return;
- }
- girq->parents = devm_kcalloc(dev, 1,
- sizeof(*girq->parents),
- GFP_KERNEL);
- if (!girq->parents) {
- chip_err(gc, "out of memory allocating parent IRQ\n");
- return;
+ /*
+ * This is for legacy and boardfile "irqchip" fwnodes: allocate
+ * irqs upfront instead of dynamically since we don't have the
+ * dynamic type of allocation that hardware description languages
+ * provide. Once all GPIO drivers using board files are gone from
+ * the kernel we can delete this code, but for a transitional period
+ * it is necessary to keep this around.
+ */
+ if (is_fwnode_irqchip(gc->irq.fwnode)) {
+ int i;
+ int ret;
+
+ for (i = 0; i < gc->ngpio; i++) {
+ struct irq_fwspec fwspec;
+ unsigned int parent_hwirq;
+ unsigned int parent_type;
+ struct gpio_irq_chip *girq = &gc->irq;
+
+ /*
+ * We call the child to parent translation function
+ * only to check if the child IRQ is valid or not.
+ * Just pick the rising edge type here as that is what
+ * we likely need to support.
+ */
+ ret = girq->child_to_parent_hwirq(gc, i,
+ IRQ_TYPE_EDGE_RISING,
+ &parent_hwirq,
+ &parent_type);
+ if (ret) {
+ chip_err(gc, "skip set-up on hwirq %d\n",
+ i);
+ continue;
+ }
+
+ fwspec.fwnode = gc->irq.fwnode;
+ /* This is the hwirq for the GPIO line side of things */
+ fwspec.param[0] = girq->child_offset_to_irq(gc, i);
+ /* Just pick something */
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
+ fwspec.param_count = 2;
+ ret = __irq_domain_alloc_irqs(gc->irq.domain,
+ /* just pick something */
+ -1,
+ 1,
+ NUMA_NO_NODE,
+ &fwspec,
+ false,
+ NULL);
+ if (ret < 0) {
+ chip_err(gc,
+ "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n",
+ i, parent_hwirq,
+ ret);
+ }
}
- girq->parents[0] = parent_irq;
- girq->num_parents = 1;
- /*
- * The parent irqchip is already using the chip_data for this
- * irqchip, so our callbacks simply use the handler_data.
- */
- irq_set_chained_handler_and_data(parent_irq, parent_handler,
- gc);
}
+
+ chip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__);
+
+ return;
}
-/**
- * gpiochip_set_chained_irqchip() - connects a chained irqchip to a gpiochip
- * @gpiochip: the gpiochip to set the irqchip chain to
- * @irqchip: the irqchip to chain to the gpiochip
- * @parent_irq: the irq number corresponding to the parent IRQ for this
- * chained irqchip
- * @parent_handler: the parent interrupt handler for the accumulated IRQ
- * coming out of the gpiochip.
- */
-void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
- struct irq_chip *irqchip,
- unsigned int parent_irq,
- irq_flow_handler_t parent_handler)
+static int gpiochip_hierarchy_irq_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
{
- if (gpiochip->irq.threaded) {
- chip_err(gpiochip, "tried to chain a threaded gpiochip\n");
- return;
+ /* We support standard DT translation */
+ if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) {
+ return irq_domain_translate_twocell(d, fwspec, hwirq, type);
}
- gpiochip_set_cascaded_irqchip(gpiochip, parent_irq, parent_handler);
+ /* This is for board files and others not using DT */
+ if (is_fwnode_irqchip(fwspec->fwnode)) {
+ int ret;
+
+ ret = irq_domain_translate_twocell(d, fwspec, hwirq, type);
+ if (ret)
+ return ret;
+ WARN_ON(*type == IRQ_TYPE_NONE);
+ return 0;
+ }
+ return -EINVAL;
}
-EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
-/**
- * gpiochip_set_nested_irqchip() - connects a nested irqchip to a gpiochip
- * @gpiochip: the gpiochip to set the irqchip nested handler to
- * @irqchip: the irqchip to nest to the gpiochip
- * @parent_irq: the irq number corresponding to the parent IRQ for this
- * nested irqchip
- */
-void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip,
- struct irq_chip *irqchip,
- unsigned int parent_irq)
+static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
+ unsigned int irq,
+ unsigned int nr_irqs,
+ void *data)
+{
+ struct gpio_chip *gc = d->host_data;
+ irq_hw_number_t hwirq;
+ unsigned int type = IRQ_TYPE_NONE;
+ struct irq_fwspec *fwspec = data;
+ void *parent_arg;
+ unsigned int parent_hwirq;
+ unsigned int parent_type;
+ struct gpio_irq_chip *girq = &gc->irq;
+ int ret;
+
+ /*
+ * The nr_irqs parameter is always one except for PCI multi-MSI
+ * so this should not happen.
+ */
+ WARN_ON(nr_irqs != 1);
+
+ ret = gc->irq.child_irq_domain_ops.translate(d, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq);
+
+ ret = girq->child_to_parent_hwirq(gc, hwirq, type,
+ &parent_hwirq, &parent_type);
+ if (ret) {
+ chip_err(gc, "can't look up hwirq %lu\n", hwirq);
+ return ret;
+ }
+ chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq);
+
+ /*
+ * We set handle_bad_irq because the .set_type() should
+ * always be invoked and set the right type of handler.
+ */
+ irq_domain_set_info(d,
+ irq,
+ hwirq,
+ gc->irq.chip,
+ gc,
+ girq->handler,
+ NULL, NULL);
+ irq_set_probe(irq);
+
+ /* This parent only handles asserted level IRQs */
+ parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq, parent_type);
+ if (!parent_arg)
+ return -ENOMEM;
+
+ chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
+ irq, parent_hwirq);
+ irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key);
+ ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg);
+ /*
+ * If the parent irqdomain is msi, the interrupts have already
+ * been allocated, so the EEXIST is good.
+ */
+ if (irq_domain_is_msi(d->parent) && (ret == -EEXIST))
+ ret = 0;
+ if (ret)
+ chip_err(gc,
+ "failed to allocate parent hwirq %d for hwirq %lu\n",
+ parent_hwirq, hwirq);
+
+ kfree(parent_arg);
+ return ret;
+}
+
+static unsigned int gpiochip_child_offset_to_irq_noop(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return offset;
+}
+
+static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops)
+{
+ ops->activate = gpiochip_irq_domain_activate;
+ ops->deactivate = gpiochip_irq_domain_deactivate;
+ ops->alloc = gpiochip_hierarchy_irq_domain_alloc;
+ ops->free = irq_domain_free_irqs_common;
+
+ /*
+ * We only allow overriding the translate() function for
+ * hierarchical chips, and this should only be done if the user
+ * really need something other than 1:1 translation.
+ */
+ if (!ops->translate)
+ ops->translate = gpiochip_hierarchy_irq_domain_translate;
+}
+
+static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
+{
+ if (!gc->irq.child_to_parent_hwirq ||
+ !gc->irq.fwnode) {
+ chip_err(gc, "missing irqdomain vital data\n");
+ return -EINVAL;
+ }
+
+ if (!gc->irq.child_offset_to_irq)
+ gc->irq.child_offset_to_irq = gpiochip_child_offset_to_irq_noop;
+
+ if (!gc->irq.populate_parent_alloc_arg)
+ gc->irq.populate_parent_alloc_arg =
+ gpiochip_populate_parent_fwspec_twocell;
+
+ gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops);
+
+ gc->irq.domain = irq_domain_create_hierarchy(
+ gc->irq.parent_domain,
+ 0,
+ gc->ngpio,
+ gc->irq.fwnode,
+ &gc->irq.child_irq_domain_ops,
+ gc);
+
+ if (!gc->irq.domain)
+ return -ENOMEM;
+
+ gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip);
+
+ return 0;
+}
+
+static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
+{
+ return !!gc->irq.parent_domain;
+}
+
+void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
+ unsigned int parent_hwirq,
+ unsigned int parent_type)
+{
+ struct irq_fwspec *fwspec;
+
+ fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
+ if (!fwspec)
+ return NULL;
+
+ fwspec->fwnode = gc->irq.parent_domain->fwnode;
+ fwspec->param_count = 2;
+ fwspec->param[0] = parent_hwirq;
+ fwspec->param[1] = parent_type;
+
+ return fwspec;
+}
+EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell);
+
+void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
+ unsigned int parent_hwirq,
+ unsigned int parent_type)
+{
+ struct irq_fwspec *fwspec;
+
+ fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
+ if (!fwspec)
+ return NULL;
+
+ fwspec->fwnode = gc->irq.parent_domain->fwnode;
+ fwspec->param_count = 4;
+ fwspec->param[0] = 0;
+ fwspec->param[1] = parent_hwirq;
+ fwspec->param[2] = 0;
+ fwspec->param[3] = parent_type;
+
+ return fwspec;
+}
+EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell);
+
+#else
+
+static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
+{
+ return -EINVAL;
+}
+
+static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
{
- gpiochip_set_cascaded_irqchip(gpiochip, parent_irq, NULL);
+ return false;
}
-EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip);
+
+#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
/**
* gpiochip_irq_map() - maps an IRQ into a GPIO irqchip
@@ -1739,38 +1297,38 @@ EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip);
int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
- struct gpio_chip *chip = d->host_data;
- int err = 0;
+ struct gpio_chip *gc = d->host_data;
+ int ret = 0;
- if (!gpiochip_irqchip_irq_valid(chip, hwirq))
+ if (!gpiochip_irqchip_irq_valid(gc, hwirq))
return -ENXIO;
- irq_set_chip_data(irq, chip);
+ irq_set_chip_data(irq, gc);
/*
* This lock class tells lockdep that GPIO irqs are in a different
* category than their parents, so it won't report false recursion.
*/
- irq_set_lockdep_class(irq, chip->irq.lock_key, chip->irq.request_key);
- irq_set_chip_and_handler(irq, chip->irq.chip, chip->irq.handler);
+ irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key);
+ irq_set_chip_and_handler(irq, gc->irq.chip, gc->irq.handler);
/* Chips that use nested thread handlers have them marked */
- if (chip->irq.threaded)
+ if (gc->irq.threaded)
irq_set_nested_thread(irq, 1);
irq_set_noprobe(irq);
- if (chip->irq.num_parents == 1)
- err = irq_set_parent(irq, chip->irq.parents[0]);
- else if (chip->irq.map)
- err = irq_set_parent(irq, chip->irq.map[hwirq]);
+ if (gc->irq.num_parents == 1)
+ ret = irq_set_parent(irq, gc->irq.parents[0]);
+ else if (gc->irq.map)
+ ret = irq_set_parent(irq, gc->irq.map[hwirq]);
- if (err < 0)
- return err;
+ if (ret < 0)
+ return ret;
/*
* No set-up of the hardware will happen if IRQ_TYPE_NONE
* is passed as default type.
*/
- if (chip->irq.default_type != IRQ_TYPE_NONE)
- irq_set_irq_type(irq, chip->irq.default_type);
+ if (gc->irq.default_type != IRQ_TYPE_NONE)
+ irq_set_irq_type(irq, gc->irq.default_type);
return 0;
}
@@ -1778,9 +1336,9 @@ EXPORT_SYMBOL_GPL(gpiochip_irq_map);
void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)
{
- struct gpio_chip *chip = d->host_data;
+ struct gpio_chip *gc = d->host_data;
- if (chip->irq.threaded)
+ if (gc->irq.threaded)
irq_set_nested_thread(irq, 0);
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
@@ -1794,6 +1352,11 @@ static const struct irq_domain_ops gpiochip_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
+/*
+ * TODO: move these activate/deactivate in under the hierarchicial
+ * irqchip implementation as static once SPMI and SSBI (all external
+ * users) are phased over.
+ */
/**
* gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ
* @domain: The IRQ domain used by this IRQ chip
@@ -1807,9 +1370,9 @@ static const struct irq_domain_ops gpiochip_domain_ops = {
int gpiochip_irq_domain_activate(struct irq_domain *domain,
struct irq_data *data, bool reserve)
{
- struct gpio_chip *chip = domain->host_data;
+ struct gpio_chip *gc = domain->host_data;
- return gpiochip_lock_as_irq(chip, data->hwirq);
+ return gpiochip_lock_as_irq(gc, data->hwirq);
}
EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate);
@@ -1825,178 +1388,249 @@ EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate);
void gpiochip_irq_domain_deactivate(struct irq_domain *domain,
struct irq_data *data)
{
- struct gpio_chip *chip = domain->host_data;
+ struct gpio_chip *gc = domain->host_data;
- return gpiochip_unlock_as_irq(chip, data->hwirq);
+ return gpiochip_unlock_as_irq(gc, data->hwirq);
}
EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate);
-static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
+static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset)
{
- if (!gpiochip_irqchip_irq_valid(chip, offset))
+ struct irq_domain *domain = gc->irq.domain;
+
+#ifdef CONFIG_GPIOLIB_IRQCHIP
+ /*
+ * Avoid race condition with other code, which tries to lookup
+ * an IRQ before the irqchip has been properly registered,
+ * i.e. while gpiochip is still being brought up.
+ */
+ if (!gc->irq.initialized)
+ return -EPROBE_DEFER;
+#endif
+
+ if (!gpiochip_irqchip_irq_valid(gc, offset))
return -ENXIO;
- return irq_create_mapping(chip->irq.domain, offset);
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ if (irq_domain_is_hierarchy(domain)) {
+ struct irq_fwspec spec;
+
+ spec.fwnode = domain->fwnode;
+ spec.param_count = 2;
+ spec.param[0] = gc->irq.child_offset_to_irq(gc, offset);
+ spec.param[1] = IRQ_TYPE_NONE;
+
+ return irq_create_fwspec_mapping(&spec);
+ }
+#endif
+
+ return irq_create_mapping(domain, offset);
+}
+
+int gpiochip_irq_reqres(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+ return gpiochip_reqres_irq(gc, d->hwirq);
}
+EXPORT_SYMBOL(gpiochip_irq_reqres);
-static int gpiochip_irq_reqres(struct irq_data *d)
+void gpiochip_irq_relres(struct irq_data *d)
{
- struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- return gpiochip_reqres_irq(chip, d->hwirq);
+ gpiochip_relres_irq(gc, d->hwirq);
}
+EXPORT_SYMBOL(gpiochip_irq_relres);
-static void gpiochip_irq_relres(struct irq_data *d)
+static void gpiochip_irq_mask(struct irq_data *d)
{
- struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- gpiochip_relres_irq(chip, d->hwirq);
+ if (gc->irq.irq_mask)
+ gc->irq.irq_mask(d);
+ gpiochip_disable_irq(gc, d->hwirq);
+}
+
+static void gpiochip_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+ gpiochip_enable_irq(gc, d->hwirq);
+ if (gc->irq.irq_unmask)
+ gc->irq.irq_unmask(d);
}
static void gpiochip_irq_enable(struct irq_data *d)
{
- struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- gpiochip_enable_irq(chip, d->hwirq);
- if (chip->irq.irq_enable)
- chip->irq.irq_enable(d);
- else
- chip->irq.chip->irq_unmask(d);
+ gpiochip_enable_irq(gc, d->hwirq);
+ gc->irq.irq_enable(d);
}
static void gpiochip_irq_disable(struct irq_data *d)
{
- struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- if (chip->irq.irq_disable)
- chip->irq.irq_disable(d);
- else
- chip->irq.chip->irq_mask(d);
- gpiochip_disable_irq(chip, d->hwirq);
+ gc->irq.irq_disable(d);
+ gpiochip_disable_irq(gc, d->hwirq);
}
-static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip)
+static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
{
- struct irq_chip *irqchip = gpiochip->irq.chip;
+ struct irq_chip *irqchip = gc->irq.chip;
+
+ if (irqchip->flags & IRQCHIP_IMMUTABLE)
+ return;
+
+ chip_warn(gc, "not an immutable chip, please consider fixing it!\n");
if (!irqchip->irq_request_resources &&
!irqchip->irq_release_resources) {
irqchip->irq_request_resources = gpiochip_irq_reqres;
irqchip->irq_release_resources = gpiochip_irq_relres;
}
- if (WARN_ON(gpiochip->irq.irq_enable))
+ if (WARN_ON(gc->irq.irq_enable))
return;
/* Check if the irqchip already has this hook... */
- if (irqchip->irq_enable == gpiochip_irq_enable) {
+ if (irqchip->irq_enable == gpiochip_irq_enable ||
+ irqchip->irq_mask == gpiochip_irq_mask) {
/*
* ...and if so, give a gentle warning that this is bad
* practice.
*/
- chip_info(gpiochip,
+ chip_info(gc,
"detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
return;
}
- gpiochip->irq.irq_enable = irqchip->irq_enable;
- gpiochip->irq.irq_disable = irqchip->irq_disable;
- irqchip->irq_enable = gpiochip_irq_enable;
- irqchip->irq_disable = gpiochip_irq_disable;
+
+ if (irqchip->irq_disable) {
+ gc->irq.irq_disable = irqchip->irq_disable;
+ irqchip->irq_disable = gpiochip_irq_disable;
+ } else {
+ gc->irq.irq_mask = irqchip->irq_mask;
+ irqchip->irq_mask = gpiochip_irq_mask;
+ }
+
+ if (irqchip->irq_enable) {
+ gc->irq.irq_enable = irqchip->irq_enable;
+ irqchip->irq_enable = gpiochip_irq_enable;
+ } else {
+ gc->irq.irq_unmask = irqchip->irq_unmask;
+ irqchip->irq_unmask = gpiochip_irq_unmask;
+ }
}
/**
* gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip
- * @gpiochip: the GPIO chip to add the IRQ chip to
+ * @gc: the GPIO chip to add the IRQ chip to
* @lock_key: lockdep class for IRQ lock
* @request_key: lockdep class for IRQ request
*/
-static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
+static int gpiochip_add_irqchip(struct gpio_chip *gc,
struct lock_class_key *lock_key,
struct lock_class_key *request_key)
{
- struct irq_chip *irqchip = gpiochip->irq.chip;
- const struct irq_domain_ops *ops;
- struct device_node *np;
+ struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev);
+ struct irq_chip *irqchip = gc->irq.chip;
unsigned int type;
unsigned int i;
if (!irqchip)
return 0;
- if (gpiochip->irq.parent_handler && gpiochip->can_sleep) {
- chip_err(gpiochip, "you cannot have chained interrupts on a chip that may sleep\n");
+ if (gc->irq.parent_handler && gc->can_sleep) {
+ chip_err(gc, "you cannot have chained interrupts on a chip that may sleep\n");
return -EINVAL;
}
- np = gpiochip->gpiodev->dev.of_node;
- type = gpiochip->irq.default_type;
+ type = gc->irq.default_type;
/*
* Specifying a default trigger is a terrible idea if DT or ACPI is
* used to configure the interrupts, as you may end up with
* conflicting triggers. Tell the user, and reset to NONE.
*/
- if (WARN(np && type != IRQ_TYPE_NONE,
- "%s: Ignoring %u default trigger\n", np->full_name, type))
+ if (WARN(fwnode && type != IRQ_TYPE_NONE,
+ "%pfw: Ignoring %u default trigger\n", fwnode, type))
type = IRQ_TYPE_NONE;
- if (has_acpi_companion(gpiochip->parent) && type != IRQ_TYPE_NONE) {
- acpi_handle_warn(ACPI_HANDLE(gpiochip->parent),
- "Ignoring %u default trigger\n", type);
- type = IRQ_TYPE_NONE;
- }
+ if (gc->to_irq)
+ chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
- gpiochip->to_irq = gpiochip_to_irq;
- gpiochip->irq.default_type = type;
- gpiochip->irq.lock_key = lock_key;
- gpiochip->irq.request_key = request_key;
+ gc->to_irq = gpiochip_to_irq;
+ gc->irq.default_type = type;
+ gc->irq.lock_key = lock_key;
+ gc->irq.request_key = request_key;
- if (gpiochip->irq.domain_ops)
- ops = gpiochip->irq.domain_ops;
- else
- ops = &gpiochip_domain_ops;
+ /* If a parent irqdomain is provided, let's build a hierarchy */
+ if (gpiochip_hierarchy_is_hierarchical(gc)) {
+ int ret = gpiochip_hierarchy_add_domain(gc);
+ if (ret)
+ return ret;
+ } else {
+ /* Some drivers provide custom irqdomain ops */
+ gc->irq.domain = irq_domain_create_simple(fwnode,
+ gc->ngpio,
+ gc->irq.first,
+ gc->irq.domain_ops ?: &gpiochip_domain_ops,
+ gc);
+ if (!gc->irq.domain)
+ return -EINVAL;
+ }
- gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio,
- gpiochip->irq.first,
- ops, gpiochip);
- if (!gpiochip->irq.domain)
- return -EINVAL;
+ if (gc->irq.parent_handler) {
+ for (i = 0; i < gc->irq.num_parents; i++) {
+ void *data;
- if (gpiochip->irq.parent_handler) {
- void *data = gpiochip->irq.parent_handler_data ?: gpiochip;
+ if (gc->irq.per_parent_data)
+ data = gc->irq.parent_handler_data_array[i];
+ else
+ data = gc->irq.parent_handler_data ?: gc;
- for (i = 0; i < gpiochip->irq.num_parents; i++) {
/*
* The parent IRQ chip is already using the chip_data
* for this IRQ chip, so our callbacks simply use the
* handler_data.
*/
- irq_set_chained_handler_and_data(gpiochip->irq.parents[i],
- gpiochip->irq.parent_handler,
+ irq_set_chained_handler_and_data(gc->irq.parents[i],
+ gc->irq.parent_handler,
data);
}
}
- gpiochip_set_irq_hooks(gpiochip);
+ gpiochip_set_irq_hooks(gc);
+
+ /*
+ * Using barrier() here to prevent compiler from reordering
+ * gc->irq.initialized before initialization of above
+ * GPIO chip irq members.
+ */
+ barrier();
+
+ gc->irq.initialized = true;
- acpi_gpiochip_request_interrupts(gpiochip);
+ acpi_gpiochip_request_interrupts(gc);
return 0;
}
/**
* gpiochip_irqchip_remove() - removes an irqchip added to a gpiochip
- * @gpiochip: the gpiochip to remove the irqchip from
+ * @gc: the gpiochip to remove the irqchip from
*
* This is called only from gpiochip_remove()
*/
-static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
+static void gpiochip_irqchip_remove(struct gpio_chip *gc)
{
- struct irq_chip *irqchip = gpiochip->irq.chip;
+ struct irq_chip *irqchip = gc->irq.chip;
unsigned int offset;
- acpi_gpiochip_free_interrupts(gpiochip);
+ acpi_gpiochip_free_interrupts(gc);
- if (irqchip && gpiochip->irq.parent_handler) {
- struct gpio_irq_chip *irq = &gpiochip->irq;
+ if (irqchip && gc->irq.parent_handler) {
+ struct gpio_irq_chip *irq = &gc->irq;
unsigned int i;
for (i = 0; i < irq->num_parents; i++)
@@ -2005,180 +1639,123 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
}
/* Remove all IRQ mappings and delete the domain */
- if (gpiochip->irq.domain) {
+ if (gc->irq.domain) {
unsigned int irq;
- for (offset = 0; offset < gpiochip->ngpio; offset++) {
- if (!gpiochip_irqchip_irq_valid(gpiochip, offset))
+ for (offset = 0; offset < gc->ngpio; offset++) {
+ if (!gpiochip_irqchip_irq_valid(gc, offset))
continue;
- irq = irq_find_mapping(gpiochip->irq.domain, offset);
+ irq = irq_find_mapping(gc->irq.domain, offset);
irq_dispose_mapping(irq);
}
- irq_domain_remove(gpiochip->irq.domain);
+ irq_domain_remove(gc->irq.domain);
}
- if (irqchip) {
+ if (irqchip && !(irqchip->flags & IRQCHIP_IMMUTABLE)) {
if (irqchip->irq_request_resources == gpiochip_irq_reqres) {
irqchip->irq_request_resources = NULL;
irqchip->irq_release_resources = NULL;
}
if (irqchip->irq_enable == gpiochip_irq_enable) {
- irqchip->irq_enable = gpiochip->irq.irq_enable;
- irqchip->irq_disable = gpiochip->irq.irq_disable;
+ irqchip->irq_enable = gc->irq.irq_enable;
+ irqchip->irq_disable = gc->irq.irq_disable;
}
}
- gpiochip->irq.irq_enable = NULL;
- gpiochip->irq.irq_disable = NULL;
- gpiochip->irq.chip = NULL;
+ gc->irq.irq_enable = NULL;
+ gc->irq.irq_disable = NULL;
+ gc->irq.chip = NULL;
- gpiochip_irqchip_free_valid_mask(gpiochip);
+ gpiochip_irqchip_free_valid_mask(gc);
}
/**
- * gpiochip_irqchip_add_key() - adds an irqchip to a gpiochip
- * @gpiochip: the gpiochip to add the irqchip to
- * @irqchip: the irqchip to add to the gpiochip
- * @first_irq: if not dynamically assigned, the base (first) IRQ to
- * allocate gpiochip irqs from
- * @handler: the irq handler to use (often a predefined irq core function)
- * @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE
- * to have the core avoid setting up any default type in the hardware.
- * @threaded: whether this irqchip uses a nested thread handler
- * @lock_key: lockdep class for IRQ lock
- * @request_key: lockdep class for IRQ request
+ * gpiochip_irqchip_add_domain() - adds an irqdomain to a gpiochip
+ * @gc: the gpiochip to add the irqchip to
+ * @domain: the irqdomain to add to the gpiochip
*
- * This function closely associates a certain irqchip with a certain
- * gpiochip, providing an irq domain to translate the local IRQs to
- * global irqs in the gpiolib core, and making sure that the gpiochip
- * is passed as chip data to all related functions. Driver callbacks
- * need to use gpiochip_get_data() to get their local state containers back
- * from the gpiochip passed as chip data. An irqdomain will be stored
- * in the gpiochip that shall be used by the driver to handle IRQ number
- * translation. The gpiochip will need to be initialized and registered
- * before calling this function.
- *
- * This function will handle two cell:ed simple IRQs and assumes all
- * the pins on the gpiochip can generate a unique IRQ. Everything else
- * need to be open coded.
+ * This function adds an IRQ domain to the gpiochip.
*/
-int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip,
- struct irq_chip *irqchip,
- unsigned int first_irq,
- irq_flow_handler_t handler,
- unsigned int type,
- bool threaded,
- struct lock_class_key *lock_key,
- struct lock_class_key *request_key)
-{
- struct device_node *of_node;
-
- if (!gpiochip || !irqchip)
- return -EINVAL;
-
- if (!gpiochip->parent) {
- pr_err("missing gpiochip .dev parent pointer\n");
- return -EINVAL;
- }
- gpiochip->irq.threaded = threaded;
- of_node = gpiochip->parent->of_node;
-#ifdef CONFIG_OF_GPIO
- /*
- * If the gpiochip has an assigned OF node this takes precedence
- * FIXME: get rid of this and use gpiochip->parent->of_node
- * everywhere
- */
- if (gpiochip->of_node)
- of_node = gpiochip->of_node;
-#endif
- /*
- * Specifying a default trigger is a terrible idea if DT or ACPI is
- * used to configure the interrupts, as you may end-up with
- * conflicting triggers. Tell the user, and reset to NONE.
- */
- if (WARN(of_node && type != IRQ_TYPE_NONE,
- "%pOF: Ignoring %d default trigger\n", of_node, type))
- type = IRQ_TYPE_NONE;
- if (has_acpi_companion(gpiochip->parent) && type != IRQ_TYPE_NONE) {
- acpi_handle_warn(ACPI_HANDLE(gpiochip->parent),
- "Ignoring %d default trigger\n", type);
- type = IRQ_TYPE_NONE;
- }
-
- gpiochip->irq.chip = irqchip;
- gpiochip->irq.handler = handler;
- gpiochip->irq.default_type = type;
- gpiochip->to_irq = gpiochip_to_irq;
- gpiochip->irq.lock_key = lock_key;
- gpiochip->irq.request_key = request_key;
- gpiochip->irq.domain = irq_domain_add_simple(of_node,
- gpiochip->ngpio, first_irq,
- &gpiochip_domain_ops, gpiochip);
- if (!gpiochip->irq.domain) {
- gpiochip->irq.chip = NULL;
+int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
+ struct irq_domain *domain)
+{
+ if (!domain)
return -EINVAL;
- }
-
- gpiochip_set_irq_hooks(gpiochip);
- acpi_gpiochip_request_interrupts(gpiochip);
+ gc->to_irq = gpiochip_to_irq;
+ gc->irq.domain = domain;
return 0;
}
-EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key);
+EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_domain);
#else /* CONFIG_GPIOLIB_IRQCHIP */
-static inline int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
+static inline int gpiochip_add_irqchip(struct gpio_chip *gc,
struct lock_class_key *lock_key,
struct lock_class_key *request_key)
{
return 0;
}
+static void gpiochip_irqchip_remove(struct gpio_chip *gc) {}
+
+static inline int gpiochip_irqchip_init_hw(struct gpio_chip *gc)
+{
+ return 0;
+}
-static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
-static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
+static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc)
{
return 0;
}
-static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
+static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc)
{ }
#endif /* CONFIG_GPIOLIB_IRQCHIP */
/**
* gpiochip_generic_request() - request the gpio function for a pin
- * @chip: the gpiochip owning the GPIO
+ * @gc: the gpiochip owning the GPIO
* @offset: the offset of the GPIO to request for GPIO function
*/
-int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)
+int gpiochip_generic_request(struct gpio_chip *gc, unsigned int offset)
{
- return pinctrl_gpio_request(chip->gpiodev->base + offset);
+#ifdef CONFIG_PINCTRL
+ if (list_empty(&gc->gpiodev->pin_ranges))
+ return 0;
+#endif
+
+ return pinctrl_gpio_request(gc->gpiodev->base + offset);
}
EXPORT_SYMBOL_GPL(gpiochip_generic_request);
/**
* gpiochip_generic_free() - free the gpio function from a pin
- * @chip: the gpiochip to request the gpio function for
+ * @gc: the gpiochip to request the gpio function for
* @offset: the offset of the GPIO to free from GPIO function
*/
-void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset)
+void gpiochip_generic_free(struct gpio_chip *gc, unsigned int offset)
{
- pinctrl_gpio_free(chip->gpiodev->base + offset);
+#ifdef CONFIG_PINCTRL
+ if (list_empty(&gc->gpiodev->pin_ranges))
+ return;
+#endif
+
+ pinctrl_gpio_free(gc->gpiodev->base + offset);
}
EXPORT_SYMBOL_GPL(gpiochip_generic_free);
/**
* gpiochip_generic_config() - apply configuration for a pin
- * @chip: the gpiochip owning the GPIO
+ * @gc: the gpiochip owning the GPIO
* @offset: the offset of the GPIO to apply the configuration
* @config: the configuration to be applied
*/
-int gpiochip_generic_config(struct gpio_chip *chip, unsigned offset,
+int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset,
unsigned long config)
{
- return pinctrl_gpio_set_config(chip->gpiodev->base + offset, config);
+ return pinctrl_gpio_set_config(gc->gpiodev->base + offset, config);
}
EXPORT_SYMBOL_GPL(gpiochip_generic_config);
@@ -2186,7 +1763,7 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_config);
/**
* gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
- * @chip: the gpiochip to add the range for
+ * @gc: the gpiochip to add the range for
* @pctldev: the pin controller to map to
* @gpio_offset: the start offset in the current gpio_chip number space
* @pin_group: name of the pin group inside the pin controller
@@ -2196,24 +1773,24 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_config);
* Documentation/devicetree/bindings/gpio/gpio.txt on how to
* bind pinctrl and gpio drivers via the "gpio-ranges" property.
*/
-int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+int gpiochip_add_pingroup_range(struct gpio_chip *gc,
struct pinctrl_dev *pctldev,
unsigned int gpio_offset, const char *pin_group)
{
struct gpio_pin_range *pin_range;
- struct gpio_device *gdev = chip->gpiodev;
+ struct gpio_device *gdev = gc->gpiodev;
int ret;
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
if (!pin_range) {
- chip_err(chip, "failed to allocate pin ranges\n");
+ chip_err(gc, "failed to allocate pin ranges\n");
return -ENOMEM;
}
/* Use local offset as range ID */
pin_range->range.id = gpio_offset;
- pin_range->range.gc = chip;
- pin_range->range.name = chip->label;
+ pin_range->range.gc = gc;
+ pin_range->range.name = gc->label;
pin_range->range.base = gdev->base + gpio_offset;
pin_range->pctldev = pctldev;
@@ -2227,7 +1804,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
pinctrl_add_gpio_range(pctldev, &pin_range->range);
- chip_dbg(chip, "created GPIO range %d->%d ==> %s PINGRP %s\n",
+ chip_dbg(gc, "created GPIO range %d->%d ==> %s PINGRP %s\n",
gpio_offset, gpio_offset + pin_range->range.npins - 1,
pinctrl_dev_get_devname(pctldev), pin_group);
@@ -2239,7 +1816,7 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
- * @chip: the gpiochip to add the range for
+ * @gc: the gpiochip to add the range for
* @pinctl_name: the dev_name() of the pin controller to map to
* @gpio_offset: the start offset in the current gpio_chip number space
* @pin_offset: the start offset in the pin controller number space
@@ -2254,24 +1831,24 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
* Documentation/devicetree/bindings/gpio/gpio.txt on how to
* bind pinctrl and gpio drivers via the "gpio-ranges" property.
*/
-int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins)
{
struct gpio_pin_range *pin_range;
- struct gpio_device *gdev = chip->gpiodev;
+ struct gpio_device *gdev = gc->gpiodev;
int ret;
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
if (!pin_range) {
- chip_err(chip, "failed to allocate pin ranges\n");
+ chip_err(gc, "failed to allocate pin ranges\n");
return -ENOMEM;
}
/* Use local offset as range ID */
pin_range->range.id = gpio_offset;
- pin_range->range.gc = chip;
- pin_range->range.name = chip->label;
+ pin_range->range.gc = gc;
+ pin_range->range.name = gc->label;
pin_range->range.base = gdev->base + gpio_offset;
pin_range->range.pin_base = pin_offset;
pin_range->range.npins = npins;
@@ -2279,11 +1856,11 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
&pin_range->range);
if (IS_ERR(pin_range->pctldev)) {
ret = PTR_ERR(pin_range->pctldev);
- chip_err(chip, "could not create pin range\n");
+ chip_err(gc, "could not create pin range\n");
kfree(pin_range);
return ret;
}
- chip_dbg(chip, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
+ chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
gpio_offset, gpio_offset + npins - 1,
pinctl_name,
pin_offset, pin_offset + npins - 1);
@@ -2296,12 +1873,12 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pin_range);
/**
* gpiochip_remove_pin_ranges() - remove all the GPIO <-> pin mappings
- * @chip: the chip to remove all the mappings for
+ * @gc: the chip to remove all the mappings for
*/
-void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+void gpiochip_remove_pin_ranges(struct gpio_chip *gc)
{
struct gpio_pin_range *pin_range, *tmp;
- struct gpio_device *gdev = chip->gpiodev;
+ struct gpio_device *gdev = gc->gpiodev;
list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) {
list_del(&pin_range->node);
@@ -2320,8 +1897,8 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
*/
static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
{
- struct gpio_chip *chip = desc->gdev->chip;
- int status;
+ struct gpio_chip *gc = desc->gdev->chip;
+ int ret;
unsigned long flags;
unsigned offset;
@@ -2339,39 +1916,40 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
desc_set_label(desc, label ? : "?");
- status = 0;
} else {
- kfree_const(label);
- status = -EBUSY;
- goto done;
+ ret = -EBUSY;
+ goto out_free_unlock;
}
- if (chip->request) {
- /* chip->request may sleep */
+ if (gc->request) {
+ /* gc->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
offset = gpio_chip_hwgpio(desc);
- if (gpiochip_line_is_valid(chip, offset))
- status = chip->request(chip, offset);
+ if (gpiochip_line_is_valid(gc, offset))
+ ret = gc->request(gc, offset);
else
- status = -EINVAL;
+ ret = -EINVAL;
spin_lock_irqsave(&gpio_lock, flags);
- if (status < 0) {
+ if (ret) {
desc_set_label(desc, NULL);
- kfree_const(label);
clear_bit(FLAG_REQUESTED, &desc->flags);
- goto done;
+ goto out_free_unlock;
}
}
- if (chip->get_direction) {
- /* chip->get_direction may sleep */
+ if (gc->get_direction) {
+ /* gc->get_direction may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
gpiod_get_direction(desc);
spin_lock_irqsave(&gpio_lock, flags);
}
-done:
spin_unlock_irqrestore(&gpio_lock, flags);
- return status;
+ return 0;
+
+out_free_unlock:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ kfree_const(label);
+ return ret;
}
/*
@@ -2414,31 +1992,31 @@ static int validate_desc(const struct gpio_desc *desc, const char *func)
int gpiod_request(struct gpio_desc *desc, const char *label)
{
- int status = -EPROBE_DEFER;
+ int ret = -EPROBE_DEFER;
struct gpio_device *gdev;
VALIDATE_DESC(desc);
gdev = desc->gdev;
if (try_module_get(gdev->owner)) {
- status = gpiod_request_commit(desc, label);
- if (status < 0)
+ ret = gpiod_request_commit(desc, label);
+ if (ret)
module_put(gdev->owner);
else
get_device(&gdev->dev);
}
- if (status)
- gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ if (ret)
+ gpiod_dbg(desc, "%s: status %d\n", __func__, ret);
- return status;
+ return ret;
}
static bool gpiod_free_commit(struct gpio_desc *desc)
{
bool ret = false;
unsigned long flags;
- struct gpio_chip *chip;
+ struct gpio_chip *gc;
might_sleep();
@@ -2446,12 +2024,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
spin_lock_irqsave(&gpio_lock, flags);
- chip = desc->gdev->chip;
- if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
- if (chip->free) {
+ gc = desc->gdev->chip;
+ if (gc && test_bit(FLAG_REQUESTED, &desc->flags)) {
+ if (gc->free) {
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(chip->can_sleep);
- chip->free(chip, gpio_chip_hwgpio(desc));
+ might_sleep_if(gc->can_sleep);
+ gc->free(gc, gpio_chip_hwgpio(desc));
spin_lock_irqsave(&gpio_lock, flags);
}
kfree_const(desc->label);
@@ -2460,11 +2038,25 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
clear_bit(FLAG_REQUESTED, &desc->flags);
clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
+ clear_bit(FLAG_PULL_UP, &desc->flags);
+ clear_bit(FLAG_PULL_DOWN, &desc->flags);
+ clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
+ clear_bit(FLAG_EDGE_RISING, &desc->flags);
+ clear_bit(FLAG_EDGE_FALLING, &desc->flags);
clear_bit(FLAG_IS_HOGGED, &desc->flags);
+#ifdef CONFIG_OF_DYNAMIC
+ desc->hog = NULL;
+#endif
+#ifdef CONFIG_GPIO_CDEV
+ WRITE_ONCE(desc->debounce_period_us, 0);
+#endif
ret = true;
}
spin_unlock_irqrestore(&gpio_lock, flags);
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIOLINE_CHANGED_RELEASED, desc);
+
return ret;
}
@@ -2480,7 +2072,7 @@ void gpiod_free(struct gpio_desc *desc)
/**
* gpiochip_is_requested - return string iff signal was requested
- * @chip: controller managing the signal
+ * @gc: controller managing the signal
* @offset: of signal within controller's 0..(ngpio - 1) range
*
* Returns NULL if the GPIO is not currently requested, else a string.
@@ -2491,15 +2083,14 @@ void gpiod_free(struct gpio_desc *desc)
* help with diagnostics, and knowing that the signal is used as a GPIO
* can help avoid accidentally multiplexing it to another controller.
*/
-const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
+const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_desc *desc;
- if (offset >= chip->ngpio)
+ desc = gpiochip_get_desc(gc, offset);
+ if (IS_ERR(desc))
return NULL;
- desc = &chip->gpiodev->descs[offset];
-
if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
return NULL;
return desc->label;
@@ -2508,7 +2099,7 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
/**
* gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor
- * @chip: GPIO chip
+ * @gc: GPIO chip
* @hwnum: hardware number of the GPIO for which to request the descriptor
* @label: label for the GPIO
* @lflags: lookup flags for this GPIO or 0 if default, this can be used to
@@ -2527,28 +2118,29 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* A pointer to the GPIO descriptor, or an ERR_PTR()-encoded negative error
* code on failure.
*/
-struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
+struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
+ unsigned int hwnum,
const char *label,
enum gpio_lookup_flags lflags,
enum gpiod_flags dflags)
{
- struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
- int err;
+ struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum);
+ int ret;
if (IS_ERR(desc)) {
- chip_err(chip, "failed to get GPIO descriptor\n");
+ chip_err(gc, "failed to get GPIO descriptor\n");
return desc;
}
- err = gpiod_request_commit(desc, label);
- if (err < 0)
- return ERR_PTR(err);
+ ret = gpiod_request_commit(desc, label);
+ if (ret < 0)
+ return ERR_PTR(ret);
- err = gpiod_configure_flags(desc, label, lflags, dflags);
- if (err) {
- chip_err(chip, "setup of own GPIO %s failed\n", label);
+ ret = gpiod_configure_flags(desc, label, lflags, dflags);
+ if (ret) {
+ chip_err(gc, "setup of own GPIO %s failed\n", label);
gpiod_free_commit(desc);
- return ERR_PTR(err);
+ return ERR_PTR(ret);
}
return desc;
@@ -2579,13 +2171,69 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
* rely on gpio_request() having been called beforehand.
*/
-static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
- enum pin_config_param mode)
+static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset,
+ unsigned long config)
{
+ if (!gc->set_config)
+ return -ENOTSUPP;
+
+ return gc->set_config(gc, offset, config);
+}
+
+static int gpio_set_config_with_argument(struct gpio_desc *desc,
+ enum pin_config_param mode,
+ u32 argument)
+{
+ struct gpio_chip *gc = desc->gdev->chip;
unsigned long config;
- unsigned arg;
+
+ config = pinconf_to_config_packed(mode, argument);
+ return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config);
+}
+
+static int gpio_set_config_with_argument_optional(struct gpio_desc *desc,
+ enum pin_config_param mode,
+ u32 argument)
+{
+ struct device *dev = &desc->gdev->dev;
+ int gpio = gpio_chip_hwgpio(desc);
+ int ret;
+
+ ret = gpio_set_config_with_argument(desc, mode, argument);
+ if (ret != -ENOTSUPP)
+ return ret;
switch (mode) {
+ case PIN_CONFIG_PERSIST_STATE:
+ dev_dbg(dev, "Persistence not supported for GPIO %d\n", gpio);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode)
+{
+ return gpio_set_config_with_argument(desc, mode, 0);
+}
+
+static int gpio_set_bias(struct gpio_desc *desc)
+{
+ enum pin_config_param bias;
+ unsigned int arg;
+
+ if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
+ bias = PIN_CONFIG_BIAS_DISABLE;
+ else if (test_bit(FLAG_PULL_UP, &desc->flags))
+ bias = PIN_CONFIG_BIAS_PULL_UP;
+ else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
+ bias = PIN_CONFIG_BIAS_PULL_DOWN;
+ else
+ return 0;
+
+ switch (bias) {
case PIN_CONFIG_BIAS_PULL_DOWN:
case PIN_CONFIG_BIAS_PULL_UP:
arg = 1;
@@ -2593,10 +2241,27 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
default:
arg = 0;
+ break;
}
- config = PIN_CONF_PACKED(mode, arg);
- return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP;
+ return gpio_set_config_with_argument_optional(desc, bias, arg);
+}
+
+/**
+ * gpio_set_debounce_timeout() - Set debounce timeout
+ * @desc: GPIO descriptor to set the debounce timeout
+ * @debounce: Debounce timeout in microseconds
+ *
+ * The function calls the certain GPIO driver to set debounce timeout
+ * in the hardware.
+ *
+ * Returns 0 on success, or negative error code otherwise.
+ */
+int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
+{
+ return gpio_set_config_with_argument_optional(desc,
+ PIN_CONFIG_INPUT_DEBOUNCE,
+ debounce);
}
/**
@@ -2610,18 +2275,18 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
*/
int gpiod_direction_input(struct gpio_desc *desc)
{
- struct gpio_chip *chip;
- int status = 0;
+ struct gpio_chip *gc;
+ int ret = 0;
VALIDATE_DESC(desc);
- chip = desc->gdev->chip;
+ gc = desc->gdev->chip;
/*
* It is legal to have no .get() and .direction_input() specified if
* the chip is output-only, but you can't specify .direction_input()
* and not support the .get() operation, that doesn't make sense.
*/
- if (!chip->get && chip->direction_input) {
+ if (!gc->get && gc->direction_input) {
gpiod_warn(desc,
"%s: missing get() but have direction_input()\n",
__func__);
@@ -2634,28 +2299,23 @@ int gpiod_direction_input(struct gpio_desc *desc)
* direction (if .get_direction() is supported) else we silently
* assume we are in input mode after this.
*/
- if (chip->direction_input) {
- status = chip->direction_input(chip, gpio_chip_hwgpio(desc));
- } else if (chip->get_direction &&
- (chip->get_direction(chip, gpio_chip_hwgpio(desc)) != 1)) {
+ if (gc->direction_input) {
+ ret = gc->direction_input(gc, gpio_chip_hwgpio(desc));
+ } else if (gc->get_direction &&
+ (gc->get_direction(gc, gpio_chip_hwgpio(desc)) != 1)) {
gpiod_warn(desc,
"%s: missing direction_input() operation and line is output\n",
__func__);
return -EIO;
}
- if (status == 0)
+ if (ret == 0) {
clear_bit(FLAG_IS_OUT, &desc->flags);
+ ret = gpio_set_bias(desc);
+ }
- if (test_bit(FLAG_PULL_UP, &desc->flags))
- gpio_set_config(chip, gpio_chip_hwgpio(desc),
- PIN_CONFIG_BIAS_PULL_UP);
- else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
- gpio_set_config(chip, gpio_chip_hwgpio(desc),
- PIN_CONFIG_BIAS_PULL_DOWN);
-
- trace_gpio_direction(desc_to_gpio(desc), 1, status);
+ trace_gpio_direction(desc_to_gpio(desc), 1, ret);
- return status;
+ return ret;
}
EXPORT_SYMBOL_GPL(gpiod_direction_input);
@@ -2734,7 +2394,6 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
- struct gpio_chip *gc;
int ret;
VALIDATE_DESC(desc);
@@ -2752,36 +2411,127 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
return -EIO;
}
- gc = desc->gdev->chip;
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
/* First see if we can enable open drain in hardware */
- ret = gpio_set_config(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_OPEN_DRAIN);
+ ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_DRAIN);
if (!ret)
goto set_output_value;
/* Emulate open drain by not actively driving the line high */
- if (value)
- return gpiod_direction_input(desc);
- }
- else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
- ret = gpio_set_config(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_OPEN_SOURCE);
+ if (value) {
+ ret = gpiod_direction_input(desc);
+ goto set_output_flag;
+ }
+ } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
+ ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE);
if (!ret)
goto set_output_value;
/* Emulate open source by not actively driving the line low */
- if (!value)
- return gpiod_direction_input(desc);
+ if (!value) {
+ ret = gpiod_direction_input(desc);
+ goto set_output_flag;
+ }
} else {
- gpio_set_config(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_PUSH_PULL);
+ gpio_set_config(desc, PIN_CONFIG_DRIVE_PUSH_PULL);
}
set_output_value:
+ ret = gpio_set_bias(desc);
+ if (ret)
+ return ret;
return gpiod_direction_output_raw_commit(desc, value);
+
+set_output_flag:
+ /*
+ * When emulating open-source or open-drain functionalities by not
+ * actively driving the line (setting mode to input) we still need to
+ * set the IS_OUT flag or otherwise we won't be able to set the line
+ * value anymore.
+ */
+ if (ret == 0)
+ set_bit(FLAG_IS_OUT, &desc->flags);
+ return ret;
}
EXPORT_SYMBOL_GPL(gpiod_direction_output);
/**
+ * gpiod_enable_hw_timestamp_ns - Enable hardware timestamp in nanoseconds.
+ *
+ * @desc: GPIO to enable.
+ * @flags: Flags related to GPIO edge.
+ *
+ * Return 0 in case of success, else negative error code.
+ */
+int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
+{
+ int ret = 0;
+ struct gpio_chip *gc;
+
+ VALIDATE_DESC(desc);
+
+ gc = desc->gdev->chip;
+ if (!gc->en_hw_timestamp) {
+ gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
+ return -ENOTSUPP;
+ }
+
+ ret = gc->en_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags);
+ if (ret)
+ gpiod_warn(desc, "%s: hw ts request failed\n", __func__);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns);
+
+/**
+ * gpiod_disable_hw_timestamp_ns - Disable hardware timestamp.
+ *
+ * @desc: GPIO to disable.
+ * @flags: Flags related to GPIO edge, same value as used during enable call.
+ *
+ * Return 0 in case of success, else negative error code.
+ */
+int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
+{
+ int ret = 0;
+ struct gpio_chip *gc;
+
+ VALIDATE_DESC(desc);
+
+ gc = desc->gdev->chip;
+ if (!gc->dis_hw_timestamp) {
+ gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
+ return -ENOTSUPP;
+ }
+
+ ret = gc->dis_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags);
+ if (ret)
+ gpiod_warn(desc, "%s: hw ts release failed\n", __func__);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns);
+
+/**
+ * gpiod_set_config - sets @config for a GPIO
+ * @desc: descriptor of the GPIO for which to set the configuration
+ * @config: Same packed config format as generic pinconf
+ *
+ * Returns:
+ * 0 on success, %-ENOTSUPP if the controller doesn't support setting the
+ * configuration.
+ */
+int gpiod_set_config(struct gpio_desc *desc, unsigned long config)
+{
+ struct gpio_chip *gc;
+
+ VALIDATE_DESC(desc);
+ gc = desc->gdev->chip;
+
+ return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_config);
+
+/**
* gpiod_set_debounce - sets @debounce time for a GPIO
* @desc: descriptor of the GPIO for which to set debounce time
* @debounce: debounce time in microseconds
@@ -2790,22 +2540,12 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output);
* 0 on success, %-ENOTSUPP if the controller doesn't support setting the
* debounce time.
*/
-int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
+int gpiod_set_debounce(struct gpio_desc *desc, unsigned int debounce)
{
- struct gpio_chip *chip;
- unsigned long config;
-
- VALIDATE_DESC(desc);
- chip = desc->gdev->chip;
- if (!chip->set || !chip->set_config) {
- gpiod_dbg(desc,
- "%s: missing set() or set_config() operations\n",
- __func__);
- return -ENOTSUPP;
- }
+ unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce);
- return chip->set_config(chip, gpio_chip_hwgpio(desc), config);
+ return gpiod_set_config(desc, config);
}
EXPORT_SYMBOL_GPL(gpiod_set_debounce);
@@ -2819,37 +2559,17 @@ EXPORT_SYMBOL_GPL(gpiod_set_debounce);
*/
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
{
- struct gpio_chip *chip;
- unsigned long packed;
- int gpio;
- int rc;
-
VALIDATE_DESC(desc);
/*
* Handle FLAG_TRANSITORY first, enabling queries to gpiolib for
* persistence state.
*/
- if (transitory)
- set_bit(FLAG_TRANSITORY, &desc->flags);
- else
- clear_bit(FLAG_TRANSITORY, &desc->flags);
+ assign_bit(FLAG_TRANSITORY, &desc->flags, transitory);
/* If the driver supports it, set the persistence state now */
- chip = desc->gdev->chip;
- if (!chip->set_config)
- return 0;
-
- packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE,
- !transitory);
- gpio = gpio_chip_hwgpio(desc);
- rc = chip->set_config(chip, gpio, packed);
- if (rc == -ENOTSUPP) {
- dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n",
- gpio);
- return 0;
- }
-
- return rc;
+ return gpio_set_config_with_argument_optional(desc,
+ PIN_CONFIG_PERSIST_STATE,
+ !transitory);
}
EXPORT_SYMBOL_GPL(gpiod_set_transitory);
@@ -2866,6 +2586,22 @@ int gpiod_is_active_low(const struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(gpiod_is_active_low);
+/**
+ * gpiod_toggle_active_low - toggle whether a GPIO is active-low or not
+ * @desc: the gpio descriptor to change
+ */
+void gpiod_toggle_active_low(struct gpio_desc *desc)
+{
+ VALIDATE_DESC_VOID(desc);
+ change_bit(FLAG_ACTIVE_LOW, &desc->flags);
+}
+EXPORT_SYMBOL_GPL(gpiod_toggle_active_low);
+
+static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc)
+{
+ return gc->get ? gc->get(gc, gpio_chip_hwgpio(desc)) : -EIO;
+}
+
/* I/O calls are only valid after configuration completed; the relevant
* "is this a valid GPIO" error checks should already have been done.
*
@@ -2890,28 +2626,26 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low);
static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
{
- struct gpio_chip *chip;
- int offset;
+ struct gpio_chip *gc;
int value;
- chip = desc->gdev->chip;
- offset = gpio_chip_hwgpio(desc);
- value = chip->get ? chip->get(chip, offset) : -EIO;
+ gc = desc->gdev->chip;
+ value = gpio_chip_get_value(gc, desc);
value = value < 0 ? value : !!value;
trace_gpio_value(desc_to_gpio(desc), 1, value);
return value;
}
-static int gpio_chip_get_multiple(struct gpio_chip *chip,
+static int gpio_chip_get_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
- if (chip->get_multiple) {
- return chip->get_multiple(chip, mask, bits);
- } else if (chip->get) {
+ if (gc->get_multiple)
+ return gc->get_multiple(gc, mask, bits);
+ if (gc->get) {
int i, value;
- for_each_set_bit(i, mask, chip->ngpio) {
- value = chip->get(chip, i);
+ for_each_set_bit(i, mask, gc->ngpio) {
+ value = gc->get(gc, i);
if (value < 0)
return value;
__assign_bit(i, bits, value);
@@ -2927,7 +2661,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- int err, i = 0;
+ int ret, i = 0;
/*
* Validate array_info against desc_array and its size.
@@ -2940,45 +2674,51 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
if (!can_sleep)
WARN_ON(array_info->chip->can_sleep);
- err = gpio_chip_get_multiple(array_info->chip,
+ ret = gpio_chip_get_multiple(array_info->chip,
array_info->get_mask,
value_bitmap);
- if (err)
- return err;
+ if (ret)
+ return ret;
if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
bitmap_xor(value_bitmap, value_bitmap,
array_info->invert_mask, array_size);
- if (bitmap_full(array_info->get_mask, array_size))
- return 0;
-
i = find_first_zero_bit(array_info->get_mask, array_size);
+ if (i == array_size)
+ return 0;
} else {
array_info = NULL;
}
while (i < array_size) {
- struct gpio_chip *chip = desc_array[i]->gdev->chip;
- unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
+ struct gpio_chip *gc = desc_array[i]->gdev->chip;
+ DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
+ DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
unsigned long *mask, *bits;
- int first, j, ret;
+ int first, j;
- if (likely(chip->ngpio <= FASTPATH_NGPIO)) {
- mask = fastpath;
+ if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
+ mask = fastpath_mask;
+ bits = fastpath_bits;
} else {
- mask = kmalloc_array(2 * BITS_TO_LONGS(chip->ngpio),
- sizeof(*mask),
- can_sleep ? GFP_KERNEL : GFP_ATOMIC);
+ gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
+
+ mask = bitmap_alloc(gc->ngpio, flags);
if (!mask)
return -ENOMEM;
+
+ bits = bitmap_alloc(gc->ngpio, flags);
+ if (!bits) {
+ bitmap_free(mask);
+ return -ENOMEM;
+ }
}
- bits = mask + BITS_TO_LONGS(chip->ngpio);
- bitmap_zero(mask, chip->ngpio);
+ bitmap_zero(mask, gc->ngpio);
if (!can_sleep)
- WARN_ON(chip->can_sleep);
+ WARN_ON(gc->can_sleep);
/* collect all inputs belonging to the same chip */
first = i;
@@ -2993,12 +2733,14 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
i = find_next_zero_bit(array_info->get_mask,
array_size, i);
} while ((i < array_size) &&
- (desc_array[i]->gdev->chip == chip));
+ (desc_array[i]->gdev->chip == gc));
- ret = gpio_chip_get_multiple(chip, mask, bits);
+ ret = gpio_chip_get_multiple(gc, mask, bits);
if (ret) {
- if (mask != fastpath)
- kfree(mask);
+ if (mask != fastpath_mask)
+ bitmap_free(mask);
+ if (bits != fastpath_bits)
+ bitmap_free(bits);
return ret;
}
@@ -3018,8 +2760,10 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
j);
}
- if (mask != fastpath)
- kfree(mask);
+ if (mask != fastpath_mask)
+ bitmap_free(mask);
+ if (bits != fastpath_bits)
+ bitmap_free(bits);
}
return 0;
}
@@ -3132,24 +2876,22 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value);
*/
static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
{
- int err = 0;
- struct gpio_chip *chip = desc->gdev->chip;
+ int ret = 0;
+ struct gpio_chip *gc = desc->gdev->chip;
int offset = gpio_chip_hwgpio(desc);
if (value) {
- err = chip->direction_input(chip, offset);
- if (!err)
- clear_bit(FLAG_IS_OUT, &desc->flags);
+ ret = gc->direction_input(gc, offset);
} else {
- err = chip->direction_output(chip, offset, 0);
- if (!err)
+ ret = gc->direction_output(gc, offset, 0);
+ if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
}
- trace_gpio_direction(desc_to_gpio(desc), value, err);
- if (err < 0)
+ trace_gpio_direction(desc_to_gpio(desc), value, ret);
+ if (ret < 0)
gpiod_err(desc,
"%s: Error in set_value for open drain err %d\n",
- __func__, err);
+ __func__, ret);
}
/*
@@ -3159,55 +2901,54 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
*/
static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
{
- int err = 0;
- struct gpio_chip *chip = desc->gdev->chip;
+ int ret = 0;
+ struct gpio_chip *gc = desc->gdev->chip;
int offset = gpio_chip_hwgpio(desc);
if (value) {
- err = chip->direction_output(chip, offset, 1);
- if (!err)
+ ret = gc->direction_output(gc, offset, 1);
+ if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
} else {
- err = chip->direction_input(chip, offset);
- if (!err)
- clear_bit(FLAG_IS_OUT, &desc->flags);
+ ret = gc->direction_input(gc, offset);
}
- trace_gpio_direction(desc_to_gpio(desc), !value, err);
- if (err < 0)
+ trace_gpio_direction(desc_to_gpio(desc), !value, ret);
+ if (ret < 0)
gpiod_err(desc,
"%s: Error in set_value for open source err %d\n",
- __func__, err);
+ __func__, ret);
}
static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
{
- struct gpio_chip *chip;
+ struct gpio_chip *gc;
- chip = desc->gdev->chip;
+ gc = desc->gdev->chip;
trace_gpio_value(desc_to_gpio(desc), 0, value);
- chip->set(chip, gpio_chip_hwgpio(desc), value);
+ gc->set(gc, gpio_chip_hwgpio(desc), value);
}
/*
* set multiple outputs on the same chip;
* use the chip's set_multiple function if available;
* otherwise set the outputs sequentially;
+ * @chip: the GPIO chip we operate on
* @mask: bit mask array; one bit per output; BITS_PER_LONG bits per word
* defines which outputs are to be changed
* @bits: bit value array; one bit per output; BITS_PER_LONG bits per word
* defines the values the outputs specified by mask are to be set to
*/
-static void gpio_chip_set_multiple(struct gpio_chip *chip,
+static void gpio_chip_set_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
- if (chip->set_multiple) {
- chip->set_multiple(chip, mask, bits);
+ if (gc->set_multiple) {
+ gc->set_multiple(gc, mask, bits);
} else {
unsigned int i;
/* set outputs if the corresponding mask bit is set */
- for_each_set_bit(i, mask, chip->ngpio)
- chip->set(chip, i, test_bit(i, bits));
+ for_each_set_bit(i, mask, gc->ngpio)
+ gc->set(gc, i, test_bit(i, bits));
}
}
@@ -3237,35 +2978,41 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
value_bitmap);
- if (bitmap_full(array_info->set_mask, array_size))
- return 0;
-
i = find_first_zero_bit(array_info->set_mask, array_size);
+ if (i == array_size)
+ return 0;
} else {
array_info = NULL;
}
while (i < array_size) {
- struct gpio_chip *chip = desc_array[i]->gdev->chip;
- unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
+ struct gpio_chip *gc = desc_array[i]->gdev->chip;
+ DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
+ DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
unsigned long *mask, *bits;
int count = 0;
- if (likely(chip->ngpio <= FASTPATH_NGPIO)) {
- mask = fastpath;
+ if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
+ mask = fastpath_mask;
+ bits = fastpath_bits;
} else {
- mask = kmalloc_array(2 * BITS_TO_LONGS(chip->ngpio),
- sizeof(*mask),
- can_sleep ? GFP_KERNEL : GFP_ATOMIC);
+ gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
+
+ mask = bitmap_alloc(gc->ngpio, flags);
if (!mask)
return -ENOMEM;
+
+ bits = bitmap_alloc(gc->ngpio, flags);
+ if (!bits) {
+ bitmap_free(mask);
+ return -ENOMEM;
+ }
}
- bits = mask + BITS_TO_LONGS(chip->ngpio);
- bitmap_zero(mask, chip->ngpio);
+ bitmap_zero(mask, gc->ngpio);
if (!can_sleep)
- WARN_ON(chip->can_sleep);
+ WARN_ON(gc->can_sleep);
do {
struct gpio_desc *desc = desc_array[i];
@@ -3292,10 +3039,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
gpio_set_open_source_value_commit(desc, value);
} else {
__set_bit(hwgpio, mask);
- if (value)
- __set_bit(hwgpio, bits);
- else
- __clear_bit(hwgpio, bits);
+ __assign_bit(hwgpio, bits, value);
count++;
}
i++;
@@ -3304,13 +3048,15 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
i = find_next_zero_bit(array_info->set_mask,
array_size, i);
} while ((i < array_size) &&
- (desc_array[i]->gdev->chip == chip));
+ (desc_array[i]->gdev->chip == gc));
/* push collected bits to outputs */
if (count != 0)
- gpio_chip_set_multiple(chip, mask, bits);
+ gpio_chip_set_multiple(gc, mask, bits);
- if (mask != fastpath)
- kfree(mask);
+ if (mask != fastpath_mask)
+ bitmap_free(mask);
+ if (bits != fastpath_bits)
+ bitmap_free(bits);
}
return 0;
}
@@ -3469,7 +3215,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
*/
int gpiod_to_irq(const struct gpio_desc *desc)
{
- struct gpio_chip *chip;
+ struct gpio_chip *gc;
int offset;
/*
@@ -3480,10 +3226,10 @@ int gpiod_to_irq(const struct gpio_desc *desc)
if (!desc || IS_ERR(desc) || !desc->gdev || !desc->gdev->chip)
return -EINVAL;
- chip = desc->gdev->chip;
+ gc = desc->gdev->chip;
offset = gpio_chip_hwgpio(desc);
- if (chip->to_irq) {
- int retirq = chip->to_irq(chip, offset);
+ if (gc->to_irq) {
+ int retirq = gc->to_irq(gc, offset);
/* Zero means NO_IRQ */
if (!retirq)
@@ -3491,23 +3237,33 @@ int gpiod_to_irq(const struct gpio_desc *desc)
return retirq;
}
+#ifdef CONFIG_GPIOLIB_IRQCHIP
+ if (gc->irq.chip) {
+ /*
+ * Avoid race condition with other code, which tries to lookup
+ * an IRQ before the irqchip has been properly registered,
+ * i.e. while gpiochip is still being brought up.
+ */
+ return -EPROBE_DEFER;
+ }
+#endif
return -ENXIO;
}
EXPORT_SYMBOL_GPL(gpiod_to_irq);
/**
* gpiochip_lock_as_irq() - lock a GPIO to be used as IRQ
- * @chip: the chip the GPIO to lock belongs to
+ * @gc: the chip the GPIO to lock belongs to
* @offset: the offset of the GPIO to lock as IRQ
*
* This is used directly by GPIO drivers that want to lock down
* a certain GPIO line to be used for IRQs.
*/
-int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
+int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_desc *desc;
- desc = gpiochip_get_desc(chip, offset);
+ desc = gpiochip_get_desc(gc, offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -3515,18 +3271,20 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
* If it's fast: flush the direction setting if something changed
* behind our back
*/
- if (!chip->can_sleep && chip->get_direction) {
+ if (!gc->can_sleep && gc->get_direction) {
int dir = gpiod_get_direction(desc);
if (dir < 0) {
- chip_err(chip, "%s: cannot get GPIO direction\n",
+ chip_err(gc, "%s: cannot get GPIO direction\n",
__func__);
return dir;
}
}
- if (test_bit(FLAG_IS_OUT, &desc->flags)) {
- chip_err(chip,
+ /* To be valid for IRQ the line needs to be input or open drain */
+ if (test_bit(FLAG_IS_OUT, &desc->flags) &&
+ !test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
+ chip_err(gc,
"%s: tried to flag a GPIO set as output for IRQ\n",
__func__);
return -EIO;
@@ -3549,17 +3307,17 @@ EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
/**
* gpiochip_unlock_as_irq() - unlock a GPIO used as IRQ
- * @chip: the chip the GPIO to lock belongs to
+ * @gc: the chip the GPIO to lock belongs to
* @offset: the offset of the GPIO to lock as IRQ
*
* This is used directly by GPIO drivers that want to indicate
* that a certain GPIO is no longer used exclusively for IRQ.
*/
-void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
+void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_desc *desc;
- desc = gpiochip_get_desc(chip, offset);
+ desc = gpiochip_get_desc(gc, offset);
if (IS_ERR(desc))
return;
@@ -3572,9 +3330,9 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
}
EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
-void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset)
+void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset)
{
- struct gpio_desc *desc = gpiochip_get_desc(chip, offset);
+ struct gpio_desc *desc = gpiochip_get_desc(gc, offset);
if (!IS_ERR(desc) &&
!WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags)))
@@ -3582,75 +3340,80 @@ void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset)
}
EXPORT_SYMBOL_GPL(gpiochip_disable_irq);
-void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset)
+void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset)
{
- struct gpio_desc *desc = gpiochip_get_desc(chip, offset);
+ struct gpio_desc *desc = gpiochip_get_desc(gc, offset);
if (!IS_ERR(desc) &&
!WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) {
- WARN_ON(test_bit(FLAG_IS_OUT, &desc->flags));
+ /*
+ * We must not be output when using IRQ UNLESS we are
+ * open drain.
+ */
+ WARN_ON(test_bit(FLAG_IS_OUT, &desc->flags) &&
+ !test_bit(FLAG_OPEN_DRAIN, &desc->flags));
set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
}
}
EXPORT_SYMBOL_GPL(gpiochip_enable_irq);
-bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset)
+bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset)
{
- if (offset >= chip->ngpio)
+ if (offset >= gc->ngpio)
return false;
- return test_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
+ return test_bit(FLAG_USED_AS_IRQ, &gc->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_irq);
-int gpiochip_reqres_irq(struct gpio_chip *chip, unsigned int offset)
+int gpiochip_reqres_irq(struct gpio_chip *gc, unsigned int offset)
{
int ret;
- if (!try_module_get(chip->gpiodev->owner))
+ if (!try_module_get(gc->gpiodev->owner))
return -ENODEV;
- ret = gpiochip_lock_as_irq(chip, offset);
+ ret = gpiochip_lock_as_irq(gc, offset);
if (ret) {
- chip_err(chip, "unable to lock HW IRQ %u for IRQ\n", offset);
- module_put(chip->gpiodev->owner);
+ chip_err(gc, "unable to lock HW IRQ %u for IRQ\n", offset);
+ module_put(gc->gpiodev->owner);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_reqres_irq);
-void gpiochip_relres_irq(struct gpio_chip *chip, unsigned int offset)
+void gpiochip_relres_irq(struct gpio_chip *gc, unsigned int offset)
{
- gpiochip_unlock_as_irq(chip, offset);
- module_put(chip->gpiodev->owner);
+ gpiochip_unlock_as_irq(gc, offset);
+ module_put(gc->gpiodev->owner);
}
EXPORT_SYMBOL_GPL(gpiochip_relres_irq);
-bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset)
+bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset)
{
- if (offset >= chip->ngpio)
+ if (offset >= gc->ngpio)
return false;
- return test_bit(FLAG_OPEN_DRAIN, &chip->gpiodev->descs[offset].flags);
+ return test_bit(FLAG_OPEN_DRAIN, &gc->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain);
-bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset)
+bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset)
{
- if (offset >= chip->ngpio)
+ if (offset >= gc->ngpio)
return false;
- return test_bit(FLAG_OPEN_SOURCE, &chip->gpiodev->descs[offset].flags);
+ return test_bit(FLAG_OPEN_SOURCE, &gc->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source);
-bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset)
+bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset)
{
- if (offset >= chip->ngpio)
+ if (offset >= gc->ngpio)
return false;
- return !test_bit(FLAG_TRANSITORY, &chip->gpiodev->descs[offset].flags);
+ return !test_bit(FLAG_TRANSITORY, &gc->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent);
@@ -3860,11 +3623,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
*/
void gpiod_add_lookup_table(struct gpiod_lookup_table *table)
{
- mutex_lock(&gpio_lookup_lock);
-
- list_add_tail(&table->list, &gpio_lookup_list);
-
- mutex_unlock(&gpio_lookup_lock);
+ gpiod_add_lookup_tables(&table, 1);
}
EXPORT_SYMBOL_GPL(gpiod_add_lookup_table);
@@ -3874,6 +3633,10 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table);
*/
void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
{
+ /* Nothing to remove */
+ if (!table)
+ return;
+
mutex_lock(&gpio_lookup_lock);
list_del(&table->list);
@@ -3888,7 +3651,7 @@ EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table);
*/
void gpiod_add_hogs(struct gpiod_hog *hogs)
{
- struct gpio_chip *chip;
+ struct gpio_chip *gc;
struct gpiod_hog *hog;
mutex_lock(&gpio_machine_hogs_mutex);
@@ -3900,15 +3663,26 @@ void gpiod_add_hogs(struct gpiod_hog *hogs)
* The chip may have been registered earlier, so check if it
* exists and, if so, try to hog the line now.
*/
- chip = find_chip_by_name(hog->chip_label);
- if (chip)
- gpiochip_machine_hog(chip, hog);
+ gc = find_chip_by_name(hog->chip_label);
+ if (gc)
+ gpiochip_machine_hog(gc, hog);
}
mutex_unlock(&gpio_machine_hogs_mutex);
}
EXPORT_SYMBOL_GPL(gpiod_add_hogs);
+void gpiod_remove_hogs(struct gpiod_hog *hogs)
+{
+ struct gpiod_hog *hog;
+
+ mutex_lock(&gpio_machine_hogs_mutex);
+ for (hog = &hogs[0]; hog->chip_label; hog++)
+ list_del(&hog->list);
+ mutex_unlock(&gpio_machine_hogs_mutex);
+}
+EXPORT_SYMBOL_GPL(gpiod_remove_hogs);
+
static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev)
{
const char *dev_id = dev ? dev_name(dev) : NULL;
@@ -3951,8 +3725,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
if (!table)
return desc;
- for (p = &table->table[0]; p->chip_label; p++) {
- struct gpio_chip *chip;
+ for (p = &table->table[0]; p->key; p++) {
+ struct gpio_chip *gc;
/* idx must always match exactly */
if (p->idx != idx)
@@ -3962,29 +3736,42 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
if (p->con_id && (!con_id || strcmp(p->con_id, con_id)))
continue;
- chip = find_chip_by_name(p->chip_label);
+ if (p->chip_hwnum == U16_MAX) {
+ desc = gpio_name_to_desc(p->key);
+ if (desc) {
+ *flags = p->flags;
+ return desc;
+ }
- if (!chip) {
+ dev_warn(dev, "cannot find GPIO line %s, deferring\n",
+ p->key);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ gc = find_chip_by_name(p->key);
+
+ if (!gc) {
/*
* As the lookup table indicates a chip with
- * p->chip_label should exist, assume it may
+ * p->key should exist, assume it may
* still appear later and let the interested
* consumer be probed again or let the Deferred
* Probe infrastructure handle the error.
*/
dev_warn(dev, "cannot find GPIO chip %s, deferring\n",
- p->chip_label);
+ p->key);
return ERR_PTR(-EPROBE_DEFER);
}
- if (chip->ngpio <= p->chip_hwnum) {
+ if (gc->ngpio <= p->chip_hwnum) {
dev_err(dev,
- "requested GPIO %d is out of range [0..%d] for chip %s\n",
- idx, chip->ngpio, chip->label);
+ "requested GPIO %u (%u) is out of range [0..%u] for chip %s\n",
+ idx, p->chip_hwnum, gc->ngpio - 1,
+ gc->label);
return ERR_PTR(-EINVAL);
}
- desc = gpiochip_get_desc(chip, p->chip_hwnum);
+ desc = gpiochip_get_desc(gc, p->chip_hwnum);
*flags = p->flags;
return desc;
@@ -3993,27 +3780,6 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
return desc;
}
-static int dt_gpio_count(struct device *dev, const char *con_id)
-{
- int ret;
- char propname[32];
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
- if (con_id)
- snprintf(propname, sizeof(propname), "%s-%s",
- con_id, gpio_suffixes[i]);
- else
- snprintf(propname, sizeof(propname), "%s",
- gpio_suffixes[i]);
-
- ret = of_gpio_named_count(dev->of_node, propname);
- if (ret > 0)
- break;
- }
- return ret ? ret : -ENOENT;
-}
-
static int platform_gpio_count(struct device *dev, const char *con_id)
{
struct gpiod_lookup_table *table;
@@ -4024,7 +3790,7 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
if (!table)
return -ENOENT;
- for (p = &table->table[0]; p->chip_label; p++) {
+ for (p = &table->table[0]; p->key; p++) {
if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) ||
(!con_id && !p->con_id))
count++;
@@ -4036,6 +3802,54 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
}
/**
+ * fwnode_gpiod_get_index - obtain a GPIO from firmware node
+ * @fwnode: handle of the firmware node
+ * @con_id: function within the GPIO consumer
+ * @index: index of the GPIO to obtain for the consumer
+ * @flags: GPIO initialization flags
+ * @label: label to attach to the requested GPIO
+ *
+ * This function can be used for drivers that get their configuration
+ * from opaque firmware.
+ *
+ * The function properly finds the corresponding GPIO using whatever is the
+ * underlying firmware interface and then makes sure that the GPIO
+ * descriptor is requested before it is returned to the caller.
+ *
+ * Returns:
+ * On successful request the GPIO pin is configured in accordance with
+ * provided @flags.
+ *
+ * In case of error an ERR_PTR() is returned.
+ */
+struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode,
+ const char *con_id, int index,
+ enum gpiod_flags flags,
+ const char *label)
+{
+ struct gpio_desc *desc;
+ char prop_name[32]; /* 32 is max size of property name */
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
+ if (con_id)
+ snprintf(prop_name, sizeof(prop_name), "%s-%s",
+ con_id, gpio_suffixes[i]);
+ else
+ snprintf(prop_name, sizeof(prop_name), "%s",
+ gpio_suffixes[i]);
+
+ desc = fwnode_get_named_gpiod(fwnode, prop_name, index, flags,
+ label);
+ if (!gpiod_not_found(desc))
+ break;
+ }
+
+ return desc;
+}
+EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index);
+
+/**
* gpiod_count - return the number of GPIOs associated with a device / function
* or -ENOENT if no GPIO has been assigned to the requested function
* @dev: GPIO consumer, can be NULL for system-global GPIOs
@@ -4043,11 +3857,12 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
*/
int gpiod_count(struct device *dev, const char *con_id)
{
+ const struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
int count = -ENOENT;
- if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
- count = dt_gpio_count(dev, con_id);
- else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev))
+ if (is_of_node(fwnode))
+ count = of_gpio_get_count(dev, con_id);
+ else if (is_acpi_node(fwnode))
count = acpi_gpio_count(dev, con_id);
if (count < 0)
@@ -4108,7 +3923,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional);
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
unsigned long lflags, enum gpiod_flags dflags)
{
- int status;
+ int ret;
if (lflags & GPIO_ACTIVE_LOW)
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
@@ -4141,24 +3956,24 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
else if (lflags & GPIO_PULL_DOWN)
set_bit(FLAG_PULL_DOWN, &desc->flags);
- status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY));
- if (status < 0)
- return status;
+ ret = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY));
+ if (ret < 0)
+ return ret;
/* No particular flag request, return here... */
if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
- pr_debug("no flags found for %s\n", con_id);
+ gpiod_dbg(desc, "no flags found for %s\n", con_id);
return 0;
}
/* Process flags */
if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)
- status = gpiod_direction_output(desc,
+ ret = gpiod_direction_output(desc,
!!(dflags & GPIOD_FLAGS_BIT_DIR_VAL));
else
- status = gpiod_direction_input(desc);
+ ret = gpiod_direction_input(desc);
- return status;
+ return ret;
}
/**
@@ -4182,28 +3997,27 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
{
unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT;
struct gpio_desc *desc = NULL;
- int status;
+ int ret;
/* Maybe we have a device name, maybe not */
const char *devname = dev ? dev_name(dev) : "?";
+ const struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);
- if (dev) {
- /* Using device tree? */
- if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
- dev_dbg(dev, "using device tree for GPIO lookup\n");
- desc = of_find_gpio(dev, con_id, idx, &lookupflags);
- } else if (ACPI_COMPANION(dev)) {
- dev_dbg(dev, "using ACPI for GPIO lookup\n");
- desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags);
- }
+ /* Using device tree? */
+ if (is_of_node(fwnode)) {
+ dev_dbg(dev, "using device tree for GPIO lookup\n");
+ desc = of_find_gpio(dev, con_id, idx, &lookupflags);
+ } else if (is_acpi_node(fwnode)) {
+ dev_dbg(dev, "using ACPI for GPIO lookup\n");
+ desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags);
}
/*
* Either we are not using DT or ACPI, or their lookup did not return
* a result. In that case, use platform lookup as a fallback.
*/
- if (!desc || desc == ERR_PTR(-ENOENT)) {
+ if (!desc || gpiod_not_found(desc)) {
dev_dbg(dev, "using lookup tables for GPIO lookup\n");
desc = gpiod_find(dev, con_id, idx, &lookupflags);
}
@@ -4217,104 +4031,36 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
* If a connection label was passed use that, else attempt to use
* the device name as label
*/
- status = gpiod_request(desc, con_id ? con_id : devname);
- if (status < 0) {
- if (status == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) {
- /*
- * This happens when there are several consumers for
- * the same GPIO line: we just return here without
- * further initialization. It is a bit if a hack.
- * This is necessary to support fixed regulators.
- *
- * FIXME: Make this more sane and safe.
- */
- dev_info(dev, "nonexclusive access to GPIO for %s\n",
- con_id ? con_id : devname);
- return desc;
- } else {
- return ERR_PTR(status);
- }
- }
-
- status = gpiod_configure_flags(desc, con_id, lookupflags, flags);
- if (status < 0) {
- dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
- gpiod_put(desc);
- return ERR_PTR(status);
- }
-
- return desc;
-}
-EXPORT_SYMBOL_GPL(gpiod_get_index);
-
-/**
- * gpiod_get_from_of_node() - obtain a GPIO from an OF node
- * @node: handle of the OF node
- * @propname: name of the DT property representing the GPIO
- * @index: index of the GPIO to obtain for the consumer
- * @dflags: GPIO initialization flags
- * @label: label to attach to the requested GPIO
- *
- * Returns:
- * On successful request the GPIO pin is configured in accordance with
- * provided @dflags.
- *
- * In case of error an ERR_PTR() is returned.
- */
-struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
- const char *propname, int index,
- enum gpiod_flags dflags,
- const char *label)
-{
- unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- struct gpio_desc *desc;
- enum of_gpio_flags flags;
- bool active_low = false;
- bool single_ended = false;
- bool open_drain = false;
- bool transitory = false;
- int ret;
-
- desc = of_get_named_gpiod_flags(node, propname,
- index, &flags);
-
- if (!desc || IS_ERR(desc)) {
- return desc;
- }
-
- active_low = flags & OF_GPIO_ACTIVE_LOW;
- single_ended = flags & OF_GPIO_SINGLE_ENDED;
- open_drain = flags & OF_GPIO_OPEN_DRAIN;
- transitory = flags & OF_GPIO_TRANSITORY;
+ ret = gpiod_request(desc, con_id ?: devname);
+ if (ret) {
+ if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
+ return ERR_PTR(ret);
- ret = gpiod_request(desc, label);
- if (ret == -EBUSY && (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
+ /*
+ * This happens when there are several consumers for
+ * the same GPIO line: we just return here without
+ * further initialization. It is a bit of a hack.
+ * This is necessary to support fixed regulators.
+ *
+ * FIXME: Make this more sane and safe.
+ */
+ dev_info(dev, "nonexclusive access to GPIO for %s\n", con_id ?: devname);
return desc;
- if (ret)
- return ERR_PTR(ret);
-
- if (active_low)
- lflags |= GPIO_ACTIVE_LOW;
-
- if (single_ended) {
- if (open_drain)
- lflags |= GPIO_OPEN_DRAIN;
- else
- lflags |= GPIO_OPEN_SOURCE;
}
- if (transitory)
- lflags |= GPIO_TRANSITORY;
-
- ret = gpiod_configure_flags(desc, propname, lflags, dflags);
+ ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
if (ret < 0) {
+ dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
gpiod_put(desc);
return ERR_PTR(ret);
}
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIOLINE_CHANGED_REQUESTED, desc);
+
return desc;
}
-EXPORT_SYMBOL(gpiod_get_from_of_node);
+EXPORT_SYMBOL_GPL(gpiod_get_index);
/**
* fwnode_get_named_gpiod - obtain a GPIO from firmware node
@@ -4346,9 +4092,6 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
struct gpio_desc *desc = ERR_PTR(-ENODEV);
int ret;
- if (!fwnode)
- return ERR_PTR(-EINVAL);
-
if (is_of_node(fwnode)) {
desc = gpiod_get_from_of_node(to_of_node(fwnode),
propname, index,
@@ -4364,7 +4107,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
acpi_gpio_update_gpiod_flags(&dflags, &info);
acpi_gpio_update_gpiod_lookup_flags(&lflags, &info);
- }
+ } else
+ return ERR_PTR(-EINVAL);
/* Currently only ACPI takes this path */
ret = gpiod_request(desc, label);
@@ -4377,6 +4121,9 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
return ERR_PTR(ret);
}
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIOLINE_CHANGED_REQUESTED, desc);
+
return desc;
}
EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
@@ -4401,10 +4148,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev,
struct gpio_desc *desc;
desc = gpiod_get_index(dev, con_id, index, flags);
- if (IS_ERR(desc)) {
- if (PTR_ERR(desc) == -ENOENT)
- return NULL;
- }
+ if (gpiod_not_found(desc))
+ return NULL;
return desc;
}
@@ -4421,47 +4166,44 @@ EXPORT_SYMBOL_GPL(gpiod_get_index_optional);
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags)
{
- struct gpio_chip *chip;
+ struct gpio_chip *gc;
struct gpio_desc *local_desc;
int hwnum;
- int status;
+ int ret;
- chip = gpiod_to_chip(desc);
+ gc = gpiod_to_chip(desc);
hwnum = gpio_chip_hwgpio(desc);
- local_desc = gpiochip_request_own_desc(chip, hwnum, name,
+ local_desc = gpiochip_request_own_desc(gc, hwnum, name,
lflags, dflags);
if (IS_ERR(local_desc)) {
- status = PTR_ERR(local_desc);
+ ret = PTR_ERR(local_desc);
pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n",
- name, chip->label, hwnum, status);
- return status;
+ name, gc->label, hwnum, ret);
+ return ret;
}
/* Mark GPIO as hogged so it can be identified and removed later */
set_bit(FLAG_IS_HOGGED, &desc->flags);
- pr_info("GPIO line %d (%s) hogged as %s%s\n",
- desc_to_gpio(desc), name,
- (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input",
- (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ?
- (dflags&GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low":"");
+ gpiod_info(desc, "hogged as %s%s\n",
+ (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input",
+ (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ?
+ (dflags & GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low" : "");
return 0;
}
/**
* gpiochip_free_hogs - Scan gpio-controller chip and release GPIO hog
- * @chip: gpio chip to act on
+ * @gc: gpio chip to act on
*/
-static void gpiochip_free_hogs(struct gpio_chip *chip)
+static void gpiochip_free_hogs(struct gpio_chip *gc)
{
- int id;
+ struct gpio_desc *desc;
- for (id = 0; id < chip->ngpio; id++) {
- if (test_bit(FLAG_IS_HOGGED, &chip->gpiodev->descs[id].flags))
- gpiochip_free_own_desc(&chip->gpiodev->descs[id]);
- }
+ for_each_gpio_desc_with_flag(gc, desc, FLAG_IS_HOGGED)
+ gpiochip_free_own_desc(desc);
}
/**
@@ -4483,7 +4225,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
struct gpio_desc *desc;
struct gpio_descs *descs;
struct gpio_array *array_info = NULL;
- struct gpio_chip *chip;
+ struct gpio_chip *gc;
int count, bitmap_size;
count = gpiod_count(dev, con_id);
@@ -4503,7 +4245,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
descs->desc[descs->ndescs] = desc;
- chip = gpiod_to_chip(desc);
+ gc = gpiod_to_chip(desc);
/*
* If pin hardware number of array member 0 is also 0, select
* its chip as a candidate for fast bitmap processing path.
@@ -4511,8 +4253,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) {
struct gpio_descs *array;
- bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
- chip->ngpio : count);
+ bitmap_size = BITS_TO_LONGS(gc->ngpio > count ?
+ gc->ngpio : count);
array = kzalloc(struct_size(descs, desc, count) +
struct_size(array_info, invert_mask,
@@ -4535,7 +4277,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
array_info->desc = descs->desc;
array_info->size = count;
- array_info->chip = chip;
+ array_info->chip = gc;
bitmap_set(array_info->get_mask, descs->ndescs,
count - descs->ndescs);
bitmap_set(array_info->set_mask, descs->ndescs,
@@ -4543,7 +4285,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
descs->info = array_info;
}
/* Unmark array members which don't belong to the 'fast' chip */
- if (array_info && array_info->chip != chip) {
+ if (array_info && array_info->chip != gc) {
__clear_bit(descs->ndescs, array_info->get_mask);
__clear_bit(descs->ndescs, array_info->set_mask);
}
@@ -4568,8 +4310,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
}
} else if (array_info) {
/* Exclude open drain or open source from fast output */
- if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
- gpiochip_line_is_open_source(chip, descs->ndescs))
+ if (gpiochip_line_is_open_drain(gc, descs->ndescs) ||
+ gpiochip_line_is_open_source(gc, descs->ndescs))
__clear_bit(descs->ndescs,
array_info->set_mask);
/* Identify 'fast' pins which require invertion */
@@ -4607,7 +4349,7 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev,
struct gpio_descs *descs;
descs = gpiod_get_array(dev, con_id, flags);
- if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT))
+ if (gpiod_not_found(descs))
return NULL;
return descs;
@@ -4642,6 +4384,43 @@ void gpiod_put_array(struct gpio_descs *descs)
}
EXPORT_SYMBOL_GPL(gpiod_put_array);
+
+static int gpio_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+
+ /*
+ * Only match if the fwnode doesn't already have a proper struct device
+ * created for it.
+ */
+ if (fwnode && fwnode->dev != dev)
+ return 0;
+ return 1;
+}
+
+static int gpio_stub_drv_probe(struct device *dev)
+{
+ /*
+ * The DT node of some GPIO chips have a "compatible" property, but
+ * never have a struct device added and probed by a driver to register
+ * the GPIO chip with gpiolib. In such cases, fw_devlink=on will cause
+ * the consumers of the GPIO chip to get probe deferred forever because
+ * they will be waiting for a device associated with the GPIO chip
+ * firmware node to get added and bound to a driver.
+ *
+ * To allow these consumers to probe, we associate the struct
+ * gpio_device of the GPIO chip with the firmware node and then simply
+ * bind it to this stub driver.
+ */
+ return 0;
+}
+
+static struct device_driver gpio_stub_drv = {
+ .name = "gpio_stub_drv",
+ .bus = &gpio_bus_type,
+ .probe = gpio_stub_drv_probe,
+};
+
static int __init gpiolib_dev_init(void)
{
int ret;
@@ -4653,14 +4432,28 @@ static int __init gpiolib_dev_init(void)
return ret;
}
- ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip");
+ ret = driver_register(&gpio_stub_drv);
+ if (ret < 0) {
+ pr_err("gpiolib: could not register GPIO stub driver\n");
+ bus_unregister(&gpio_bus_type);
+ return ret;
+ }
+
+ ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME);
if (ret < 0) {
pr_err("gpiolib: failed to allocate char dev region\n");
+ driver_unregister(&gpio_stub_drv);
bus_unregister(&gpio_bus_type);
- } else {
- gpiolib_initialized = true;
- gpiochip_setup_devs();
+ return ret;
}
+
+ gpiolib_initialized = true;
+ gpiochip_setup_devs();
+
+#if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO)
+ WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier));
+#endif /* CONFIG_OF_DYNAMIC && CONFIG_OF_GPIO */
+
return ret;
}
core_initcall(gpiolib_dev_init);
@@ -4669,34 +4462,32 @@ core_initcall(gpiolib_dev_init);
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
{
- unsigned i;
- struct gpio_chip *chip = gdev->chip;
+ struct gpio_chip *gc = gdev->chip;
+ struct gpio_desc *desc;
unsigned gpio = gdev->base;
- struct gpio_desc *gdesc = &gdev->descs[0];
+ int value;
bool is_out;
bool is_irq;
bool active_low;
- for (i = 0; i < gdev->ngpio; i++, gpio++, gdesc++) {
- if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) {
- if (gdesc->name) {
- seq_printf(s, " gpio-%-3d (%-20.20s)\n",
- gpio, gdesc->name);
- }
- continue;
+ for_each_gpio_desc(gc, desc) {
+ if (test_bit(FLAG_REQUESTED, &desc->flags)) {
+ gpiod_get_direction(desc);
+ is_out = test_bit(FLAG_IS_OUT, &desc->flags);
+ value = gpio_chip_get_value(gc, desc);
+ is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags);
+ active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s%s\n",
+ gpio, desc->name ?: "", desc->label,
+ is_out ? "out" : "in ",
+ value >= 0 ? (value ? "hi" : "lo") : "? ",
+ is_irq ? "IRQ " : "",
+ active_low ? "ACTIVE LOW" : "");
+ } else if (desc->name) {
+ seq_printf(s, " gpio-%-3d (%-20.20s)\n", gpio, desc->name);
}
- gpiod_get_direction(gdesc);
- is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
- is_irq = test_bit(FLAG_USED_AS_IRQ, &gdesc->flags);
- active_low = test_bit(FLAG_ACTIVE_LOW, &gdesc->flags);
- seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s%s",
- gpio, gdesc->name ? gdesc->name : "", gdesc->label,
- is_out ? "out" : "in ",
- chip->get ? (chip->get(chip, i) ? "hi" : "lo") : "? ",
- is_irq ? "IRQ " : "",
- active_low ? "ACTIVE LOW" : "");
- seq_printf(s, "\n");
+ gpio++;
}
}
@@ -4729,7 +4520,7 @@ static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
if (list_is_last(&gdev->list, &gpio_devices))
ret = NULL;
else
- ret = list_entry(gdev->list.next, struct gpio_device, list);
+ ret = list_first_entry(&gdev->list, struct gpio_device, list);
spin_unlock_irqrestore(&gpio_lock, flags);
s->private = "\n";
@@ -4745,10 +4536,10 @@ static void gpiolib_seq_stop(struct seq_file *s, void *v)
static int gpiolib_seq_show(struct seq_file *s, void *v)
{
struct gpio_device *gdev = v;
- struct gpio_chip *chip = gdev->chip;
+ struct gpio_chip *gc = gdev->chip;
struct device *parent;
- if (!chip) {
+ if (!gc) {
seq_printf(s, "%s%s: (dangling chip)", (char *)s->private,
dev_name(&gdev->dev));
return 0;
@@ -4757,50 +4548,37 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private,
dev_name(&gdev->dev),
gdev->base, gdev->base + gdev->ngpio - 1);
- parent = chip->parent;
+ parent = gc->parent;
if (parent)
seq_printf(s, ", parent: %s/%s",
parent->bus ? parent->bus->name : "no-bus",
dev_name(parent));
- if (chip->label)
- seq_printf(s, ", %s", chip->label);
- if (chip->can_sleep)
+ if (gc->label)
+ seq_printf(s, ", %s", gc->label);
+ if (gc->can_sleep)
seq_printf(s, ", can sleep");
seq_printf(s, ":\n");
- if (chip->dbg_show)
- chip->dbg_show(s, chip);
+ if (gc->dbg_show)
+ gc->dbg_show(s, gc);
else
gpiolib_dbg_show(s, gdev);
return 0;
}
-static const struct seq_operations gpiolib_seq_ops = {
+static const struct seq_operations gpiolib_sops = {
.start = gpiolib_seq_start,
.next = gpiolib_seq_next,
.stop = gpiolib_seq_stop,
.show = gpiolib_seq_show,
};
-
-static int gpiolib_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &gpiolib_seq_ops);
-}
-
-static const struct file_operations gpiolib_operations = {
- .owner = THIS_MODULE,
- .open = gpiolib_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
+DEFINE_SEQ_ATTRIBUTE(gpiolib);
static int __init gpiolib_debugfs_init(void)
{
/* /sys/kernel/debug/gpio */
- debugfs_create_file("gpio", S_IFREG | S_IRUGO, NULL, NULL,
- &gpiolib_operations);
+ debugfs_create_file("gpio", 0444, NULL, NULL, &gpiolib_fops);
return 0;
}
subsys_initcall(gpiolib_debugfs_init);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 7c52c2442173..d900ecdbac46 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -16,8 +16,7 @@
#include <linux/module.h>
#include <linux/cdev.h>
-enum of_gpio_flags;
-struct acpi_device;
+#define GPIOCHIP_NAME "gpiochip"
/**
* struct gpio_device - internal state container for GPIO devices
@@ -38,6 +37,9 @@ struct acpi_device;
* or name of the IP component in a System on Chip.
* @data: per-instance data assigned by the driver
* @list: links gpio_device:s together for traversal
+ * @notifier: used to notify subscribers about lines being requested, released
+ * or reconfigured
+ * @pin_ranges: range of pins served by the GPIO driver
*
* This state container holds most of the runtime variable data
* for a GPIO device and can hold references and live on after the
@@ -57,6 +59,7 @@ struct gpio_device {
const char *label;
void *data;
struct list_head list;
+ struct blocking_notifier_head notifier;
#ifdef CONFIG_PINCTRL
/*
@@ -69,126 +72,23 @@ struct gpio_device {
#endif
};
-/**
- * struct acpi_gpio_info - ACPI GPIO specific information
- * @adev: reference to ACPI device which consumes GPIO resource
- * @flags: GPIO initialization flags
- * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
- * @pin_config: pin bias as provided by ACPI
- * @polarity: interrupt polarity as provided by ACPI
- * @triggering: triggering type as provided by ACPI
- * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
- */
-struct acpi_gpio_info {
- struct acpi_device *adev;
- enum gpiod_flags flags;
- bool gpioint;
- int pin_config;
- int polarity;
- int triggering;
- unsigned int quirks;
-};
-
/* gpio suffixes used for ACPI and device tree lookup */
static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
-#ifdef CONFIG_OF_GPIO
-struct gpio_desc *of_find_gpio(struct device *dev,
- const char *con_id,
- unsigned int idx,
- unsigned long *lookupflags);
-struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
- const char *list_name, int index, enum of_gpio_flags *flags);
-int of_gpiochip_add(struct gpio_chip *gc);
-void of_gpiochip_remove(struct gpio_chip *gc);
-#else
-static inline struct gpio_desc *of_find_gpio(struct device *dev,
- const char *con_id,
- unsigned int idx,
- unsigned long *lookupflags)
-{
- return ERR_PTR(-ENOENT);
-}
-static inline struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
- const char *list_name, int index, enum of_gpio_flags *flags)
-{
- return ERR_PTR(-ENOENT);
-}
-static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; }
-static inline void of_gpiochip_remove(struct gpio_chip *gc) { }
-#endif /* CONFIG_OF_GPIO */
-
-#ifdef CONFIG_ACPI
-void acpi_gpiochip_add(struct gpio_chip *chip);
-void acpi_gpiochip_remove(struct gpio_chip *chip);
-
-void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
-void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
-
-int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
- struct acpi_gpio_info *info);
-int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
- struct acpi_gpio_info *info);
-
-struct gpio_desc *acpi_find_gpio(struct device *dev,
- const char *con_id,
- unsigned int idx,
- enum gpiod_flags *dflags,
- unsigned long *lookupflags);
-struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
- const char *propname, int index,
- struct acpi_gpio_info *info);
-
-int acpi_gpio_count(struct device *dev, const char *con_id);
-
-bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
-#else
-static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
-static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
-
-static inline void
-acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
-
-static inline void
-acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
-
-static inline int
-acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info)
-{
- return 0;
-}
-static inline int
-acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
- struct acpi_gpio_info *info)
-{
- return 0;
-}
-
-static inline struct gpio_desc *
-acpi_find_gpio(struct device *dev, const char *con_id,
- unsigned int idx, enum gpiod_flags *dflags,
- unsigned long *lookupflags)
-{
- return ERR_PTR(-ENOENT);
-}
-static inline struct gpio_desc *
-acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname,
- int index, struct acpi_gpio_info *info)
-{
- return ERR_PTR(-ENXIO);
-}
-static inline int acpi_gpio_count(struct device *dev, const char *con_id)
-{
- return -ENODEV;
-}
-
-static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
- const char *con_id)
-{
- return false;
-}
-#endif
-
+/**
+ * struct gpio_array - Opaque descriptor for a structure of GPIO array attributes
+ *
+ * @desc: Array of pointers to the GPIO descriptors
+ * @size: Number of elements in desc
+ * @chip: Parent GPIO chip
+ * @get_mask: Get mask used in fastpath
+ * @set_mask: Set mask used in fastpath
+ * @invert_mask: Invert mask used in fastpath
+ *
+ * This structure is attached to struct gpiod_descs obtained from
+ * gpiod_get_array() and can be passed back to get/set array functions in order
+ * to activate fast processing path if applicable.
+ */
struct gpio_array {
struct gpio_desc **desc;
unsigned int size;
@@ -198,7 +98,17 @@ struct gpio_array {
unsigned long invert_mask[];
};
-struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
+struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
+
+#define for_each_gpio_desc(gc, desc) \
+ for (unsigned int __i = 0; \
+ __i < gc->ngpio && (desc = gpiochip_get_desc(gc, __i)); \
+ __i++) \
+
+#define for_each_gpio_desc_with_flag(gc, desc, flag) \
+ for_each_gpio_desc(gc, desc) \
+ if (!test_bit(flag, &desc->flags)) {} else
+
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
@@ -213,6 +123,23 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
extern spinlock_t gpio_lock;
extern struct list_head gpio_devices;
+
+/**
+ * struct gpio_desc - Opaque descriptor for a GPIO
+ *
+ * @gdev: Pointer to the parent GPIO device
+ * @flags: Binary descriptor flags
+ * @label: Name of the consumer
+ * @name: Line name
+ * @hog: Pointer to the device node that hogs this line (if any)
+ * @debounce_period_us: Debounce period in microseconds
+ *
+ * These are obtained using gpiod_get() and are preferable to the old
+ * integer-based handles.
+ *
+ * Contrary to integers, a pointer to a &struct gpio_desc is guaranteed to be
+ * valid until the GPIO is released.
+ */
struct gpio_desc {
struct gpio_device *gdev;
unsigned long flags;
@@ -230,17 +157,44 @@ struct gpio_desc {
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
+#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
+#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
+#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
+#define FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */
+#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
/* Connection label */
const char *label;
/* Name of the GPIO */
const char *name;
+#ifdef CONFIG_OF_DYNAMIC
+ struct device_node *hog;
+#endif
+#ifdef CONFIG_GPIO_CDEV
+ /* debounce period in microseconds */
+ unsigned int debounce_period_us;
+#endif
};
+#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
+
int gpiod_request(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc);
+
+static inline int gpiod_request_user(struct gpio_desc *desc, const char *label)
+{
+ int ret;
+
+ ret = gpiod_request(desc, label);
+ if (ret == -EPROBE_DEFER)
+ ret = -ENODEV;
+
+ return ret;
+}
+
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
unsigned long lflags, enum gpiod_flags dflags);
+int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags);
@@ -275,35 +229,17 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
/* With chip prefix */
-#define chip_emerg(chip, fmt, ...) \
- dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
-#define chip_crit(chip, fmt, ...) \
- dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
-#define chip_err(chip, fmt, ...) \
- dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
-#define chip_warn(chip, fmt, ...) \
- dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
-#define chip_info(chip, fmt, ...) \
- dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
-#define chip_dbg(chip, fmt, ...) \
- dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
-
-#ifdef CONFIG_GPIO_SYSFS
-
-int gpiochip_sysfs_register(struct gpio_device *gdev);
-void gpiochip_sysfs_unregister(struct gpio_device *gdev);
-
-#else
-
-static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
-{
- return 0;
-}
-
-static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
-{
-}
-
-#endif /* CONFIG_GPIO_SYSFS */
+#define chip_emerg(gc, fmt, ...) \
+ dev_emerg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
+#define chip_crit(gc, fmt, ...) \
+ dev_crit(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
+#define chip_err(gc, fmt, ...) \
+ dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
+#define chip_warn(gc, fmt, ...) \
+ dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
+#define chip_info(gc, fmt, ...) \
+ dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
+#define chip_dbg(gc, fmt, ...) \
+ dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#endif /* GPIOLIB_H */