summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/gpio
Initial import
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig991
-rw-r--r--drivers/gpio/Makefile114
-rw-r--r--drivers/gpio/devres.c392
-rw-r--r--drivers/gpio/gpio-74x164.c197
-rw-r--r--drivers/gpio/gpio-74xx-mmio.c170
-rw-r--r--drivers/gpio/gpio-adnp.c561
-rw-r--r--drivers/gpio/gpio-adp5520.c190
-rw-r--r--drivers/gpio/gpio-adp5588.c497
-rw-r--r--drivers/gpio/gpio-altera.c374
-rw-r--r--drivers/gpio/gpio-amd8111.c252
-rw-r--r--drivers/gpio/gpio-arizona.c168
-rw-r--r--drivers/gpio/gpio-bcm-kona.c680
-rw-r--r--drivers/gpio/gpio-bt8xx.c323
-rw-r--r--drivers/gpio/gpio-clps711x.c100
-rw-r--r--drivers/gpio/gpio-crystalcove.c395
-rw-r--r--drivers/gpio/gpio-cs5535.c380
-rw-r--r--drivers/gpio/gpio-da9052.c257
-rw-r--r--drivers/gpio/gpio-da9055.c204
-rw-r--r--drivers/gpio/gpio-davinci.c629
-rw-r--r--drivers/gpio/gpio-dln2.c532
-rw-r--r--drivers/gpio/gpio-dwapb.c692
-rw-r--r--drivers/gpio/gpio-em.c443
-rw-r--r--drivers/gpio/gpio-ep93xx.c390
-rw-r--r--drivers/gpio/gpio-f7188x.c494
-rw-r--r--drivers/gpio/gpio-ge.c114
-rw-r--r--drivers/gpio/gpio-generic.c663
-rw-r--r--drivers/gpio/gpio-grgpio.c504
-rw-r--r--drivers/gpio/gpio-ich.c546
-rw-r--r--drivers/gpio/gpio-intel-mid.c453
-rw-r--r--drivers/gpio/gpio-iop.c131
-rw-r--r--drivers/gpio/gpio-it8761e.c230
-rw-r--r--drivers/gpio/gpio-janz-ttl.c216
-rw-r--r--drivers/gpio/gpio-kempld.c219
-rw-r--r--drivers/gpio/gpio-ks8695.c317
-rw-r--r--drivers/gpio/gpio-loongson.c115
-rw-r--r--drivers/gpio/gpio-lp3943.c242
-rw-r--r--drivers/gpio/gpio-lpc32xx.c577
-rw-r--r--drivers/gpio/gpio-lynxpoint.c474
-rw-r--r--drivers/gpio/gpio-max7300.c89
-rw-r--r--drivers/gpio/gpio-max7301.c114
-rw-r--r--drivers/gpio/gpio-max730x.c246
-rw-r--r--drivers/gpio/gpio-max732x.c764
-rw-r--r--drivers/gpio/gpio-mb86s7x.c237
-rw-r--r--drivers/gpio/gpio-mc33880.c189
-rw-r--r--drivers/gpio/gpio-mc9s08dz60.c147
-rw-r--r--drivers/gpio/gpio-mcp23s08.c1083
-rw-r--r--drivers/gpio/gpio-ml-ioh.c613
-rw-r--r--drivers/gpio/gpio-mm-lantiq.c163
-rw-r--r--drivers/gpio/gpio-moxart.c114
-rw-r--r--drivers/gpio/gpio-mpc5200.c388
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c434
-rw-r--r--drivers/gpio/gpio-msic.c337
-rw-r--r--drivers/gpio/gpio-msm-v2.c462
-rw-r--r--drivers/gpio/gpio-mvebu.c863
-rw-r--r--drivers/gpio/gpio-mxc.c515
-rw-r--r--drivers/gpio/gpio-mxs.c370
-rw-r--r--drivers/gpio/gpio-octeon.c157
-rw-r--r--drivers/gpio/gpio-omap.c1617
-rw-r--r--drivers/gpio/gpio-palmas.c240
-rw-r--r--drivers/gpio/gpio-pca953x.c784
-rw-r--r--drivers/gpio/gpio-pcf857x.c442
-rw-r--r--drivers/gpio/gpio-pch.c541
-rw-r--r--drivers/gpio/gpio-pl061.c408
-rw-r--r--drivers/gpio/gpio-pxa.c747
-rw-r--r--drivers/gpio/gpio-rc5t583.c178
-rw-r--r--drivers/gpio/gpio-rcar.c519
-rw-r--r--drivers/gpio/gpio-rdc321x.c221
-rw-r--r--drivers/gpio/gpio-sa1100.c267
-rw-r--r--drivers/gpio/gpio-samsung.c1328
-rw-r--r--drivers/gpio/gpio-sch.c244
-rw-r--r--drivers/gpio/gpio-sch311x.c438
-rw-r--r--drivers/gpio/gpio-sodaville.c291
-rw-r--r--drivers/gpio/gpio-spear-spics.c207
-rw-r--r--drivers/gpio/gpio-sta2x11.c439
-rw-r--r--drivers/gpio/gpio-stmpe.c454
-rw-r--r--drivers/gpio/gpio-stp-xway.c295
-rw-r--r--drivers/gpio/gpio-sx150x.c709
-rw-r--r--drivers/gpio/gpio-syscon.c265
-rw-r--r--drivers/gpio/gpio-tb10x.c320
-rw-r--r--drivers/gpio/gpio-tc3589x.c337
-rw-r--r--drivers/gpio/gpio-tegra.c590
-rw-r--r--drivers/gpio/gpio-timberdale.c346
-rw-r--r--drivers/gpio/gpio-tps6586x.c166
-rw-r--r--drivers/gpio/gpio-tps65910.c220
-rw-r--r--drivers/gpio/gpio-tps65912.c153
-rw-r--r--drivers/gpio/gpio-ts5500.c465
-rw-r--r--drivers/gpio/gpio-twl4030.c628
-rw-r--r--drivers/gpio/gpio-twl6040.c133
-rw-r--r--drivers/gpio/gpio-tz1090-pdc.c242
-rw-r--r--drivers/gpio/gpio-tz1090.c605
-rw-r--r--drivers/gpio/gpio-ucb1400.c108
-rw-r--r--drivers/gpio/gpio-vf610.c294
-rw-r--r--drivers/gpio/gpio-viperboard.c510
-rw-r--r--drivers/gpio/gpio-vr41xx.c597
-rw-r--r--drivers/gpio/gpio-vx855.c287
-rw-r--r--drivers/gpio/gpio-wm831x.c308
-rw-r--r--drivers/gpio/gpio-wm8350.c174
-rw-r--r--drivers/gpio/gpio-wm8994.c314
-rw-r--r--drivers/gpio/gpio-xgene-sb.c160
-rw-r--r--drivers/gpio/gpio-xgene.c243
-rw-r--r--drivers/gpio/gpio-xilinx.c356
-rw-r--r--drivers/gpio/gpio-xtensa.c178
-rw-r--r--drivers/gpio/gpio-zevio.c235
-rw-r--r--drivers/gpio/gpio-zynq.c727
-rw-r--r--drivers/gpio/gpiolib-acpi.c808
-rw-r--r--drivers/gpio/gpiolib-legacy.c112
-rw-r--r--drivers/gpio/gpiolib-of.c446
-rw-r--r--drivers/gpio/gpiolib-sysfs.c859
-rw-r--r--drivers/gpio/gpiolib.c2377
-rw-r--r--drivers/gpio/gpiolib.h170
110 files changed, 45613 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
new file mode 100644
index 000000000..caefe806d
--- /dev/null
+++ b/drivers/gpio/Kconfig
@@ -0,0 +1,991 @@
+#
+# GPIO infrastructure and drivers
+#
+
+config ARCH_HAVE_CUSTOM_GPIO_H
+ bool
+ help
+ Selecting this config option from the architecture Kconfig allows
+ the architecture to provide a custom asm/gpio.h implementation
+ overriding the default implementations. New uses of this are
+ strongly discouraged.
+
+config ARCH_WANT_OPTIONAL_GPIOLIB
+ bool
+ help
+ Select this config option from the architecture Kconfig, if
+ it is possible to use gpiolib on the architecture, but let the
+ user decide whether to actually build it or not.
+ Select this instead of ARCH_REQUIRE_GPIOLIB, if your architecture does
+ not depend on GPIOs being available, but rather let the user
+ decide whether he needs it or not.
+
+config ARCH_REQUIRE_GPIOLIB
+ bool
+ select GPIOLIB
+ help
+ Platforms select gpiolib if they use this infrastructure
+ for all their GPIOs, usually starting with ones integrated
+ into SOC processors.
+ Selecting this from the architecture code will cause the gpiolib
+ code to always get built in.
+
+
+menuconfig GPIOLIB
+ bool "GPIO Support"
+ depends on ARCH_WANT_OPTIONAL_GPIOLIB || ARCH_REQUIRE_GPIOLIB
+ help
+ This enables GPIO support through the generic GPIO library.
+ You only need to enable this, if you also want to enable
+ one or more of the GPIO drivers below.
+
+ If unsure, say N.
+
+if GPIOLIB
+
+config GPIO_DEVRES
+ def_bool y
+ depends on HAS_IOMEM
+
+config OF_GPIO
+ def_bool y
+ depends on OF
+
+config GPIO_ACPI
+ def_bool y
+ depends on ACPI
+
+config GPIOLIB_IRQCHIP
+ select IRQ_DOMAIN
+ bool
+
+config DEBUG_GPIO
+ bool "Debug GPIO calls"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here to add some extra checks and diagnostics to GPIO calls.
+ These checks help ensure that GPIOs have been properly initialized
+ before they are used, and that sleeping calls are not made from
+ non-sleeping contexts. They can make bitbanged serial protocols
+ slower. The diagnostics help catch the type of setup errors
+ that are most common when setting up new platforms or boards.
+
+config GPIO_SYSFS
+ bool "/sys/class/gpio/... (sysfs interface)"
+ depends on SYSFS
+ help
+ Say Y here to add a sysfs interface for GPIOs.
+
+ This is mostly useful to work around omissions in a system's
+ kernel support. Those are common in custom and semicustom
+ hardware assembled using standard kernels with a minimum of
+ custom patches. In those cases, userspace code may import
+ a given GPIO from the kernel, if no kernel driver requested it.
+
+ Kernel drivers may also request that a particular GPIO be
+ exported to userspace; this can be useful when debugging.
+
+config GPIO_GENERIC
+ tristate
+
+# put drivers in the right section, in alphabetical order
+
+# This symbol is selected by both I2C and SPI expanders
+config GPIO_MAX730X
+ tristate
+
+menu "Memory mapped GPIO drivers"
+
+config GPIO_74XX_MMIO
+ tristate "GPIO driver for 74xx-ICs with MMIO access"
+ depends on OF_GPIO
+ select GPIO_GENERIC
+ help
+ Say yes here to support GPIO functionality for 74xx-compatible ICs
+ with MMIO access. Compatible models include:
+ 1 bit: 741G125 (Input), 741G74 (Output)
+ 2 bits: 742G125 (Input), 7474 (Output)
+ 4 bits: 74125 (Input), 74175 (Output)
+ 6 bits: 74365 (Input), 74174 (Output)
+ 8 bits: 74244 (Input), 74273 (Output)
+ 16 bits: 741624 (Input), 7416374 (Output)
+
+config GPIO_ALTERA
+ tristate "Altera GPIO"
+ depends on OF_GPIO
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ Say Y or M here to build support for the Altera PIO device.
+
+ If driver is built as a module it will be called gpio-altera.
+
+config GPIO_BCM_KONA
+ bool "Broadcom Kona GPIO"
+ depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
+ help
+ Turn on GPIO support for Broadcom "Kona" chips.
+
+config GPIO_CLPS711X
+ tristate "CLPS711X GPIO support"
+ depends on ARCH_CLPS711X || COMPILE_TEST
+ select GPIO_GENERIC
+ help
+ Say yes here to support GPIO on CLPS711X SoCs.
+
+config GPIO_DAVINCI
+ bool "TI Davinci/Keystone GPIO support"
+ default y if ARCH_DAVINCI
+ depends on ARM && (ARCH_DAVINCI || ARCH_KEYSTONE)
+ help
+ Say yes here to enable GPIO support for TI Davinci/Keystone SoCs.
+
+config GPIO_DWAPB
+ tristate "Synopsys DesignWare APB GPIO driver"
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
+ help
+ Say Y or M here to build support for the Synopsys DesignWare APB
+ GPIO block.
+
+config GPIO_EM
+ tristate "Emma Mobile GPIO"
+ depends on ARM && OF_GPIO
+ help
+ Say yes here to support GPIO on Renesas Emma Mobile SoCs.
+
+config GPIO_EP93XX
+ def_bool y
+ depends on ARCH_EP93XX
+ select GPIO_GENERIC
+
+config GPIO_F7188X
+ tristate "F71869, F71869A, F71882FG and F71889F GPIO support"
+ depends on X86
+ help
+ This option enables support for GPIOs found on Fintek Super-I/O
+ chips F71869, F71869A, F71882FG and F71889F.
+
+ To compile this driver as a module, choose M here: the module will
+ be called f7188x-gpio.
+
+config GPIO_GE_FPGA
+ bool "GE FPGA based GPIO"
+ depends on GE_FPGA
+ select GPIO_GENERIC
+ help
+ Support for common GPIO functionality provided on some GE Single Board
+ Computers.
+
+ This driver provides basic support (configure as input or output, read
+ and write pin state) for GPIO implemented in a number of GE single
+ board computers.
+
+config GPIO_GENERIC_PLATFORM
+ tristate "Generic memory-mapped GPIO controller support (MMIO platform device)"
+ select GPIO_GENERIC
+ help
+ Say yes here to support basic platform_device memory-mapped GPIO controllers.
+
+config GPIO_GRGPIO
+ tristate "Aeroflex Gaisler GRGPIO support"
+ depends on OF
+ select GPIO_GENERIC
+ select IRQ_DOMAIN
+ help
+ Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
+ VHDL IP core library.
+
+config GPIO_ICH
+ tristate "Intel ICH GPIO"
+ depends on PCI && X86
+ select MFD_CORE
+ select 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
+ Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake).
+
+ If unsure, say N.
+
+config GPIO_IOP
+ tristate "Intel IOP GPIO"
+ depends on ARM && (ARCH_IOP32X || ARCH_IOP33X)
+ help
+ Say yes here to support the GPIO functionality of a number of Intel
+ IOP32X or IOP33X.
+
+ If unsure, say N.
+
+config GPIO_IT8761E
+ tristate "IT8761E GPIO support"
+ depends on X86 # unconditional access to IO space.
+ help
+ Say yes here to support GPIO functionality of IT8761E super I/O chip.
+
+config GPIO_LOONGSON
+ bool "Loongson-2/3 GPIO support"
+ depends on CPU_LOONGSON2 || CPU_LOONGSON3
+ help
+ driver for GPIO functionality on Loongson-2F/3A/3B processors.
+
+config GPIO_LYNXPOINT
+ tristate "Intel Lynxpoint GPIO support"
+ depends on ACPI && X86
+ select GPIOLIB_IRQCHIP
+ help
+ driver for GPIO functionality on Intel Lynxpoint PCH chipset
+ Requires ACPI device enumeration code to set up a platform device.
+
+config GPIO_MB86S7X
+ bool "GPIO support for Fujitsu MB86S7x Platforms"
+ depends on ARCH_MB86S7X
+ help
+ Say yes here to support the GPIO controller in Fujitsu MB86S70 SoCs.
+
+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.
+
+config GPIO_MOXART
+ bool "MOXART GPIO support"
+ depends on ARCH_MOXART
+ select GPIO_GENERIC
+ help
+ Select this option to enable GPIO driver for
+ MOXA ART SoC devices.
+
+config GPIO_MPC5200
+ def_bool y
+ depends on PPC_MPC52xx
+
+config GPIO_MPC8XXX
+ bool "MPC512x/MPC8xxx GPIO support"
+ depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \
+ FSL_SOC_BOOKE || PPC_86xx
+ help
+ Say Y here if you're going to use hardware that connects to the
+ MPC512x/831x/834x/837x/8572/8610 GPIOs.
+
+config GPIO_MSM_V2
+ tristate "Qualcomm MSM GPIO v2"
+ depends on GPIOLIB && OF && ARCH_QCOM
+ help
+ Say yes here to support the GPIO interface on ARM v7 based
+ Qualcomm MSM chips. Most of the pins on the MSM can be
+ selected for GPIO, and are controlled by this driver.
+
+config GPIO_MVEBU
+ def_bool y
+ depends on PLAT_ORION
+ depends on OF
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
+
+config GPIO_MXC
+ def_bool y
+ depends on ARCH_MXC
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
+
+config GPIO_MXS
+ def_bool y
+ depends on ARCH_MXS
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
+
+config GPIO_OCTEON
+ tristate "Cavium OCTEON GPIO"
+ depends on GPIOLIB && CAVIUM_OCTEON_SOC
+ default y
+ help
+ Say yes here to support the on-chip GPIO lines on the OCTEON
+ family of SOCs.
+
+config GPIO_OMAP
+ bool "TI OMAP GPIO support" if COMPILE_TEST && !ARCH_OMAP2PLUS
+ default y if ARCH_OMAP
+ depends on ARM
+ select GENERIC_IRQ_CHIP
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to enable GPIO support for TI OMAP SoCs.
+
+config GPIO_PL061
+ bool "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
+
+config GPIO_PXA
+ bool "PXA GPIO support"
+ depends on ARCH_PXA || ARCH_MMP
+ help
+ Say yes here to support the PXA GPIO device
+
+config GPIO_RCAR
+ tristate "Renesas R-Car GPIO"
+ depends on ARM && (ARCH_SHMOBILE || COMPILE_TEST)
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to support GPIO on Renesas R-Car SoCs.
+
+config GPIO_SAMSUNG
+ bool
+ depends on PLAT_SAMSUNG
+ help
+ Legacy GPIO support. Use only for platforms without support for
+ pinctrl.
+
+config GPIO_SCH
+ tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO"
+ depends on PCI && X86
+ select MFD_CORE
+ select LPC_SCH
+ help
+ Say yes here to support GPIO interface on Intel Poulsbo SCH,
+ Intel Tunnel Creek processor, Intel Centerton processor or
+ Intel Quark X1000 SoC.
+
+ The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
+ 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
+ system from the Suspend-to-RAM state.
+
+ The Intel Tunnel Creek processor has 5 GPIOs powered by the
+ core power rail and 9 from suspend power supply.
+
+ The Intel Centerton processor has a total of 30 GPIO pins.
+ Twenty-one are powered by the core power rail and 9 from the
+ suspend power supply.
+
+ The Intel Quark X1000 SoC has 2 GPIOs powered by the core
+ power well and 6 from the suspend power well.
+
+config GPIO_SCH311X
+ tristate "SMSC SCH311x SuperI/O GPIO"
+ help
+ Driver to enable the GPIOs found on SMSC SMSC SCH3112, SCH3114 and
+ SCH3116 "Super I/O" chipsets.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-sch311x.
+
+config GPIO_SPEAR_SPICS
+ bool "ST SPEAr13xx SPI Chip Select as GPIO support"
+ depends on PLAT_SPEAR
+ select GENERIC_IRQ_CHIP
+ help
+ Say yes here to support ST SPEAr SPI Chip Select as GPIO device
+
+config GPIO_STA2X11
+ bool "STA2x11/ConneXt GPIO support"
+ depends on MFD_STA2X11
+ select GENERIC_IRQ_CHIP
+ help
+ Say yes here to support the STA2x11/ConneXt GPIO device.
+ The GPIO module has 128 GPIO pins with alternate functions.
+
+config GPIO_STP_XWAY
+ bool "XWAY STP GPIOs"
+ depends on SOC_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
+ phy status.
+
+config GPIO_SYSCON
+ tristate "GPIO based on SYSCON"
+ depends on MFD_SYSCON && OF
+ help
+ Say yes here to support GPIO functionality though SYSCON driver.
+
+config GPIO_TB10X
+ bool
+ select GENERIC_IRQ_CHIP
+ select OF_GPIO
+
+config GPIO_TS5500
+ tristate "TS-5500 DIO blocks and compatibles"
+ depends on TS5500 || COMPILE_TEST
+ help
+ This driver supports Digital I/O exposed by pin blocks found on some
+ Technologic Systems platforms. It includes, but is not limited to, 3
+ blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
+ LCD port.
+
+config GPIO_TZ1090
+ bool "Toumaz Xenif TZ1090 GPIO support"
+ depends on SOC_TZ1090
+ select GENERIC_IRQ_CHIP
+ default y
+ help
+ Say yes here to support Toumaz Xenif TZ1090 GPIOs.
+
+config GPIO_TZ1090_PDC
+ bool "Toumaz Xenif TZ1090 PDC GPIO support"
+ depends on SOC_TZ1090
+ default y
+ help
+ Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
+
+config GPIO_VF610
+ def_bool y
+ depends on ARCH_MXC && SOC_VF610
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to support Vybrid vf610 GPIOs.
+
+config GPIO_VR41XX
+ tristate "NEC VR4100 series General-purpose I/O Uint support"
+ depends on CPU_VR41XX
+ help
+ Say yes here to support the NEC VR4100 series General-purpose I/O Uint
+
+config GPIO_VX855
+ tristate "VIA VX855/VX875 GPIO"
+ depends on PCI
+ select MFD_CORE
+ select MFD_VX855
+ help
+ 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
+ functionality of the device.
+
+config GPIO_XGENE
+ bool "APM X-Gene GPIO controller support"
+ depends on ARM64 && OF_GPIO
+ help
+ This driver is to support the GPIO block within the APM X-Gene SoC
+ platform's generic flash controller. The GPIO pins are muxed with
+ the generic flash controller's address and data pins. Say yes
+ here to enable the GFC GPIO functionality.
+
+config GPIO_XGENE_SB
+ tristate "APM X-Gene GPIO standby controller support"
+ depends on ARCH_XGENE && OF_GPIO
+ select GPIO_GENERIC
+ help
+ This driver supports the GPIO block within the APM X-Gene
+ Standby Domain. Say yes here to enable the GPIO functionality.
+
+config GPIO_XILINX
+ tristate "Xilinx GPIO support"
+ depends on OF_GPIO && (PPC || MICROBLAZE || ARCH_ZYNQ || X86)
+ help
+ Say yes here to support the Xilinx FPGA GPIO device
+
+config GPIO_XTENSA
+ bool "Xtensa GPIO32 support"
+ depends on XTENSA
+ depends on HAVE_XTENSA_GPIO32
+ depends on !SMP
+ help
+ Say yes here to support the Xtensa internal GPIO32 IMPWIRE (input)
+ and EXPSTATE (output) ports
+
+config GPIO_ZEVIO
+ bool "LSI ZEVIO SoC memory mapped GPIOs"
+ depends on ARM && OF_GPIO
+ help
+ Say yes here to support the GPIO controller in LSI ZEVIO SoCs.
+
+config GPIO_ZYNQ
+ tristate "Xilinx Zynq GPIO support"
+ depends on ARCH_ZYNQ
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to support Xilinx Zynq GPIO controller.
+
+endmenu
+
+menu "I2C GPIO expanders"
+ depends on I2C
+
+config GPIO_ADP5588
+ tristate "ADP5588 I2C GPIO expander"
+ depends on I2C
+ help
+ This option enables support for 18 GPIOs found
+ on Analog Devices ADP5588 GPIO Expanders.
+
+config GPIO_ADP5588_IRQ
+ bool "Interrupt controller support for ADP5588"
+ depends on GPIO_ADP5588=y
+ help
+ Say yes here to enable the adp5588 to be used as an interrupt
+ controller. It requires the driver to be built in the kernel.
+
+config GPIO_ADNP
+ tristate "Avionic Design N-bit GPIO expander"
+ depends on I2C && OF_GPIO
+ select GPIOLIB_IRQCHIP
+ help
+ This option enables support for N GPIOs found on Avionic Design
+ I2C GPIO expanders. The register space will be extended by powers
+ of two, so the controller will need to accommodate for that. For
+ example: if a controller provides 48 pins, 6 registers will be
+ enough to represent all pins, but the driver will assume a
+ register layout for 64 pins (8 registers).
+
+config GPIO_MAX7300
+ tristate "Maxim MAX7300 GPIO expander"
+ depends on I2C
+ select GPIO_MAX730X
+ help
+ GPIO driver for Maxim MAX7300 I2C-based GPIO expander.
+
+config GPIO_MAX732X
+ tristate "MAX7319, MAX7320-7327 I2C Port Expanders"
+ depends on I2C
+ help
+ Say yes here to support the MAX7319, MAX7320-7327 series of I2C
+ Port Expanders. Each IO port on these chips has a fixed role of
+ Input (designated by 'I'), Push-Pull Output ('O'), or Open-Drain
+ Input and Output (designed by 'P'). The combinations are listed
+ below:
+
+ 8 bits: max7319 (8I), max7320 (8O), max7321 (8P),
+ max7322 (4I4O), max7323 (4P4O)
+
+ 16 bits: max7324 (8I8O), max7325 (8P8O),
+ max7326 (4I12O), max7327 (4P12O)
+
+ Board setup code must specify the model to use, and the start
+ number for these GPIOs.
+
+config GPIO_MAX732X_IRQ
+ bool "Interrupt controller support for MAX732x"
+ depends on GPIO_MAX732X=y
+ select GPIOLIB_IRQCHIP
+ help
+ 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"
+ depends on I2C
+ help
+ Say yes here to provide access to several register-oriented
+ SMBus I/O expanders, made mostly by NXP or TI. Compatible
+ models include:
+
+ 4 bits: pca9536, pca9537
+
+ 8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554,
+ pca9556, pca9557, pca9574, tca6408, xra1202
+
+ 16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575,
+ tca6416
+
+ 24 bits: tca6424
+
+ 40 bits: pca9505, pca9698
+
+config GPIO_PCA953X_IRQ
+ bool "Interrupt controller support for PCA953x"
+ depends on GPIO_PCA953X=y
+ 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.
+
+config GPIO_PCF857X
+ tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
+ depends on I2C
+ select GPIOLIB_IRQCHIP
+ select IRQ_DOMAIN
+ help
+ Say yes here to provide access to most "quasi-bidirectional" I2C
+ GPIO expanders used for additional digital outputs or inputs.
+ Most of these parts are from NXP, though TI is a second source for
+ some of them. Compatible models include:
+
+ 8 bits: pcf8574, pcf8574a, pca8574, pca8574a,
+ pca9670, pca9672, pca9674, pca9674a,
+ max7328, max7329
+
+ 16 bits: pcf8575, pcf8575c, pca8575,
+ pca9671, pca9673, pca9675
+
+ Your board setup code will need to declare the expanders in
+ use, and assign numbers to the GPIOs they expose. Those GPIOs
+ can then be used from drivers and other kernel code, just like
+ other GPIOs, but only accessible from task contexts.
+
+ This driver provides an in-kernel interface to those GPIOs using
+ platform-neutral GPIO calls.
+
+config GPIO_SX150X
+ bool "Semtech SX150x I2C GPIO expander"
+ depends on I2C=y
+ select GPIOLIB_IRQCHIP
+ default n
+ help
+ Say yes here to provide support for Semtech SX150-series I2C
+ GPIO expanders. Compatible models include:
+
+ 8 bits: sx1508q
+ 16 bits: sx1509q
+
+endmenu
+
+menu "MFD GPIO expanders"
+
+config GPIO_ADP5520
+ tristate "GPIO Support for ADP5520 PMIC"
+ depends on PMIC_ADP5520
+ help
+ This option enables support for on-chip GPIO found
+ on Analog Devices ADP5520 PMICs.
+
+config GPIO_ARIZONA
+ tristate "Wolfson Microelectronics Arizona class devices"
+ depends on MFD_ARIZONA
+ help
+ Support for GPIOs on Wolfson Arizona class devices.
+
+config GPIO_CRYSTAL_COVE
+ tristate "GPIO support for Crystal Cove PMIC"
+ depends on INTEL_SOC_PMIC
+ select GPIOLIB_IRQCHIP
+ help
+ Support for GPIO pins on 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
+ called gpio-crystalcove.
+
+config GPIO_CS5535
+ tristate "AMD CS5535/CS5536 GPIO support"
+ depends on MFD_CS5535
+ help
+ The AMD CS5535 and CS5536 southbridges support 28 GPIO pins that
+ can be used for quite a number of things. The CS5535/6 is found on
+ AMD Geode and Lemote Yeeloong devices.
+
+ If unsure, say N.
+
+config GPIO_DA9052
+ tristate "Dialog DA9052 GPIO"
+ depends on PMIC_DA9052
+ help
+ Say yes here to enable the GPIO driver for the DA9052 chip.
+
+config GPIO_DA9055
+ tristate "Dialog Semiconductor DA9055 GPIO"
+ depends on MFD_DA9055
+ help
+ 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.
+
+ If driver is built as a module it will be called gpio-da9055.
+
+config GPIO_DLN2
+ tristate "Diolan DLN2 GPIO support"
+ depends on MFD_DLN2
+ select GPIOLIB_IRQCHIP
+
+ help
+ Select this option to enable GPIO driver for the Diolan DLN2
+ board.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-dln2.
+
+config GPIO_JANZ_TTL
+ tristate "Janz VMOD-TTL Digital IO Module"
+ depends on MFD_JANZ_CMODIO
+ help
+ This enables support for the Janz VMOD-TTL Digital IO module.
+ This driver provides support for driving the pins in output
+ mode only. Input mode is not supported.
+
+config GPIO_KEMPLD
+ tristate "Kontron ETX / COMexpress GPIO"
+ depends on MFD_KEMPLD
+ help
+ This enables support for the PLD GPIO interface on some Kontron ETX
+ and COMexpress (ETXexpress) modules.
+
+ This driver can also be built as a module. If so, the module will be
+ called gpio-kempld.
+
+config GPIO_LP3943
+ tristate "TI/National Semiconductor LP3943 GPIO expander"
+ depends on MFD_LP3943
+ help
+ GPIO driver for LP3943 MFD.
+ LP3943 can be used as a GPIO expander which provides up to 16 GPIOs.
+ Open drain outputs are required for this usage.
+
+config GPIO_MSIC
+ bool "Intel MSIC mixed signal gpio support"
+ depends on 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
+ help
+ Select this option to enable GPIO driver for the TI PALMAS
+ series chip family.
+
+config GPIO_RC5T583
+ bool "RICOH RC5T583 GPIO"
+ depends on MFD_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.
+
+config GPIO_STMPE
+ bool "STMPE GPIOs"
+ depends on MFD_STMPE
+ depends on OF_GPIO
+ select GPIOLIB_IRQCHIP
+ help
+ This enables support for the GPIOs found on the STMPE I/O
+ Expanders.
+
+config GPIO_TC3589X
+ bool "TC3589X GPIOs"
+ depends on MFD_TC3589X
+ depends on OF_GPIO
+ select GPIOLIB_IRQCHIP
+ help
+ This enables support for the GPIOs found on the TC3589X
+ I/O Expander.
+
+config GPIO_TIMBERDALE
+ bool "Support for timberdale GPIO IP"
+ depends on MFD_TIMBERDALE
+ ---help---
+ Add support for the GPIO IP in the timberdale FPGA.
+
+config GPIO_TPS6586X
+ bool "TPS6586X GPIO"
+ depends on MFD_TPS6586X
+ help
+ Select this option to enable GPIO driver for the TPS6586X
+ chip family.
+
+config GPIO_TPS65910
+ bool "TPS65910 GPIO"
+ depends on MFD_TPS65910
+ help
+ Select this option to enable GPIO driver for the TPS65910
+ chip family.
+
+config GPIO_TPS65912
+ tristate "TI TPS65912 GPIO"
+ depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
+ help
+ This driver supports TPS65912 gpio chip
+
+config GPIO_TWL4030
+ tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
+ depends on TWL4030_CORE
+ help
+ Say yes here to access the GPIO signals of various multi-function
+ power management chips from Texas Instruments.
+
+config GPIO_TWL6040
+ tristate "TWL6040 GPO"
+ depends on TWL6040_CORE
+ help
+ Say yes here to access the GPO signals of twl6040
+ audio chip from Texas Instruments.
+
+config GPIO_UCB1400
+ tristate "Philips UCB1400 GPIO"
+ depends on UCB1400_CORE
+ help
+ This enables support for the Philips UCB1400 GPIO pins.
+ The UCB1400 is an AC97 audio codec.
+
+config GPIO_WM831X
+ tristate "WM831x GPIOs"
+ depends on MFD_WM831X
+ help
+ Say yes here to access the GPIO signals of WM831x power management
+ chips from Wolfson Microelectronics.
+
+config GPIO_WM8350
+ tristate "WM8350 GPIOs"
+ depends on MFD_WM8350
+ help
+ Say yes here to access the GPIO signals of WM8350 power management
+ chips from Wolfson Microelectronics.
+
+config GPIO_WM8994
+ tristate "WM8994 GPIOs"
+ depends on MFD_WM8994
+ help
+ Say yes here to access the GPIO signals of WM8994 audio hub
+ CODECs from Wolfson Microelectronics.
+
+endmenu
+
+menu "PCI GPIO expanders"
+ depends on PCI
+
+config GPIO_AMD8111
+ tristate "AMD 8111 GPIO driver"
+ depends on PCI
+ help
+ The AMD 8111 south bridge 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
+ of this driver!
+
+ If unsure, say N
+
+config GPIO_BT8XX
+ tristate "BT8XX GPIO abuser"
+ depends on PCI && VIDEO_BT848=n
+ help
+ The BT8xx frame grabber chip has 24 GPIO pins that can be abused
+ as a cheap PCI GPIO card.
+
+ This chip can be found on Miro, Hauppauge and STB TV-cards.
+
+ 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/bt8xxgpio.txt
+
+ If unsure, say N.
+
+config GPIO_INTEL_MID
+ bool "Intel Mid GPIO support"
+ depends on PCI && X86
+ select GPIOLIB_IRQCHIP
+ help
+ Say Y here to support Intel Mid GPIO.
+
+config GPIO_ML_IOH
+ tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
+ depends on PCI
+ 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 access the IOH's GPIO device.
+
+config GPIO_PCH
+ tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7223/ML7831) GPIO"
+ depends on PCI && (X86_32 || 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 can access PCH GPIO device.
+
+ This driver also can be used for LAPIS Semiconductor IOH(Input/
+ Output Hub), ML7223 and ML7831.
+ 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.
+
+config GPIO_RDC321X
+ tristate "RDC R-321x GPIO support"
+ depends on PCI
+ select MFD_CORE
+ select MFD_RDC321X
+ help
+ Support for the RDC R321x SoC GPIOs over southbridge
+ PCI configuration space.
+
+config GPIO_SODAVILLE
+ bool "Intel Sodaville GPIO support"
+ depends on X86 && PCI && OF
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
+ help
+ Say Y here to support Intel Sodaville GPIO.
+
+endmenu
+
+menu "SPI GPIO expanders"
+ depends on SPI_MASTER
+
+config GPIO_74X164
+ tristate "74x164 serial-in/parallel-out 8-bits shift register"
+ depends on SPI_MASTER && OF
+ 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.
+
+config GPIO_MAX7301
+ tristate "Maxim MAX7301 GPIO expander"
+ depends on SPI_MASTER
+ select GPIO_MAX730X
+ help
+ GPIO driver for Maxim MAX7301 SPI-based GPIO expander.
+
+config GPIO_MCP23S08
+ tristate "Microchip MCP23xxx I/O expander"
+ depends on (SPI_MASTER && !I2C) || I2C
+ help
+ SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
+ I/O expanders.
+ This provides a GPIO interface supporting inputs and outputs.
+ The I2C versions of the chips can be used as interrupt-controller.
+
+config GPIO_MC33880
+ tristate "Freescale MC33880 high-side/low-side switch"
+ depends on SPI_MASTER
+ help
+ SPI driver for Freescale MC33880 high-side/low-side switch.
+ This provides GPIO interface supporting inputs and outputs.
+
+endmenu
+
+menu "USB GPIO expanders"
+ depends on USB
+
+config GPIO_VIPERBOARD
+ tristate "Viperboard GPIO a & b support"
+ depends on MFD_VIPERBOARD && USB
+ help
+ Say yes here to access the GPIO signals of Nano River
+ Technologies Viperboard. There are two GPIO chips on the
+ board: gpioa and gpiob.
+ See viperboard API specification and Nano
+ River Tech's viperboard.h for detailed meaning
+ of the module parameters.
+
+endmenu
+
+endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
new file mode 100644
index 000000000..f71bb9713
--- /dev/null
+++ b/drivers/gpio/Makefile
@@ -0,0 +1,114 @@
+# generic gpio support: platform drivers, dedicated expander chips, etc
+
+ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
+
+obj-$(CONFIG_GPIO_DEVRES) += devres.o
+obj-$(CONFIG_GPIOLIB) += gpiolib.o
+obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o
+obj-$(CONFIG_OF_GPIO) += gpiolib-of.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_GENERIC) += gpio-generic.o
+
+obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
+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_ALTERA) += gpio-altera.o
+obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
+obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
+obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
+obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
+obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
+obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
+obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
+obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
+obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
+obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
+obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o
+obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
+obj-$(CONFIG_GPIO_EM) += gpio-em.o
+obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
+obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
+obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
+obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
+obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
+obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
+obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.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_INTEL_MID) += gpio-intel-mid.o
+obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o
+obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
+obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
+obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
+obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
+obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
+obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o
+obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.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_MCP23S08) += gpio-mcp23s08.o
+obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
+obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o
+obj-$(CONFIG_GPIO_MOXART) += gpio-moxart.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_MSM_V2) += gpio-msm-v2.o
+obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
+obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
+obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
+obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
+obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
+obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
+obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o
+obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
+obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
+obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
+obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
+obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
+obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
+obj-$(CONFIG_GPIO_SAMSUNG) += gpio-samsung.o
+obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
+obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
+obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
+obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
+obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
+obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
+obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
+obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
+obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
+obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
+obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
+obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
+obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
+obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
+obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
+obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
+obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
+obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
+obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
+obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
+obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o
+obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o
+obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
+obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
+obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
+obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
+obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
+obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
+obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
+obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
+obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o
+obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o
+obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
+obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
+obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c
new file mode 100644
index 000000000..07ba82317
--- /dev/null
+++ b/drivers/gpio/devres.c
@@ -0,0 +1,392 @@
+/*
+ * drivers/gpio/devres.c - managed gpio resources
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This file is based on kernel/irq/devres.c
+ *
+ * Copyright (c) 2011 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+static void devm_gpiod_release(struct device *dev, void *res)
+{
+ struct gpio_desc **desc = res;
+
+ gpiod_put(*desc);
+}
+
+static int devm_gpiod_match(struct device *dev, void *res, void *data)
+{
+ struct gpio_desc **this = res, **gpio = data;
+
+ return *this == *gpio;
+}
+
+static void devm_gpiod_release_array(struct device *dev, void *res)
+{
+ struct gpio_descs **descs = res;
+
+ gpiod_put_array(*descs);
+}
+
+static int devm_gpiod_match_array(struct device *dev, void *res, void *data)
+{
+ struct gpio_descs **this = res, **gpios = data;
+
+ return *this == *gpios;
+}
+
+/**
+ * devm_gpiod_get - Resource-managed gpiod_get()
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get(). GPIO descriptors returned from this function are
+ * automatically disposed on driver detach. See gpiod_get() for detailed
+ * information about behavior and return values.
+ */
+struct gpio_desc *__must_check __devm_gpiod_get(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ return devm_gpiod_get_index(dev, con_id, 0, flags);
+}
+EXPORT_SYMBOL(__devm_gpiod_get);
+
+/**
+ * devm_gpiod_get_optional - Resource-managed gpiod_get_optional()
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get_optional(). GPIO descriptors returned from this function
+ * are automatically disposed on driver detach. See gpiod_get_optional() for
+ * detailed information about behavior and return values.
+ */
+struct gpio_desc *__must_check __devm_gpiod_get_optional(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ return devm_gpiod_get_index_optional(dev, con_id, 0, flags);
+}
+EXPORT_SYMBOL(__devm_gpiod_get_optional);
+
+/**
+ * devm_gpiod_get_index - Resource-managed gpiod_get_index()
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @idx: index of the GPIO to obtain in the consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get_index(). GPIO descriptors returned from this function are
+ * automatically disposed on driver detach. See gpiod_get_index() for detailed
+ * information about behavior and return values.
+ */
+struct gpio_desc *__must_check __devm_gpiod_get_index(struct device *dev,
+ const char *con_id,
+ unsigned int idx,
+ enum gpiod_flags flags)
+{
+ struct gpio_desc **dr;
+ struct gpio_desc *desc;
+
+ dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *),
+ GFP_KERNEL);
+ if (!dr)
+ return ERR_PTR(-ENOMEM);
+
+ desc = gpiod_get_index(dev, con_id, idx, flags);
+ if (IS_ERR(desc)) {
+ devres_free(dr);
+ return desc;
+ }
+
+ *dr = desc;
+ devres_add(dev, dr);
+
+ return desc;
+}
+EXPORT_SYMBOL(__devm_gpiod_get_index);
+
+/**
+ * devm_get_gpiod_from_child - get a GPIO descriptor from a device's child node
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @child: firmware node (child of @dev)
+ *
+ * GPIO descriptors returned from this function are automatically disposed on
+ * driver detach.
+ */
+struct gpio_desc *devm_get_gpiod_from_child(struct device *dev,
+ const char *con_id,
+ struct fwnode_handle *child)
+{
+ static const char * const suffixes[] = { "gpios", "gpio" };
+ 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(suffixes); i++) {
+ if (con_id)
+ snprintf(prop_name, sizeof(prop_name), "%s-%s",
+ con_id, suffixes[i]);
+ else
+ snprintf(prop_name, sizeof(prop_name), "%s",
+ suffixes[i]);
+
+ desc = fwnode_get_named_gpiod(child, prop_name);
+ if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
+ break;
+ }
+ if (IS_ERR(desc)) {
+ devres_free(dr);
+ return desc;
+ }
+
+ *dr = desc;
+ devres_add(dev, dr);
+
+ return desc;
+}
+EXPORT_SYMBOL(devm_get_gpiod_from_child);
+
+/**
+ * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional()
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @index: index of the GPIO to obtain in the consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get_index_optional(). GPIO descriptors returned from this
+ * function are automatically disposed on driver detach. See
+ * gpiod_get_index_optional() for detailed information about behavior and
+ * return values.
+ */
+struct gpio_desc *__must_check __devm_gpiod_get_index_optional(struct device *dev,
+ const char *con_id,
+ unsigned int index,
+ enum gpiod_flags flags)
+{
+ 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;
+ }
+
+ return desc;
+}
+EXPORT_SYMBOL(__devm_gpiod_get_index_optional);
+
+/**
+ * devm_gpiod_get_array - Resource-managed gpiod_get_array()
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get_array(). GPIO descriptors returned from this function are
+ * automatically disposed on driver detach. See gpiod_get_array() for detailed
+ * information about behavior and return values.
+ */
+struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ struct gpio_descs **dr;
+ struct gpio_descs *descs;
+
+ dr = devres_alloc(devm_gpiod_release_array,
+ sizeof(struct gpio_descs *), GFP_KERNEL);
+ if (!dr)
+ return ERR_PTR(-ENOMEM);
+
+ descs = gpiod_get_array(dev, con_id, flags);
+ if (IS_ERR(descs)) {
+ devres_free(dr);
+ return descs;
+ }
+
+ *dr = descs;
+ devres_add(dev, dr);
+
+ return descs;
+}
+EXPORT_SYMBOL(devm_gpiod_get_array);
+
+/**
+ * devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional()
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get_array_optional(). GPIO descriptors returned from this
+ * function are automatically disposed on driver detach.
+ * See gpiod_get_array_optional() for detailed information about behavior and
+ * return values.
+ */
+struct gpio_descs *__must_check
+devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
+ enum gpiod_flags flags)
+{
+ struct gpio_descs *descs;
+
+ descs = devm_gpiod_get_array(dev, con_id, flags);
+ if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT))
+ return NULL;
+
+ return descs;
+}
+EXPORT_SYMBOL(devm_gpiod_get_array_optional);
+
+/**
+ * devm_gpiod_put - Resource-managed gpiod_put()
+ * @desc: GPIO descriptor to dispose of
+ *
+ * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or
+ * devm_gpiod_get_index(). Normally this function will not be called as the GPIO
+ * will be disposed of by the resource management code.
+ */
+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);
+
+/**
+ * devm_gpiod_put_array - Resource-managed gpiod_put_array()
+ * @descs: GPIO descriptor array to dispose of
+ *
+ * Dispose of an array of GPIO descriptors obtained with devm_gpiod_get_array().
+ * Normally this function will not be called as the GPIOs will be disposed of
+ * by the resource management code.
+ */
+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);
+
+
+
+
+static void devm_gpio_release(struct device *dev, void *res)
+{
+ unsigned *gpio = res;
+
+ gpio_free(*gpio);
+}
+
+static int devm_gpio_match(struct device *dev, void *res, void *data)
+{
+ unsigned *this = res, *gpio = data;
+
+ return *this == *gpio;
+}
+
+/**
+ * devm_gpio_request - request a GPIO for a managed device
+ * @dev: device to request the GPIO for
+ * @gpio: GPIO to allocate
+ * @label: the name of the requested GPIO
+ *
+ * Except for the extra @dev argument, this function takes the
+ * same arguments and performs the same function as
+ * gpio_request(). GPIOs requested with this function will be
+ * automatically freed on driver detach.
+ *
+ * If an GPIO allocated with this function needs to be freed
+ * separately, devm_gpio_free() must be used.
+ */
+
+int devm_gpio_request(struct device *dev, unsigned gpio, const char *label)
+{
+ unsigned *dr;
+ int rc;
+
+ dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ rc = gpio_request(gpio, label);
+ if (rc) {
+ devres_free(dr);
+ return rc;
+ }
+
+ *dr = gpio;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL(devm_gpio_request);
+
+/**
+ * devm_gpio_request_one - request a single GPIO with initial setup
+ * @dev: device to request for
+ * @gpio: the GPIO number
+ * @flags: GPIO configuration as specified by GPIOF_*
+ * @label: a literal description string of this GPIO
+ */
+int devm_gpio_request_one(struct device *dev, unsigned gpio,
+ unsigned long flags, const char *label)
+{
+ unsigned *dr;
+ int rc;
+
+ dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ rc = gpio_request_one(gpio, flags, label);
+ if (rc) {
+ devres_free(dr);
+ return rc;
+ }
+
+ *dr = gpio;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL(devm_gpio_request_one);
+
+/**
+ * devm_gpio_free - free a GPIO
+ * @dev: device to free GPIO for
+ * @gpio: GPIO to free
+ *
+ * Except for the extra @dev argument, this function takes the
+ * same arguments and performs the same function as gpio_free().
+ * This function instead of gpio_free() should be used to manually
+ * free GPIOs allocated with devm_gpio_request().
+ */
+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);
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
new file mode 100644
index 000000000..e3d968f75
--- /dev/null
+++ b/drivers/gpio/gpio-74x164.c
@@ -0,0 +1,197 @@
+/*
+ * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver
+ *
+ * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.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.
+ */
+
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define GEN_74X164_NUMBER_GPIOS 8
+
+struct gen_74x164_chip {
+ u8 *buffer;
+ struct gpio_chip gpio_chip;
+ struct mutex lock;
+ u32 registers;
+};
+
+static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
+{
+ return container_of(gc, struct gen_74x164_chip, gpio_chip);
+}
+
+static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
+{
+ struct spi_device *spi = to_spi_device(chip->gpio_chip.dev);
+ struct spi_message message;
+ struct spi_transfer *msg_buf;
+ int i, ret = 0;
+
+ msg_buf = kzalloc(chip->registers * sizeof(struct spi_transfer),
+ GFP_KERNEL);
+ if (!msg_buf)
+ return -ENOMEM;
+
+ spi_message_init(&message);
+
+ /*
+ * Since the registers are chained, every byte sent will make
+ * the previous byte shift to the next register in the
+ * chain. Thus, the first byte send will end up in the last
+ * register at the end of the transfer. So, to have a logical
+ * numbering, send the bytes in reverse order so that the last
+ * byte of the buffer will end up in the last register.
+ */
+ for (i = chip->registers - 1; i >= 0; i--) {
+ msg_buf[i].tx_buf = chip->buffer + i;
+ msg_buf[i].len = sizeof(u8);
+ spi_message_add_tail(msg_buf + i, &message);
+ }
+
+ ret = spi_sync(spi, &message);
+
+ kfree(msg_buf);
+
+ return ret;
+}
+
+static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
+ u8 bank = offset / 8;
+ u8 pin = offset % 8;
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = (chip->buffer[bank] >> pin) & 0x1;
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static void gen_74x164_set_value(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
+ u8 bank = offset / 8;
+ u8 pin = offset % 8;
+
+ mutex_lock(&chip->lock);
+ if (val)
+ chip->buffer[bank] |= (1 << pin);
+ else
+ chip->buffer[bank] &= ~(1 << pin);
+
+ __gen_74x164_write_config(chip);
+ mutex_unlock(&chip->lock);
+}
+
+static int gen_74x164_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ gen_74x164_set_value(gc, offset, val);
+ return 0;
+}
+
+static int gen_74x164_probe(struct spi_device *spi)
+{
+ struct gen_74x164_chip *chip;
+ int ret;
+
+ /*
+ * bits_per_word cannot be configured in platform data
+ */
+ spi->bits_per_word = 8;
+
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, chip);
+
+ chip->gpio_chip.label = spi->modalias;
+ chip->gpio_chip.direction_output = gen_74x164_direction_output;
+ chip->gpio_chip.get = gen_74x164_get_value;
+ chip->gpio_chip.set = gen_74x164_set_value;
+ chip->gpio_chip.base = -1;
+
+ if (of_property_read_u32(spi->dev.of_node, "registers-number",
+ &chip->registers)) {
+ dev_err(&spi->dev,
+ "Missing registers-number property in the DT.\n");
+ return -EINVAL;
+ }
+
+ chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
+ chip->buffer = devm_kzalloc(&spi->dev, chip->registers, GFP_KERNEL);
+ if (!chip->buffer)
+ return -ENOMEM;
+
+ chip->gpio_chip.can_sleep = true;
+ chip->gpio_chip.dev = &spi->dev;
+ chip->gpio_chip.owner = THIS_MODULE;
+
+ mutex_init(&chip->lock);
+
+ ret = __gen_74x164_write_config(chip);
+ if (ret) {
+ dev_err(&spi->dev, "Failed writing: %d\n", ret);
+ goto exit_destroy;
+ }
+
+ ret = gpiochip_add(&chip->gpio_chip);
+ if (!ret)
+ return 0;
+
+exit_destroy:
+ mutex_destroy(&chip->lock);
+
+ return ret;
+}
+
+static int gen_74x164_remove(struct spi_device *spi)
+{
+ struct gen_74x164_chip *chip = spi_get_drvdata(spi);
+
+ gpiochip_remove(&chip->gpio_chip);
+ mutex_destroy(&chip->lock);
+
+ return 0;
+}
+
+static const struct of_device_id gen_74x164_dt_ids[] = {
+ { .compatible = "fairchild,74hc595" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);
+
+static struct spi_driver gen_74x164_driver = {
+ .driver = {
+ .name = "74x164",
+ .owner = THIS_MODULE,
+ .of_match_table = gen_74x164_dt_ids,
+ },
+ .probe = gen_74x164_probe,
+ .remove = gen_74x164_remove,
+};
+module_spi_driver(gen_74x164_driver);
+
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
+MODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c
new file mode 100644
index 000000000..0763655cc
--- /dev/null
+++ b/drivers/gpio/gpio-74xx-mmio.c
@@ -0,0 +1,170 @@
+/*
+ * 74xx MMIO GPIO driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/platform_device.h>
+
+#define MMIO_74XX_DIR_IN (0 << 8)
+#define MMIO_74XX_DIR_OUT (1 << 8)
+#define MMIO_74XX_BIT_CNT(x) ((x) & 0xff)
+
+struct mmio_74xx_gpio_priv {
+ struct bgpio_chip bgc;
+ unsigned flags;
+};
+
+static const struct of_device_id mmio_74xx_gpio_ids[] = {
+ {
+ .compatible = "ti,741g125",
+ .data = (const void *)(MMIO_74XX_DIR_IN | 1),
+ },
+ {
+ .compatible = "ti,742g125",
+ .data = (const void *)(MMIO_74XX_DIR_IN | 2),
+ },
+ {
+ .compatible = "ti,74125",
+ .data = (const void *)(MMIO_74XX_DIR_IN | 4),
+ },
+ {
+ .compatible = "ti,74365",
+ .data = (const void *)(MMIO_74XX_DIR_IN | 6),
+ },
+ {
+ .compatible = "ti,74244",
+ .data = (const void *)(MMIO_74XX_DIR_IN | 8),
+ },
+ {
+ .compatible = "ti,741624",
+ .data = (const void *)(MMIO_74XX_DIR_IN | 16),
+ },
+ {
+ .compatible = "ti,741g74",
+ .data = (const void *)(MMIO_74XX_DIR_OUT | 1),
+ },
+ {
+ .compatible = "ti,7474",
+ .data = (const void *)(MMIO_74XX_DIR_OUT | 2),
+ },
+ {
+ .compatible = "ti,74175",
+ .data = (const void *)(MMIO_74XX_DIR_OUT | 4),
+ },
+ {
+ .compatible = "ti,74174",
+ .data = (const void *)(MMIO_74XX_DIR_OUT | 6),
+ },
+ {
+ .compatible = "ti,74273",
+ .data = (const void *)(MMIO_74XX_DIR_OUT | 8),
+ },
+ {
+ .compatible = "ti,7416374",
+ .data = (const void *)(MMIO_74XX_DIR_OUT | 16),
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mmio_74xx_gpio_ids);
+
+static inline struct mmio_74xx_gpio_priv *to_74xx_gpio(struct gpio_chip *gc)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ return container_of(bgc, struct mmio_74xx_gpio_priv, bgc);
+}
+
+static int mmio_74xx_get_direction(struct gpio_chip *gc, unsigned offset)
+{
+ struct mmio_74xx_gpio_priv *priv = to_74xx_gpio(gc);
+
+ return (priv->flags & MMIO_74XX_DIR_OUT) ? GPIOF_DIR_OUT : GPIOF_DIR_IN;
+}
+
+static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct mmio_74xx_gpio_priv *priv = to_74xx_gpio(gc);
+
+ return (priv->flags & MMIO_74XX_DIR_OUT) ? -ENOTSUPP : 0;
+}
+
+static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct mmio_74xx_gpio_priv *priv = to_74xx_gpio(gc);
+
+ if (priv->flags & MMIO_74XX_DIR_OUT) {
+ gc->set(gc, gpio, val);
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int mmio_74xx_gpio_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(mmio_74xx_gpio_ids, &pdev->dev);
+ struct mmio_74xx_gpio_priv *priv;
+ struct resource *res;
+ void __iomem *dat;
+ int err;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dat = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dat))
+ return PTR_ERR(dat);
+
+ priv->flags = (unsigned)of_id->data;
+
+ err = bgpio_init(&priv->bgc, &pdev->dev,
+ DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8),
+ dat, NULL, NULL, NULL, NULL, 0);
+ if (err)
+ return err;
+
+ priv->bgc.gc.direction_input = mmio_74xx_dir_in;
+ priv->bgc.gc.direction_output = mmio_74xx_dir_out;
+ priv->bgc.gc.get_direction = mmio_74xx_get_direction;
+ priv->bgc.gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags);
+ priv->bgc.gc.owner = THIS_MODULE;
+
+ platform_set_drvdata(pdev, priv);
+
+ return gpiochip_add(&priv->bgc.gc);
+}
+
+static int mmio_74xx_gpio_remove(struct platform_device *pdev)
+{
+ struct mmio_74xx_gpio_priv *priv = platform_get_drvdata(pdev);
+
+ return bgpio_remove(&priv->bgc);
+}
+
+static struct platform_driver mmio_74xx_gpio_driver = {
+ .driver = {
+ .name = "74xx-mmio-gpio",
+ .of_match_table = mmio_74xx_gpio_ids,
+ },
+ .probe = mmio_74xx_gpio_probe,
+ .remove = mmio_74xx_gpio_remove,
+};
+module_platform_driver(mmio_74xx_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("74xx MMIO GPIO driver");
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
new file mode 100644
index 000000000..d3d0a90fe
--- /dev/null
+++ b/drivers/gpio/gpio-adnp.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2011-2012 Avionic Design GmbH
+ *
+ * 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.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#define GPIO_DDR(gpio) (0x00 << (gpio)->reg_shift)
+#define GPIO_PLR(gpio) (0x01 << (gpio)->reg_shift)
+#define GPIO_IER(gpio) (0x02 << (gpio)->reg_shift)
+#define GPIO_ISR(gpio) (0x03 << (gpio)->reg_shift)
+#define GPIO_PTR(gpio) (0x04 << (gpio)->reg_shift)
+
+struct adnp {
+ struct i2c_client *client;
+ struct gpio_chip gpio;
+ unsigned int reg_shift;
+
+ struct mutex i2c_lock;
+ struct mutex irq_lock;
+
+ u8 *irq_enable;
+ u8 *irq_level;
+ u8 *irq_rise;
+ u8 *irq_fall;
+ u8 *irq_high;
+ u8 *irq_low;
+};
+
+static inline struct adnp *to_adnp(struct gpio_chip *chip)
+{
+ return container_of(chip, struct adnp, gpio);
+}
+
+static int adnp_read(struct adnp *adnp, unsigned offset, uint8_t *value)
+{
+ int err;
+
+ err = i2c_smbus_read_byte_data(adnp->client, offset);
+ if (err < 0) {
+ dev_err(adnp->gpio.dev, "%s failed: %d\n",
+ "i2c_smbus_read_byte_data()", err);
+ return err;
+ }
+
+ *value = err;
+ return 0;
+}
+
+static int adnp_write(struct adnp *adnp, unsigned offset, uint8_t value)
+{
+ int err;
+
+ err = i2c_smbus_write_byte_data(adnp->client, offset, value);
+ if (err < 0) {
+ dev_err(adnp->gpio.dev, "%s failed: %d\n",
+ "i2c_smbus_write_byte_data()", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int adnp_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct adnp *adnp = to_adnp(chip);
+ unsigned int reg = offset >> adnp->reg_shift;
+ unsigned int pos = offset & 7;
+ u8 value;
+ int err;
+
+ err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &value);
+ if (err < 0)
+ return err;
+
+ return (value & BIT(pos)) ? 1 : 0;
+}
+
+static void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value)
+{
+ unsigned int reg = offset >> adnp->reg_shift;
+ unsigned int pos = offset & 7;
+ int err;
+ u8 val;
+
+ err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &val);
+ if (err < 0)
+ return;
+
+ if (value)
+ val |= BIT(pos);
+ else
+ val &= ~BIT(pos);
+
+ adnp_write(adnp, GPIO_PLR(adnp) + reg, val);
+}
+
+static void adnp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct adnp *adnp = to_adnp(chip);
+
+ mutex_lock(&adnp->i2c_lock);
+ __adnp_gpio_set(adnp, offset, value);
+ mutex_unlock(&adnp->i2c_lock);
+}
+
+static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct adnp *adnp = to_adnp(chip);
+ unsigned int reg = offset >> adnp->reg_shift;
+ unsigned int pos = offset & 7;
+ u8 value;
+ int err;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value);
+ if (err < 0)
+ goto out;
+
+ value &= ~BIT(pos);
+
+ err = adnp_write(adnp, GPIO_DDR(adnp) + reg, value);
+ if (err < 0)
+ goto out;
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value);
+ if (err < 0)
+ goto out;
+
+ if (err & BIT(pos))
+ err = -EACCES;
+
+ err = 0;
+
+out:
+ mutex_unlock(&adnp->i2c_lock);
+ return err;
+}
+
+static int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct adnp *adnp = to_adnp(chip);
+ unsigned int reg = offset >> adnp->reg_shift;
+ unsigned int pos = offset & 7;
+ int err;
+ u8 val;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val);
+ if (err < 0)
+ goto out;
+
+ val |= BIT(pos);
+
+ err = adnp_write(adnp, GPIO_DDR(adnp) + reg, val);
+ if (err < 0)
+ goto out;
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val);
+ if (err < 0)
+ goto out;
+
+ if (!(val & BIT(pos))) {
+ err = -EPERM;
+ goto out;
+ }
+
+ __adnp_gpio_set(adnp, offset, value);
+ err = 0;
+
+out:
+ mutex_unlock(&adnp->i2c_lock);
+ return err;
+}
+
+static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct adnp *adnp = to_adnp(chip);
+ unsigned int num_regs = 1 << adnp->reg_shift, i, j;
+ int err;
+
+ for (i = 0; i < num_regs; i++) {
+ u8 ddr, plr, ier, isr;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ return;
+ }
+
+ err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ return;
+ }
+
+ err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ return;
+ }
+
+ err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ return;
+ }
+
+ mutex_unlock(&adnp->i2c_lock);
+
+ for (j = 0; j < 8; j++) {
+ unsigned int bit = (i << adnp->reg_shift) + j;
+ const char *direction = "input ";
+ const char *level = "low ";
+ const char *interrupt = "disabled";
+ const char *pending = "";
+
+ if (ddr & BIT(j))
+ direction = "output";
+
+ if (plr & BIT(j))
+ level = "high";
+
+ if (ier & BIT(j))
+ interrupt = "enabled ";
+
+ if (isr & BIT(j))
+ pending = "pending";
+
+ seq_printf(s, "%2u: %s %s IRQ %s %s\n", bit,
+ direction, level, interrupt, pending);
+ }
+ }
+}
+
+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->dev = &adnp->client->dev;
+ chip->of_node = chip->dev->of_node;
+ chip->owner = THIS_MODULE;
+
+ err = gpiochip_add(chip);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static irqreturn_t adnp_irq(int irq, void *data)
+{
+ struct adnp *adnp = data;
+ unsigned int num_regs, i;
+
+ num_regs = 1 << adnp->reg_shift;
+
+ for (i = 0; i < num_regs; i++) {
+ unsigned int base = i << adnp->reg_shift, bit;
+ u8 changed, level, isr, ier;
+ unsigned long pending;
+ int err;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ continue;
+ }
+
+ err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ continue;
+ }
+
+ err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ continue;
+ }
+
+ mutex_unlock(&adnp->i2c_lock);
+
+ /* determine pins that changed levels */
+ changed = level ^ adnp->irq_level[i];
+
+ /* compute edge-triggered interrupts */
+ pending = changed & ((adnp->irq_fall[i] & ~level) |
+ (adnp->irq_rise[i] & level));
+
+ /* add in level-triggered interrupts */
+ pending |= (adnp->irq_high[i] & level) |
+ (adnp->irq_low[i] & ~level);
+
+ /* mask out non-pending and disabled interrupts */
+ pending &= isr & ier;
+
+ for_each_set_bit(bit, &pending, 8) {
+ unsigned int child_irq;
+ child_irq = irq_find_mapping(adnp->gpio.irqdomain,
+ base + bit);
+ handle_nested_irq(child_irq);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void adnp_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
+ unsigned int reg = d->hwirq >> adnp->reg_shift;
+ unsigned int pos = d->hwirq & 7;
+
+ adnp->irq_enable[reg] &= ~BIT(pos);
+}
+
+static void adnp_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
+ unsigned int reg = d->hwirq >> adnp->reg_shift;
+ unsigned int pos = d->hwirq & 7;
+
+ adnp->irq_enable[reg] |= BIT(pos);
+}
+
+static int adnp_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
+ unsigned int reg = d->hwirq >> adnp->reg_shift;
+ unsigned int pos = d->hwirq & 7;
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ adnp->irq_rise[reg] |= BIT(pos);
+ else
+ adnp->irq_rise[reg] &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ adnp->irq_fall[reg] |= BIT(pos);
+ else
+ adnp->irq_fall[reg] &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_LEVEL_HIGH)
+ adnp->irq_high[reg] |= BIT(pos);
+ else
+ adnp->irq_high[reg] &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_LEVEL_LOW)
+ adnp->irq_low[reg] |= BIT(pos);
+ else
+ adnp->irq_low[reg] &= ~BIT(pos);
+
+ return 0;
+}
+
+static void adnp_irq_bus_lock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
+
+ mutex_lock(&adnp->irq_lock);
+}
+
+static void adnp_irq_bus_unlock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
+ unsigned int num_regs = 1 << adnp->reg_shift, i;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ for (i = 0; i < num_regs; i++)
+ adnp_write(adnp, GPIO_IER(adnp) + i, adnp->irq_enable[i]);
+
+ mutex_unlock(&adnp->i2c_lock);
+ mutex_unlock(&adnp->irq_lock);
+}
+
+static struct irq_chip adnp_irq_chip = {
+ .name = "gpio-adnp",
+ .irq_mask = adnp_irq_mask,
+ .irq_unmask = adnp_irq_unmask,
+ .irq_set_type = adnp_irq_set_type,
+ .irq_bus_lock = adnp_irq_bus_lock,
+ .irq_bus_sync_unlock = adnp_irq_bus_unlock,
+};
+
+static int adnp_irq_setup(struct adnp *adnp)
+{
+ unsigned int num_regs = 1 << adnp->reg_shift, i;
+ struct gpio_chip *chip = &adnp->gpio;
+ int err;
+
+ mutex_init(&adnp->irq_lock);
+
+ /*
+ * Allocate memory to keep track of the current level and trigger
+ * modes of the interrupts. To avoid multiple allocations, a single
+ * large buffer is allocated and pointers are setup to point at the
+ * corresponding offsets. For consistency, the layout of the buffer
+ * is chosen to match the register layout of the hardware in that
+ * each segment contains the corresponding bits for all interrupts.
+ */
+ adnp->irq_enable = devm_kzalloc(chip->dev, num_regs * 6, GFP_KERNEL);
+ if (!adnp->irq_enable)
+ return -ENOMEM;
+
+ adnp->irq_level = adnp->irq_enable + (num_regs * 1);
+ adnp->irq_rise = adnp->irq_enable + (num_regs * 2);
+ adnp->irq_fall = adnp->irq_enable + (num_regs * 3);
+ adnp->irq_high = adnp->irq_enable + (num_regs * 4);
+ adnp->irq_low = adnp->irq_enable + (num_regs * 5);
+
+ for (i = 0; i < num_regs; i++) {
+ /*
+ * Read the initial level of all pins to allow the emulation
+ * of edge triggered interrupts.
+ */
+ err = adnp_read(adnp, GPIO_PLR(adnp) + i, &adnp->irq_level[i]);
+ if (err < 0)
+ return err;
+
+ /* disable all interrupts */
+ err = adnp_write(adnp, GPIO_IER(adnp) + i, 0);
+ if (err < 0)
+ return err;
+
+ adnp->irq_enable[i] = 0x00;
+ }
+
+ err = devm_request_threaded_irq(chip->dev, adnp->client->irq,
+ NULL, adnp_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(chip->dev), adnp);
+ if (err != 0) {
+ dev_err(chip->dev, "can't request IRQ#%d: %d\n",
+ adnp->client->irq, err);
+ return err;
+ }
+
+ err = gpiochip_irqchip_add(chip,
+ &adnp_irq_chip,
+ 0,
+ handle_simple_irq,
+ IRQ_TYPE_NONE);
+ if (err) {
+ dev_err(chip->dev,
+ "could not connect irqchip to gpiochip\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int adnp_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct adnp *adnp;
+ u32 num_gpios;
+ int err;
+
+ err = of_property_read_u32(np, "nr-gpios", &num_gpios);
+ if (err < 0)
+ return err;
+
+ client->irq = irq_of_parse_and_map(np, 0);
+ if (!client->irq)
+ return -EPROBE_DEFER;
+
+ adnp = devm_kzalloc(&client->dev, sizeof(*adnp), GFP_KERNEL);
+ if (!adnp)
+ return -ENOMEM;
+
+ mutex_init(&adnp->i2c_lock);
+ adnp->client = client;
+
+ err = adnp_gpio_setup(adnp, num_gpios);
+ 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;
+}
+
+static int adnp_i2c_remove(struct i2c_client *client)
+{
+ struct adnp *adnp = i2c_get_clientdata(client);
+
+ gpiochip_remove(&adnp->gpio);
+ return 0;
+}
+
+static const struct i2c_device_id adnp_i2c_id[] = {
+ { "gpio-adnp" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, adnp_i2c_id);
+
+static const struct of_device_id adnp_of_match[] = {
+ { .compatible = "ad,gpio-adnp", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adnp_of_match);
+
+static struct i2c_driver adnp_i2c_driver = {
+ .driver = {
+ .name = "gpio-adnp",
+ .owner = THIS_MODULE,
+ .of_match_table = adnp_of_match,
+ },
+ .probe = adnp_i2c_probe,
+ .remove = adnp_i2c_remove,
+ .id_table = adnp_i2c_id,
+};
+module_i2c_driver(adnp_i2c_driver);
+
+MODULE_DESCRIPTION("Avionic Design N-bit GPIO expander");
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c
new file mode 100644
index 000000000..caff711ca
--- /dev/null
+++ b/drivers/gpio/gpio-adp5520.c
@@ -0,0 +1,190 @@
+/*
+ * GPIO driver for Analog Devices ADP5520 MFD PMICs
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/adp5520.h>
+
+#include <linux/gpio.h>
+
+struct adp5520_gpio {
+ struct device *master;
+ struct gpio_chip gpio_chip;
+ unsigned char lut[ADP5520_MAXGPIOS];
+ unsigned long output;
+};
+
+static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off)
+{
+ struct adp5520_gpio *dev;
+ uint8_t reg_val;
+
+ dev = container_of(chip, struct adp5520_gpio, gpio_chip);
+
+ /*
+ * There are dedicated registers for GPIO IN/OUT.
+ * Make sure we return the right value, even when configured as output
+ */
+
+ if (test_bit(off, &dev->output))
+ adp5520_read(dev->master, ADP5520_GPIO_OUT, &reg_val);
+ else
+ adp5520_read(dev->master, ADP5520_GPIO_IN, &reg_val);
+
+ return !!(reg_val & dev->lut[off]);
+}
+
+static void adp5520_gpio_set_value(struct gpio_chip *chip,
+ unsigned off, int val)
+{
+ struct adp5520_gpio *dev;
+ dev = container_of(chip, struct adp5520_gpio, gpio_chip);
+
+ if (val)
+ adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
+ else
+ adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
+}
+
+static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
+{
+ struct adp5520_gpio *dev;
+ dev = container_of(chip, struct adp5520_gpio, gpio_chip);
+
+ clear_bit(off, &dev->output);
+
+ return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2,
+ dev->lut[off]);
+}
+
+static int adp5520_gpio_direction_output(struct gpio_chip *chip,
+ unsigned off, int val)
+{
+ struct adp5520_gpio *dev;
+ int ret = 0;
+ dev = container_of(chip, struct adp5520_gpio, gpio_chip);
+
+ set_bit(off, &dev->output);
+
+ if (val)
+ ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT,
+ dev->lut[off]);
+ else
+ ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT,
+ dev->lut[off]);
+
+ ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2,
+ dev->lut[off]);
+
+ return ret;
+}
+
+static int adp5520_gpio_probe(struct platform_device *pdev)
+{
+ struct adp5520_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct adp5520_gpio *dev;
+ struct gpio_chip *gc;
+ int ret, i, gpios;
+ unsigned char ctl_mask = 0;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ if (pdev->id != ID_ADP5520) {
+ dev_err(&pdev->dev, "only ADP5520 supports GPIO\n");
+ return -ENODEV;
+ }
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+
+ dev->master = pdev->dev.parent;
+
+ for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++)
+ if (pdata->gpio_en_mask & (1 << i))
+ dev->lut[gpios++] = 1 << i;
+
+ if (gpios < 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ gc = &dev->gpio_chip;
+ gc->direction_input = adp5520_gpio_direction_input;
+ gc->direction_output = adp5520_gpio_direction_output;
+ gc->get = adp5520_gpio_get_value;
+ gc->set = adp5520_gpio_set_value;
+ gc->can_sleep = true;
+
+ gc->base = pdata->gpio_start;
+ gc->ngpio = gpios;
+ gc->label = pdev->name;
+ gc->owner = THIS_MODULE;
+
+ ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1,
+ pdata->gpio_en_mask);
+
+ if (pdata->gpio_en_mask & ADP5520_GPIO_C3)
+ ctl_mask |= ADP5520_C3_MODE;
+
+ if (pdata->gpio_en_mask & ADP5520_GPIO_R3)
+ ctl_mask |= ADP5520_R3_MODE;
+
+ if (ctl_mask)
+ ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
+ ctl_mask);
+
+ ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
+ pdata->gpio_pullup_mask);
+
+ if (ret) {
+ dev_err(&pdev->dev, "failed to write\n");
+ goto err;
+ }
+
+ ret = gpiochip_add(&dev->gpio_chip);
+ if (ret)
+ goto err;
+
+ platform_set_drvdata(pdev, dev);
+ return 0;
+
+err:
+ return ret;
+}
+
+static int adp5520_gpio_remove(struct platform_device *pdev)
+{
+ struct adp5520_gpio *dev;
+
+ dev = platform_get_drvdata(pdev);
+ gpiochip_remove(&dev->gpio_chip);
+
+ return 0;
+}
+
+static struct platform_driver adp5520_gpio_driver = {
+ .driver = {
+ .name = "adp5520-gpio",
+ },
+ .probe = adp5520_gpio_probe,
+ .remove = adp5520_gpio_remove,
+};
+
+module_platform_driver(adp5520_gpio_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("GPIO ADP5520 Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:adp5520-gpio");
diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c
new file mode 100644
index 000000000..d3fe6a677
--- /dev/null
+++ b/drivers/gpio/gpio-adp5588.c
@@ -0,0 +1,497 @@
+/*
+ * GPIO Chip driver for Analog Devices
+ * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
+ *
+ * Copyright 2009-2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <linux/i2c/adp5588.h>
+
+#define DRV_NAME "adp5588-gpio"
+
+/*
+ * Early pre 4.0 Silicon required to delay readout by at least 25ms,
+ * since the Event Counter Register updated 25ms after the interrupt
+ * asserted.
+ */
+#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
+
+struct adp5588_gpio {
+ struct i2c_client *client;
+ struct gpio_chip gpio_chip;
+ struct mutex lock; /* protect cached dir, dat_out */
+ /* protect serialized access to the interrupt controller bus */
+ struct mutex irq_lock;
+ unsigned gpio_start;
+ unsigned irq_base;
+ uint8_t dat_out[3];
+ uint8_t dir[3];
+ uint8_t int_lvl[3];
+ uint8_t int_en[3];
+ uint8_t irq_mask[3];
+ uint8_t irq_stat[3];
+};
+
+static int adp5588_gpio_read(struct i2c_client *client, u8 reg)
+{
+ int ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0)
+ dev_err(&client->dev, "Read Error\n");
+
+ return ret;
+}
+
+static int adp5588_gpio_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (ret < 0)
+ dev_err(&client->dev, "Write Error\n");
+
+ return ret;
+}
+
+static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
+{
+ struct adp5588_gpio *dev =
+ container_of(chip, struct adp5588_gpio, gpio_chip);
+ unsigned bank = ADP5588_BANK(off);
+ unsigned bit = ADP5588_BIT(off);
+ int val;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->dir[bank] & bit)
+ val = dev->dat_out[bank];
+ else
+ val = adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + bank);
+
+ mutex_unlock(&dev->lock);
+
+ return !!(val & bit);
+}
+
+static void adp5588_gpio_set_value(struct gpio_chip *chip,
+ unsigned off, int val)
+{
+ unsigned bank, bit;
+ struct adp5588_gpio *dev =
+ container_of(chip, struct adp5588_gpio, gpio_chip);
+
+ bank = ADP5588_BANK(off);
+ bit = ADP5588_BIT(off);
+
+ mutex_lock(&dev->lock);
+ if (val)
+ dev->dat_out[bank] |= bit;
+ else
+ dev->dat_out[bank] &= ~bit;
+
+ adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank,
+ dev->dat_out[bank]);
+ mutex_unlock(&dev->lock);
+}
+
+static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
+{
+ int ret;
+ unsigned bank;
+ struct adp5588_gpio *dev =
+ container_of(chip, struct adp5588_gpio, gpio_chip);
+
+ bank = ADP5588_BANK(off);
+
+ mutex_lock(&dev->lock);
+ dev->dir[bank] &= ~ADP5588_BIT(off);
+ ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]);
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+static int adp5588_gpio_direction_output(struct gpio_chip *chip,
+ unsigned off, int val)
+{
+ int ret;
+ unsigned bank, bit;
+ struct adp5588_gpio *dev =
+ container_of(chip, struct adp5588_gpio, gpio_chip);
+
+ bank = ADP5588_BANK(off);
+ bit = ADP5588_BIT(off);
+
+ mutex_lock(&dev->lock);
+ dev->dir[bank] |= bit;
+
+ if (val)
+ dev->dat_out[bank] |= bit;
+ else
+ dev->dat_out[bank] &= ~bit;
+
+ ret = adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank,
+ dev->dat_out[bank]);
+ ret |= adp5588_gpio_write(dev->client, GPIO_DIR1 + bank,
+ dev->dir[bank]);
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+#ifdef CONFIG_GPIO_ADP5588_IRQ
+static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off)
+{
+ struct adp5588_gpio *dev =
+ container_of(chip, struct adp5588_gpio, gpio_chip);
+ return dev->irq_base + off;
+}
+
+static void adp5588_irq_bus_lock(struct irq_data *d)
+{
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+
+ mutex_lock(&dev->irq_lock);
+}
+
+ /*
+ * genirq core code can issue chip->mask/unmask from atomic context.
+ * This doesn't work for slow busses where an access needs to sleep.
+ * bus_sync_unlock() is therefore called outside the atomic context,
+ * syncs the current irq mask state with the slow external controller
+ * and unlocks the bus.
+ */
+
+static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
+{
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ int i;
+
+ for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++)
+ if (dev->int_en[i] ^ dev->irq_mask[i]) {
+ dev->int_en[i] = dev->irq_mask[i];
+ adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i,
+ dev->int_en[i]);
+ }
+
+ mutex_unlock(&dev->irq_lock);
+}
+
+static void adp5588_irq_mask(struct irq_data *d)
+{
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ unsigned gpio = d->irq - dev->irq_base;
+
+ dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio);
+}
+
+static void adp5588_irq_unmask(struct irq_data *d)
+{
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ unsigned gpio = d->irq - dev->irq_base;
+
+ dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio);
+}
+
+static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ uint16_t gpio = d->irq - dev->irq_base;
+ unsigned bank, bit;
+
+ if ((type & IRQ_TYPE_EDGE_BOTH)) {
+ dev_err(&dev->client->dev, "irq %d: unsupported type %d\n",
+ d->irq, type);
+ return -EINVAL;
+ }
+
+ bank = ADP5588_BANK(gpio);
+ bit = ADP5588_BIT(gpio);
+
+ if (type & IRQ_TYPE_LEVEL_HIGH)
+ dev->int_lvl[bank] |= bit;
+ else if (type & IRQ_TYPE_LEVEL_LOW)
+ dev->int_lvl[bank] &= ~bit;
+ else
+ return -EINVAL;
+
+ adp5588_gpio_direction_input(&dev->gpio_chip, gpio);
+ adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + bank,
+ dev->int_lvl[bank]);
+
+ return 0;
+}
+
+static struct irq_chip adp5588_irq_chip = {
+ .name = "adp5588",
+ .irq_mask = adp5588_irq_mask,
+ .irq_unmask = adp5588_irq_unmask,
+ .irq_bus_lock = adp5588_irq_bus_lock,
+ .irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock,
+ .irq_set_type = adp5588_irq_set_type,
+};
+
+static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf)
+{
+ int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf);
+
+ if (ret < 0)
+ dev_err(&client->dev, "Read INT_STAT Error\n");
+
+ return ret;
+}
+
+static irqreturn_t adp5588_irq_handler(int irq, void *devid)
+{
+ struct adp5588_gpio *dev = devid;
+ unsigned status, bank, bit, pending;
+ int ret;
+ status = adp5588_gpio_read(dev->client, INT_STAT);
+
+ if (status & ADP5588_GPI_INT) {
+ ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat);
+ if (ret < 0)
+ memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat));
+
+ for (bank = 0, bit = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO);
+ bank++, bit = 0) {
+ pending = dev->irq_stat[bank] & dev->irq_mask[bank];
+
+ while (pending) {
+ if (pending & (1 << bit)) {
+ handle_nested_irq(dev->irq_base +
+ (bank << 3) + bit);
+ pending &= ~(1 << bit);
+
+ }
+ bit++;
+ }
+ }
+ }
+
+ adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */
+
+ return IRQ_HANDLED;
+}
+
+static int adp5588_irq_setup(struct adp5588_gpio *dev)
+{
+ struct i2c_client *client = dev->client;
+ struct adp5588_gpio_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ unsigned gpio;
+ int ret;
+
+ adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC);
+ adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */
+ adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */
+
+ dev->irq_base = pdata->irq_base;
+ mutex_init(&dev->irq_lock);
+
+ for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) {
+ int irq = gpio + dev->irq_base;
+ irq_set_chip_data(irq, dev);
+ irq_set_chip_and_handler(irq, &adp5588_irq_chip,
+ handle_level_irq);
+ irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ /*
+ * ARM needs us to explicitly flag the IRQ as VALID,
+ * once we do so, it will also set the noprobe.
+ */
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ }
+
+ ret = request_threaded_irq(client->irq,
+ NULL,
+ adp5588_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&client->dev), dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to request irq %d\n",
+ client->irq);
+ goto out;
+ }
+
+ dev->gpio_chip.to_irq = adp5588_gpio_to_irq;
+ adp5588_gpio_write(client, CFG,
+ ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT);
+
+ return 0;
+
+out:
+ dev->irq_base = 0;
+ return ret;
+}
+
+static void adp5588_irq_teardown(struct adp5588_gpio *dev)
+{
+ if (dev->irq_base)
+ free_irq(dev->client->irq, dev);
+}
+
+#else
+static int adp5588_irq_setup(struct adp5588_gpio *dev)
+{
+ struct i2c_client *client = dev->client;
+ dev_warn(&client->dev, "interrupt support not compiled in\n");
+
+ return 0;
+}
+
+static void adp5588_irq_teardown(struct adp5588_gpio *dev)
+{
+}
+#endif /* CONFIG_GPIO_ADP5588_IRQ */
+
+static int adp5588_gpio_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adp5588_gpio_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct adp5588_gpio *dev;
+ struct gpio_chip *gc;
+ int ret, i, revid;
+
+ if (!pdata) {
+ dev_err(&client->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->client = client;
+
+ gc = &dev->gpio_chip;
+ gc->direction_input = adp5588_gpio_direction_input;
+ gc->direction_output = adp5588_gpio_direction_output;
+ gc->get = adp5588_gpio_get_value;
+ gc->set = adp5588_gpio_set_value;
+ gc->can_sleep = true;
+
+ gc->base = pdata->gpio_start;
+ gc->ngpio = ADP5588_MAXGPIO;
+ gc->label = client->name;
+ gc->owner = THIS_MODULE;
+ gc->names = pdata->names;
+
+ mutex_init(&dev->lock);
+
+ ret = adp5588_gpio_read(dev->client, DEV_ID);
+ if (ret < 0)
+ goto err;
+
+ revid = ret & ADP5588_DEVICE_ID_MASK;
+
+ for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
+ dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i);
+ dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i);
+ ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0);
+ ret |= adp5588_gpio_write(client, GPIO_PULL1 + i,
+ (pdata->pullup_dis_mask >> (8 * i)) & 0xFF);
+ ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0);
+ if (ret)
+ goto err;
+ }
+
+ if (pdata->irq_base) {
+ if (WA_DELAYED_READOUT_REVID(revid)) {
+ dev_warn(&client->dev, "GPIO int not supported\n");
+ } else {
+ ret = adp5588_irq_setup(dev);
+ if (ret)
+ goto err;
+ }
+ }
+
+ ret = gpiochip_add(&dev->gpio_chip);
+ if (ret)
+ goto err_irq;
+
+ dev_info(&client->dev, "IRQ Base: %d Rev.: %d\n",
+ pdata->irq_base, revid);
+
+ if (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;
+
+err_irq:
+ adp5588_irq_teardown(dev);
+err:
+ return ret;
+}
+
+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->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->irq_base)
+ free_irq(dev->client->irq, dev);
+
+ gpiochip_remove(&dev->gpio_chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id adp5588_gpio_id[] = {
+ {DRV_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id);
+
+static struct i2c_driver adp5588_gpio_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = adp5588_gpio_probe,
+ .remove = adp5588_gpio_remove,
+ .id_table = adp5588_gpio_id,
+};
+
+module_i2c_driver(adp5588_gpio_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("GPIO ADP5588 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c
new file mode 100644
index 000000000..449fb46cb
--- /dev/null
+++ b/drivers/gpio/gpio-altera.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Based on gpio-mpc8xxx.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+
+#define ALTERA_GPIO_MAX_NGPIO 32
+#define ALTERA_GPIO_DATA 0x0
+#define ALTERA_GPIO_DIR 0x4
+#define ALTERA_GPIO_IRQ_MASK 0x8
+#define ALTERA_GPIO_EDGE_CAP 0xc
+
+/**
+* struct altera_gpio_chip
+* @mmchip : memory mapped chip structure.
+* @gpio_lock : synchronization lock so that new irq/set/get requests
+ will be blocked until the current one completes.
+* @interrupt_trigger : specifies the hardware configured IRQ trigger type
+ (rising, falling, both, high)
+* @mapped_irq : kernel mapped irq number.
+*/
+struct altera_gpio_chip {
+ struct of_mm_gpio_chip mmchip;
+ spinlock_t gpio_lock;
+ int interrupt_trigger;
+ int mapped_irq;
+};
+
+static void altera_gpio_irq_unmask(struct irq_data *d)
+{
+ struct altera_gpio_chip *altera_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ unsigned long flags;
+ u32 intmask;
+
+ altera_gc = irq_data_get_irq_chip_data(d);
+ mm_gc = &altera_gc->mmchip;
+
+ spin_lock_irqsave(&altera_gc->gpio_lock, flags);
+ intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+ /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */
+ intmask |= BIT(irqd_to_hwirq(d));
+ writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+ spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
+}
+
+static void altera_gpio_irq_mask(struct irq_data *d)
+{
+ struct altera_gpio_chip *altera_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ unsigned long flags;
+ u32 intmask;
+
+ altera_gc = irq_data_get_irq_chip_data(d);
+ mm_gc = &altera_gc->mmchip;
+
+ spin_lock_irqsave(&altera_gc->gpio_lock, flags);
+ intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+ /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */
+ intmask &= ~BIT(irqd_to_hwirq(d));
+ writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+ 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
+ */
+static int altera_gpio_irq_set_type(struct irq_data *d,
+ unsigned int type)
+{
+ struct altera_gpio_chip *altera_gc;
+
+ altera_gc = irq_data_get_irq_chip_data(d);
+
+ if (type == IRQ_TYPE_NONE)
+ return 0;
+ if (type == IRQ_TYPE_LEVEL_HIGH &&
+ altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH)
+ return 0;
+ if (type == IRQ_TYPE_EDGE_RISING &&
+ altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_RISING)
+ return 0;
+ if (type == IRQ_TYPE_EDGE_FALLING &&
+ altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_FALLING)
+ return 0;
+ if (type == IRQ_TYPE_EDGE_BOTH &&
+ altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_BOTH)
+ return 0;
+
+ return -EINVAL;
+}
+
+static unsigned int altera_gpio_irq_startup(struct irq_data *d) {
+ altera_gpio_irq_unmask(d);
+
+ return 0;
+}
+
+static struct irq_chip altera_irq_chip = {
+ .name = "altera-gpio",
+ .irq_mask = altera_gpio_irq_mask,
+ .irq_unmask = altera_gpio_irq_unmask,
+ .irq_set_type = altera_gpio_irq_set_type,
+ .irq_startup = altera_gpio_irq_startup,
+ .irq_shutdown = altera_gpio_irq_mask,
+};
+
+static int altera_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct of_mm_gpio_chip *mm_gc;
+
+ mm_gc = to_of_mm_gpio_chip(gc);
+
+ return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset));
+}
+
+static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct of_mm_gpio_chip *mm_gc;
+ struct altera_gpio_chip *chip;
+ unsigned long flags;
+ unsigned int data_reg;
+
+ mm_gc = to_of_mm_gpio_chip(gc);
+ chip = container_of(mm_gc, struct altera_gpio_chip, mmchip);
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+ data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA);
+ if (value)
+ data_reg |= BIT(offset);
+ else
+ data_reg &= ~BIT(offset);
+ writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA);
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct of_mm_gpio_chip *mm_gc;
+ struct altera_gpio_chip *chip;
+ unsigned long flags;
+ unsigned int gpio_ddr;
+
+ mm_gc = to_of_mm_gpio_chip(gc);
+ chip = container_of(mm_gc, struct altera_gpio_chip, mmchip);
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+ /* Set pin as input, assumes software controlled IP */
+ gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR);
+ gpio_ddr &= ~BIT(offset);
+ writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR);
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+ return 0;
+}
+
+static int altera_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset, int value)
+{
+ struct of_mm_gpio_chip *mm_gc;
+ struct altera_gpio_chip *chip;
+ unsigned long flags;
+ unsigned int data_reg, gpio_ddr;
+
+ mm_gc = to_of_mm_gpio_chip(gc);
+ chip = container_of(mm_gc, struct altera_gpio_chip, mmchip);
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+ /* Sets the GPIO value */
+ data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA);
+ if (value)
+ data_reg |= BIT(offset);
+ else
+ data_reg &= ~BIT(offset);
+ writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA);
+
+ /* Set pin as output, assumes software controlled IP */
+ gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR);
+ gpio_ddr |= BIT(offset);
+ writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR);
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+ return 0;
+}
+
+static void altera_gpio_irq_edge_handler(unsigned int irq,
+ struct irq_desc *desc)
+{
+ struct altera_gpio_chip *altera_gc;
+ struct irq_chip *chip;
+ struct of_mm_gpio_chip *mm_gc;
+ struct irq_domain *irqdomain;
+ unsigned long status;
+ int i;
+
+ altera_gc = irq_desc_get_handler_data(desc);
+ chip = irq_desc_get_chip(desc);
+ mm_gc = &altera_gc->mmchip;
+ irqdomain = altera_gc->mmchip.gc.irqdomain;
+
+ chained_irq_enter(chip, desc);
+
+ while ((status =
+ (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));
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+
+static void altera_gpio_irq_leveL_high_handler(unsigned int irq,
+ struct irq_desc *desc)
+{
+ struct altera_gpio_chip *altera_gc;
+ struct irq_chip *chip;
+ struct of_mm_gpio_chip *mm_gc;
+ struct irq_domain *irqdomain;
+ unsigned long status;
+ int i;
+
+ altera_gc = irq_desc_get_handler_data(desc);
+ chip = irq_desc_get_chip(desc);
+ mm_gc = &altera_gc->mmchip;
+ irqdomain = altera_gc->mmchip.gc.irqdomain;
+
+ chained_irq_enter(chip, 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));
+ }
+ chained_irq_exit(chip, desc);
+}
+
+static int altera_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ int reg, ret;
+ struct altera_gpio_chip *altera_gc;
+
+ altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL);
+ if (!altera_gc)
+ return -ENOMEM;
+
+ spin_lock_init(&altera_gc->gpio_lock);
+
+ if (of_property_read_u32(node, "altr,ngpio", &reg))
+ /* By default assume maximum ngpio */
+ altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO;
+ else
+ altera_gc->mmchip.gc.ngpio = reg;
+
+ if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) {
+ dev_warn(&pdev->dev,
+ "ngpio is greater than %d, defaulting to %d\n",
+ ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO);
+ altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO;
+ }
+
+ altera_gc->mmchip.gc.direction_input = altera_gpio_direction_input;
+ altera_gc->mmchip.gc.direction_output = altera_gpio_direction_output;
+ altera_gc->mmchip.gc.get = altera_gpio_get;
+ altera_gc->mmchip.gc.set = altera_gpio_set;
+ altera_gc->mmchip.gc.owner = THIS_MODULE;
+ altera_gc->mmchip.gc.dev = &pdev->dev;
+
+ ret = of_mm_gpiochip_add(node, &altera_gc->mmchip);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, altera_gc);
+
+ altera_gc->mapped_irq = platform_get_irq(pdev, 0);
+
+ if (altera_gc->mapped_irq < 0)
+ goto skip_irq;
+
+ if (of_property_read_u32(node, "altr,interrupt-type", &reg)) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev,
+ "altr,interrupt-type value not set in device tree\n");
+ goto teardown;
+ }
+ altera_gc->interrupt_trigger = reg;
+
+ ret = gpiochip_irqchip_add(&altera_gc->mmchip.gc, &altera_irq_chip, 0,
+ handle_simple_irq, IRQ_TYPE_NONE);
+
+ if (ret) {
+ dev_info(&pdev->dev, "could not add irqchip\n");
+ return ret;
+ }
+
+ gpiochip_set_chained_irqchip(&altera_gc->mmchip.gc,
+ &altera_irq_chip,
+ altera_gc->mapped_irq,
+ altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH ?
+ altera_gpio_irq_leveL_high_handler :
+ altera_gpio_irq_edge_handler);
+
+skip_irq:
+ return 0;
+teardown:
+ pr_err("%s: registration failed with status %d\n",
+ node->full_name, ret);
+
+ return ret;
+}
+
+static int altera_gpio_remove(struct platform_device *pdev)
+{
+ struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&altera_gc->mmchip.gc);
+
+ return -EIO;
+}
+
+static const struct of_device_id altera_gpio_of_match[] = {
+ { .compatible = "altr,pio-1.0", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altera_gpio_of_match);
+
+static struct platform_driver altera_gpio_driver = {
+ .driver = {
+ .name = "altera_gpio",
+ .of_match_table = of_match_ptr(altera_gpio_of_match),
+ },
+ .probe = altera_gpio_probe,
+ .remove = altera_gpio_remove,
+};
+
+static int __init altera_gpio_init(void)
+{
+ return platform_driver_register(&altera_gpio_driver);
+}
+subsys_initcall(altera_gpio_init);
+
+static void __exit altera_gpio_exit(void)
+{
+ platform_driver_unregister(&altera_gpio_driver);
+}
+module_exit(altera_gpio_exit);
+
+MODULE_AUTHOR("Tien Hock Loh <thloh@altera.com>");
+MODULE_DESCRIPTION("Altera GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c
new file mode 100644
index 000000000..d00d81928
--- /dev/null
+++ b/drivers/gpio/gpio-amd8111.c
@@ -0,0 +1,252 @@
+/*
+ * GPIO driver for AMD 8111 south bridges
+ *
+ * Copyright (c) 2012 Dmitry Eremin-Solenikov
+ *
+ * Based on the AMD RNG driver:
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ * with the majority of the code coming from:
+ *
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc
+ *
+ * derived from
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#define PMBASE_OFFSET 0xb0
+#define PMBASE_SIZE 0x30
+
+#define AMD_REG_GPIO(i) (0x10 + (i))
+
+#define AMD_GPIO_LTCH_STS 0x40 /* Latch status, w1 */
+#define AMD_GPIO_RTIN 0x20 /* Real Time in, ro */
+#define AMD_GPIO_DEBOUNCE 0x10 /* Debounce, rw */
+#define AMD_GPIO_MODE_MASK 0x0c /* Pin Mode Select, rw */
+#define AMD_GPIO_MODE_IN 0x00
+#define AMD_GPIO_MODE_OUT 0x04
+/* Enable alternative (e.g. clkout, IRQ, etc) function of the pin */
+#define AMD_GPIO_MODE_ALTFN 0x08 /* Or 0x09 */
+#define AMD_GPIO_X_MASK 0x03 /* In/Out specific, rw */
+#define AMD_GPIO_X_IN_ACTIVEHI 0x01 /* Active High */
+#define AMD_GPIO_X_IN_LATCH 0x02 /* Latched version is selected */
+#define AMD_GPIO_X_OUT_LOW 0x00
+#define AMD_GPIO_X_OUT_HI 0x01
+#define AMD_GPIO_X_OUT_CLK0 0x02
+#define AMD_GPIO_X_OUT_CLK1 0x03
+
+/*
+ * Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE. We do not actually
+ * register a pci_driver, because someone else might one day
+ * want to register another driver on the same PCI id.
+ */
+static const struct pci_device_id pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 0 },
+ { 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+struct amd_gpio {
+ struct gpio_chip chip;
+ u32 pmbase;
+ void __iomem *pm;
+ struct pci_dev *pdev;
+ spinlock_t lock; /* guards hw registers and orig table */
+ u8 orig[32];
+};
+
+#define to_agp(chip) container_of(chip, struct amd_gpio, chip)
+
+static int amd_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct amd_gpio *agp = to_agp(chip);
+
+ agp->orig[offset] = ioread8(agp->pm + AMD_REG_GPIO(offset)) &
+ (AMD_GPIO_DEBOUNCE | AMD_GPIO_MODE_MASK | AMD_GPIO_X_MASK);
+
+ dev_dbg(&agp->pdev->dev, "Requested gpio %d, data %x\n", offset, agp->orig[offset]);
+
+ return 0;
+}
+
+static void amd_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct amd_gpio *agp = to_agp(chip);
+
+ dev_dbg(&agp->pdev->dev, "Freed gpio %d, data %x\n", offset, agp->orig[offset]);
+
+ iowrite8(agp->orig[offset], agp->pm + AMD_REG_GPIO(offset));
+}
+
+static void amd_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct amd_gpio *agp = to_agp(chip);
+ u8 temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&agp->lock, flags);
+ temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
+ temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_OUT | (value ? AMD_GPIO_X_OUT_HI : AMD_GPIO_X_OUT_LOW);
+ iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
+ spin_unlock_irqrestore(&agp->lock, flags);
+
+ dev_dbg(&agp->pdev->dev, "Setting gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
+}
+
+static int amd_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct amd_gpio *agp = to_agp(chip);
+ u8 temp;
+
+ temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
+
+ dev_dbg(&agp->pdev->dev, "Getting gpio %d, reg=%02x\n", offset, temp);
+
+ return (temp & AMD_GPIO_RTIN) ? 1 : 0;
+}
+
+static int amd_gpio_dirout(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct amd_gpio *agp = to_agp(chip);
+ u8 temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&agp->lock, flags);
+ temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
+ temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_OUT | (value ? AMD_GPIO_X_OUT_HI : AMD_GPIO_X_OUT_LOW);
+ iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
+ spin_unlock_irqrestore(&agp->lock, flags);
+
+ dev_dbg(&agp->pdev->dev, "Dirout gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
+
+ return 0;
+}
+
+static int amd_gpio_dirin(struct gpio_chip *chip, unsigned offset)
+{
+ struct amd_gpio *agp = to_agp(chip);
+ u8 temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&agp->lock, flags);
+ temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
+ temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_IN;
+ iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
+ spin_unlock_irqrestore(&agp->lock, flags);
+
+ dev_dbg(&agp->pdev->dev, "Dirin gpio %d, reg=%02x\n", offset, temp);
+
+ return 0;
+}
+
+static struct amd_gpio gp = {
+ .chip = {
+ .label = "AMD GPIO",
+ .owner = THIS_MODULE,
+ .base = -1,
+ .ngpio = 32,
+ .request = amd_gpio_request,
+ .free = amd_gpio_free,
+ .set = amd_gpio_set,
+ .get = amd_gpio_get,
+ .direction_output = amd_gpio_dirout,
+ .direction_input = amd_gpio_dirin,
+ },
+};
+
+static int __init amd_gpio_init(void)
+{
+ int err = -ENODEV;
+ 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.
+ *
+ * We can't use plain pci_driver mechanism,
+ * as the device is really a multiple function device,
+ * main driver that binds to the pci_device is an smbus
+ * driver and have to find & bind to the device this way.
+ */
+ for_each_pci_dev(pdev) {
+ ent = pci_match_id(pci_tbl, pdev);
+ if (ent)
+ goto found;
+ }
+ /* Device not found. */
+ goto out;
+
+found:
+ err = pci_read_config_dword(pdev, 0x58, &gp.pmbase);
+ if (err)
+ goto out;
+ err = -EIO;
+ gp.pmbase &= 0x0000FF00;
+ if (gp.pmbase == 0)
+ goto out;
+ if (!request_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE, "AMD GPIO")) {
+ dev_err(&pdev->dev, "AMD GPIO region 0x%x already in use!\n",
+ gp.pmbase + PMBASE_OFFSET);
+ err = -EBUSY;
+ goto out;
+ }
+ gp.pm = ioport_map(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
+ if (!gp.pm) {
+ dev_err(&pdev->dev, "Couldn't map io port into io memory\n");
+ release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
+ err = -ENOMEM;
+ goto out;
+ }
+ gp.pdev = pdev;
+ gp.chip.dev = &pdev->dev;
+
+ spin_lock_init(&gp.lock);
+
+ printk(KERN_INFO "AMD-8111 GPIO detected\n");
+ err = gpiochip_add(&gp.chip);
+ if (err) {
+ printk(KERN_ERR "GPIO registering failed (%d)\n",
+ err);
+ ioport_unmap(gp.pm);
+ release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
+ goto out;
+ }
+out:
+ return err;
+}
+
+static void __exit amd_gpio_exit(void)
+{
+ gpiochip_remove(&gp.chip);
+ ioport_unmap(gp.pm);
+ release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
+}
+
+module_init(amd_gpio_init);
+module_exit(amd_gpio_exit);
+
+MODULE_AUTHOR("The Linux Kernel team");
+MODULE_DESCRIPTION("GPIO driver for AMD chipsets");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c
new file mode 100644
index 000000000..052fbc8fd
--- /dev/null
+++ b/drivers/gpio/gpio-arizona.c
@@ -0,0 +1,168 @@
+/*
+ * gpiolib support for Wolfson Arizona class devices
+ *
+ * Copyright 2012 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+struct arizona_gpio {
+ struct arizona *arizona;
+ struct gpio_chip gpio_chip;
+};
+
+static inline struct arizona_gpio *to_arizona_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct arizona_gpio, gpio_chip);
+}
+
+static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct arizona_gpio *arizona_gpio = to_arizona_gpio(chip);
+ struct arizona *arizona = arizona_gpio->arizona;
+
+ return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
+ ARIZONA_GPN_DIR, ARIZONA_GPN_DIR);
+}
+
+static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct arizona_gpio *arizona_gpio = to_arizona_gpio(chip);
+ struct arizona *arizona = arizona_gpio->arizona;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & ARIZONA_GPN_LVL)
+ return 1;
+ else
+ return 0;
+}
+
+static int arizona_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct arizona_gpio *arizona_gpio = to_arizona_gpio(chip);
+ struct arizona *arizona = arizona_gpio->arizona;
+
+ if (value)
+ value = ARIZONA_GPN_LVL;
+
+ return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
+ ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
+}
+
+static void arizona_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct arizona_gpio *arizona_gpio = to_arizona_gpio(chip);
+ struct arizona *arizona = arizona_gpio->arizona;
+
+ if (value)
+ value = ARIZONA_GPN_LVL;
+
+ regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
+ ARIZONA_GPN_LVL, value);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "arizona",
+ .owner = THIS_MODULE,
+ .direction_input = arizona_gpio_direction_in,
+ .get = arizona_gpio_get,
+ .direction_output = arizona_gpio_direction_out,
+ .set = arizona_gpio_set,
+ .can_sleep = true,
+};
+
+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_gpio *arizona_gpio;
+ int ret;
+
+ arizona_gpio = devm_kzalloc(&pdev->dev, sizeof(*arizona_gpio),
+ GFP_KERNEL);
+ if (!arizona_gpio)
+ return -ENOMEM;
+
+ arizona_gpio->arizona = arizona;
+ arizona_gpio->gpio_chip = template_chip;
+ arizona_gpio->gpio_chip.dev = &pdev->dev;
+#ifdef CONFIG_OF_GPIO
+ arizona_gpio->gpio_chip.of_node = arizona->dev->of_node;
+#endif
+
+ switch (arizona->type) {
+ case WM5102:
+ case WM5110:
+ case WM8280:
+ case WM8997:
+ arizona_gpio->gpio_chip.ngpio = 5;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown chip variant %d\n",
+ arizona->type);
+ return -EINVAL;
+ }
+
+ if (pdata && pdata->gpio_base)
+ arizona_gpio->gpio_chip.base = pdata->gpio_base;
+ else
+ arizona_gpio->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&arizona_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
+ ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, arizona_gpio);
+
+ return ret;
+
+err:
+ return ret;
+}
+
+static int arizona_gpio_remove(struct platform_device *pdev)
+{
+ struct arizona_gpio *arizona_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&arizona_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver arizona_gpio_driver = {
+ .driver.name = "arizona-gpio",
+ .probe = arizona_gpio_probe,
+ .remove = arizona_gpio_remove,
+};
+
+module_platform_driver(arizona_gpio_driver);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("GPIO interface for Arizona devices");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:arizona-gpio");
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
new file mode 100644
index 000000000..b164ce837
--- /dev/null
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -0,0 +1,680 @@
+/*
+ * Copyright (C) 2012-2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/module.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define BCM_GPIO_PASSWD 0x00a5a501
+#define GPIO_PER_BANK 32
+#define GPIO_MAX_BANK_NUM 8
+
+#define GPIO_BANK(gpio) ((gpio) >> 5)
+#define GPIO_BIT(gpio) ((gpio) & (GPIO_PER_BANK - 1))
+
+/* There is a GPIO control register for each GPIO */
+#define GPIO_CONTROL(gpio) (0x00000100 + ((gpio) << 2))
+
+/* The remaining registers are per GPIO bank */
+#define GPIO_OUT_STATUS(bank) (0x00000000 + ((bank) << 2))
+#define GPIO_IN_STATUS(bank) (0x00000020 + ((bank) << 2))
+#define GPIO_OUT_SET(bank) (0x00000040 + ((bank) << 2))
+#define GPIO_OUT_CLEAR(bank) (0x00000060 + ((bank) << 2))
+#define GPIO_INT_STATUS(bank) (0x00000080 + ((bank) << 2))
+#define GPIO_INT_MASK(bank) (0x000000a0 + ((bank) << 2))
+#define GPIO_INT_MSKCLR(bank) (0x000000c0 + ((bank) << 2))
+#define GPIO_PWD_STATUS(bank) (0x00000500 + ((bank) << 2))
+
+#define GPIO_GPPWR_OFFSET 0x00000520
+
+#define GPIO_GPCTR0_DBR_SHIFT 5
+#define GPIO_GPCTR0_DBR_MASK 0x000001e0
+
+#define GPIO_GPCTR0_ITR_SHIFT 3
+#define GPIO_GPCTR0_ITR_MASK 0x00000018
+#define GPIO_GPCTR0_ITR_CMD_RISING_EDGE 0x00000001
+#define GPIO_GPCTR0_ITR_CMD_FALLING_EDGE 0x00000002
+#define GPIO_GPCTR0_ITR_CMD_BOTH_EDGE 0x00000003
+
+#define GPIO_GPCTR0_IOTR_MASK 0x00000001
+#define GPIO_GPCTR0_IOTR_CMD_0UTPUT 0x00000000
+#define GPIO_GPCTR0_IOTR_CMD_INPUT 0x00000001
+
+#define GPIO_GPCTR0_DB_ENABLE_MASK 0x00000100
+
+#define LOCK_CODE 0xffffffff
+#define UNLOCK_CODE 0x00000000
+
+struct bcm_kona_gpio {
+ void __iomem *reg_base;
+ int num_bank;
+ spinlock_t lock;
+ struct gpio_chip gpio_chip;
+ struct irq_domain *irq_domain;
+ struct bcm_kona_gpio_bank *banks;
+ struct platform_device *pdev;
+};
+
+struct bcm_kona_gpio_bank {
+ int id;
+ int irq;
+ /* Used in the interrupt handler */
+ struct bcm_kona_gpio *kona_gpio;
+};
+
+static inline struct bcm_kona_gpio *to_kona_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct bcm_kona_gpio, gpio_chip);
+}
+
+static inline void bcm_kona_gpio_write_lock_regs(void __iomem *reg_base,
+ int bank_id, u32 lockcode)
+{
+ writel(BCM_GPIO_PASSWD, reg_base + GPIO_GPPWR_OFFSET);
+ writel(lockcode, reg_base + GPIO_PWD_STATUS(bank_id));
+}
+
+static void bcm_kona_gpio_lock_gpio(struct bcm_kona_gpio *kona_gpio,
+ unsigned gpio)
+{
+ u32 val;
+ unsigned long flags;
+ int bank_id = GPIO_BANK(gpio);
+
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id));
+ val |= BIT(gpio);
+ bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val);
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+}
+
+static void bcm_kona_gpio_unlock_gpio(struct bcm_kona_gpio *kona_gpio,
+ unsigned gpio)
+{
+ u32 val;
+ unsigned long flags;
+ int bank_id = GPIO_BANK(gpio);
+
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id));
+ val &= ~BIT(gpio);
+ bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val);
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+}
+
+static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ int bank_id = GPIO_BANK(gpio);
+ int bit = GPIO_BIT(gpio);
+ u32 val, reg_offset;
+ unsigned long flags;
+
+ kona_gpio = to_kona_gpio(chip);
+ reg_base = kona_gpio->reg_base;
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ /* determine the GPIO pin direction */
+ val = readl(reg_base + GPIO_CONTROL(gpio));
+ val &= GPIO_GPCTR0_IOTR_MASK;
+
+ /* this function only applies to output pin */
+ if (GPIO_GPCTR0_IOTR_CMD_INPUT == val)
+ goto out;
+
+ reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id);
+
+ val = readl(reg_base + reg_offset);
+ val |= BIT(bit);
+ writel(val, reg_base + reg_offset);
+
+out:
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+}
+
+static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ int bank_id = GPIO_BANK(gpio);
+ int bit = GPIO_BIT(gpio);
+ u32 val, reg_offset;
+ unsigned long flags;
+
+ kona_gpio = to_kona_gpio(chip);
+ reg_base = kona_gpio->reg_base;
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ /* determine the GPIO pin direction */
+ val = readl(reg_base + GPIO_CONTROL(gpio));
+ val &= GPIO_GPCTR0_IOTR_MASK;
+
+ /* read the GPIO bank status */
+ reg_offset = (GPIO_GPCTR0_IOTR_CMD_INPUT == val) ?
+ GPIO_IN_STATUS(bank_id) : GPIO_OUT_STATUS(bank_id);
+ val = readl(reg_base + reg_offset);
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+
+ /* return the specified bit status */
+ return !!(val & BIT(bit));
+}
+
+static int bcm_kona_gpio_request(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcm_kona_gpio *kona_gpio = to_kona_gpio(chip);
+
+ bcm_kona_gpio_unlock_gpio(kona_gpio, gpio);
+ return 0;
+}
+
+static void bcm_kona_gpio_free(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcm_kona_gpio *kona_gpio = to_kona_gpio(chip);
+
+ bcm_kona_gpio_lock_gpio(kona_gpio, gpio);
+}
+
+static int bcm_kona_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ u32 val;
+ unsigned long flags;
+
+ kona_gpio = to_kona_gpio(chip);
+ reg_base = kona_gpio->reg_base;
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(reg_base + GPIO_CONTROL(gpio));
+ val &= ~GPIO_GPCTR0_IOTR_MASK;
+ val |= GPIO_GPCTR0_IOTR_CMD_INPUT;
+ writel(val, reg_base + GPIO_CONTROL(gpio));
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+
+ return 0;
+}
+
+static int bcm_kona_gpio_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ int bank_id = GPIO_BANK(gpio);
+ int bit = GPIO_BIT(gpio);
+ u32 val, reg_offset;
+ unsigned long flags;
+
+ kona_gpio = to_kona_gpio(chip);
+ reg_base = kona_gpio->reg_base;
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(reg_base + GPIO_CONTROL(gpio));
+ val &= ~GPIO_GPCTR0_IOTR_MASK;
+ val |= GPIO_GPCTR0_IOTR_CMD_0UTPUT;
+ writel(val, reg_base + GPIO_CONTROL(gpio));
+ reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id);
+
+ val = readl(reg_base + reg_offset);
+ val |= BIT(bit);
+ writel(val, reg_base + reg_offset);
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+
+ return 0;
+}
+
+static int bcm_kona_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcm_kona_gpio *kona_gpio;
+
+ kona_gpio = to_kona_gpio(chip);
+ if (gpio >= kona_gpio->gpio_chip.ngpio)
+ return -ENXIO;
+ return irq_create_mapping(kona_gpio->irq_domain, gpio);
+}
+
+static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio,
+ unsigned debounce)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ u32 val, res;
+ unsigned long flags;
+
+ kona_gpio = to_kona_gpio(chip);
+ reg_base = kona_gpio->reg_base;
+ /* debounce must be 1-128ms (or 0) */
+ if ((debounce > 0 && debounce < 1000) || debounce > 128000) {
+ dev_err(chip->dev, "Debounce value %u not in range\n",
+ debounce);
+ return -EINVAL;
+ }
+
+ /* calculate debounce bit value */
+ if (debounce != 0) {
+ /* Convert to ms */
+ debounce /= 1000;
+ /* find the MSB */
+ res = fls(debounce) - 1;
+ /* Check if MSB-1 is set (round up or down) */
+ if (res > 0 && (debounce & BIT(res - 1)))
+ res++;
+ }
+
+ /* spin lock for read-modify-write of the GPIO register */
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(reg_base + GPIO_CONTROL(gpio));
+ val &= ~GPIO_GPCTR0_DBR_MASK;
+
+ if (debounce == 0) {
+ /* disable debounce */
+ val &= ~GPIO_GPCTR0_DB_ENABLE_MASK;
+ } else {
+ val |= GPIO_GPCTR0_DB_ENABLE_MASK |
+ (res << GPIO_GPCTR0_DBR_SHIFT);
+ }
+
+ writel(val, reg_base + GPIO_CONTROL(gpio));
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+
+ return 0;
+}
+
+static struct gpio_chip template_chip = {
+ .label = "bcm-kona-gpio",
+ .owner = THIS_MODULE,
+ .request = bcm_kona_gpio_request,
+ .free = bcm_kona_gpio_free,
+ .direction_input = bcm_kona_gpio_direction_input,
+ .get = bcm_kona_gpio_get,
+ .direction_output = bcm_kona_gpio_direction_output,
+ .set = bcm_kona_gpio_set,
+ .set_debounce = bcm_kona_gpio_set_debounce,
+ .to_irq = bcm_kona_gpio_to_irq,
+ .base = 0,
+};
+
+static void bcm_kona_gpio_irq_ack(struct irq_data *d)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ unsigned gpio = d->hwirq;
+ int bank_id = GPIO_BANK(gpio);
+ int bit = GPIO_BIT(gpio);
+ u32 val;
+ unsigned long flags;
+
+ kona_gpio = irq_data_get_irq_chip_data(d);
+ reg_base = kona_gpio->reg_base;
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(reg_base + GPIO_INT_STATUS(bank_id));
+ val |= BIT(bit);
+ writel(val, reg_base + GPIO_INT_STATUS(bank_id));
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+}
+
+static void bcm_kona_gpio_irq_mask(struct irq_data *d)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ unsigned gpio = d->hwirq;
+ int bank_id = GPIO_BANK(gpio);
+ int bit = GPIO_BIT(gpio);
+ u32 val;
+ unsigned long flags;
+
+ kona_gpio = irq_data_get_irq_chip_data(d);
+ reg_base = kona_gpio->reg_base;
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(reg_base + GPIO_INT_MASK(bank_id));
+ val |= BIT(bit);
+ writel(val, reg_base + GPIO_INT_MASK(bank_id));
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+}
+
+static void bcm_kona_gpio_irq_unmask(struct irq_data *d)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ unsigned gpio = d->hwirq;
+ int bank_id = GPIO_BANK(gpio);
+ int bit = GPIO_BIT(gpio);
+ u32 val;
+ unsigned long flags;
+
+ kona_gpio = irq_data_get_irq_chip_data(d);
+ reg_base = kona_gpio->reg_base;
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(reg_base + GPIO_INT_MSKCLR(bank_id));
+ val |= BIT(bit);
+ writel(val, reg_base + GPIO_INT_MSKCLR(bank_id));
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+}
+
+static int bcm_kona_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct bcm_kona_gpio *kona_gpio;
+ void __iomem *reg_base;
+ unsigned gpio = d->hwirq;
+ u32 lvl_type;
+ u32 val;
+ unsigned long flags;
+
+ kona_gpio = irq_data_get_irq_chip_data(d);
+ reg_base = kona_gpio->reg_base;
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ lvl_type = GPIO_GPCTR0_ITR_CMD_RISING_EDGE;
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ lvl_type = GPIO_GPCTR0_ITR_CMD_FALLING_EDGE;
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ lvl_type = GPIO_GPCTR0_ITR_CMD_BOTH_EDGE;
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ case IRQ_TYPE_LEVEL_LOW:
+ /* BCM GPIO doesn't support level triggering */
+ default:
+ dev_err(kona_gpio->gpio_chip.dev,
+ "Invalid BCM GPIO irq type 0x%x\n", type);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(reg_base + GPIO_CONTROL(gpio));
+ val &= ~GPIO_GPCTR0_ITR_MASK;
+ val |= lvl_type << GPIO_GPCTR0_ITR_SHIFT;
+ writel(val, reg_base + GPIO_CONTROL(gpio));
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
+
+ return 0;
+}
+
+static void bcm_kona_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ void __iomem *reg_base;
+ int bit, bank_id;
+ unsigned long sta;
+ struct bcm_kona_gpio_bank *bank = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+
+ /*
+ * For bank interrupts, we can't use chip_data to store the kona_gpio
+ * pointer, since GIC needs it for its own purposes. Therefore, we get
+ * our pointer from the bank structure.
+ */
+ reg_base = bank->kona_gpio->reg_base;
+ bank_id = bank->id;
+
+ while ((sta = readl(reg_base + GPIO_INT_STATUS(bank_id)) &
+ (~(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.
+ */
+ 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);
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int bcm_kona_gpio_irq_reqres(struct irq_data *d)
+{
+ struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d);
+
+ if (gpiochip_lock_as_irq(&kona_gpio->gpio_chip, d->hwirq)) {
+ dev_err(kona_gpio->gpio_chip.dev,
+ "unable to lock HW IRQ %lu for IRQ\n",
+ d->hwirq);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void bcm_kona_gpio_irq_relres(struct irq_data *d)
+{
+ struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d);
+
+ gpiochip_unlock_as_irq(&kona_gpio->gpio_chip, d->hwirq);
+}
+
+static struct irq_chip bcm_gpio_irq_chip = {
+ .name = "bcm-kona-gpio",
+ .irq_ack = bcm_kona_gpio_irq_ack,
+ .irq_mask = bcm_kona_gpio_irq_mask,
+ .irq_unmask = bcm_kona_gpio_irq_unmask,
+ .irq_set_type = bcm_kona_gpio_irq_set_type,
+ .irq_request_resources = bcm_kona_gpio_irq_reqres,
+ .irq_release_resources = bcm_kona_gpio_irq_relres,
+};
+
+static struct of_device_id const bcm_kona_gpio_of_match[] = {
+ { .compatible = "brcm,kona-gpio" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, bcm_kona_gpio_of_match);
+
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
+static int bcm_kona_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ int ret;
+
+ ret = irq_set_chip_data(irq, d->host_data);
+ if (ret < 0)
+ return ret;
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_and_handler(irq, &bcm_gpio_irq_chip, handle_simple_irq);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+
+ return 0;
+}
+
+static void bcm_kona_gpio_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static struct irq_domain_ops bcm_kona_irq_ops = {
+ .map = bcm_kona_gpio_irq_map,
+ .unmap = bcm_kona_gpio_irq_unmap,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static void bcm_kona_gpio_reset(struct bcm_kona_gpio *kona_gpio)
+{
+ void __iomem *reg_base;
+ int i;
+
+ reg_base = kona_gpio->reg_base;
+ /* disable interrupts and clear status */
+ for (i = 0; i < kona_gpio->num_bank; i++) {
+ /* Unlock the entire bank first */
+ bcm_kona_gpio_write_lock_regs(kona_gpio, i, UNLOCK_CODE);
+ writel(0xffffffff, reg_base + GPIO_INT_MASK(i));
+ writel(0xffffffff, reg_base + GPIO_INT_STATUS(i));
+ /* Now re-lock the bank */
+ bcm_kona_gpio_write_lock_regs(kona_gpio, i, LOCK_CODE);
+ }
+}
+
+static int bcm_kona_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ struct resource *res;
+ struct bcm_kona_gpio_bank *bank;
+ struct bcm_kona_gpio *kona_gpio;
+ struct gpio_chip *chip;
+ int ret;
+ int i;
+
+ match = of_match_device(bcm_kona_gpio_of_match, dev);
+ if (!match) {
+ dev_err(dev, "Failed to find gpio controller\n");
+ return -ENODEV;
+ }
+
+ kona_gpio = devm_kzalloc(dev, sizeof(*kona_gpio), GFP_KERNEL);
+ if (!kona_gpio)
+ return -ENOMEM;
+
+ 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) {
+ dev_err(dev, "Couldn't determine # GPIO banks\n");
+ return -ENOENT;
+ }
+ if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) {
+ dev_err(dev, "Too many GPIO banks configured (max=%d)\n",
+ GPIO_MAX_BANK_NUM);
+ return -ENXIO;
+ }
+ kona_gpio->banks = devm_kzalloc(dev,
+ kona_gpio->num_bank *
+ sizeof(*kona_gpio->banks), GFP_KERNEL);
+ if (!kona_gpio->banks)
+ return -ENOMEM;
+
+ kona_gpio->pdev = pdev;
+ platform_set_drvdata(pdev, kona_gpio);
+ chip->of_node = dev->of_node;
+ chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK;
+
+ kona_gpio->irq_domain = irq_domain_add_linear(dev->of_node,
+ chip->ngpio,
+ &bcm_kona_irq_ops,
+ kona_gpio);
+ if (!kona_gpio->irq_domain) {
+ dev_err(dev, "Couldn't allocate IRQ domain\n");
+ return -ENXIO;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ kona_gpio->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(kona_gpio->reg_base)) {
+ ret = -ENXIO;
+ goto err_irq_domain;
+ }
+
+ for (i = 0; i < kona_gpio->num_bank; i++) {
+ bank = &kona_gpio->banks[i];
+ bank->id = i;
+ bank->irq = platform_get_irq(pdev, i);
+ bank->kona_gpio = kona_gpio;
+ if (bank->irq < 0) {
+ dev_err(dev, "Couldn't get IRQ for bank %d", i);
+ ret = -ENOENT;
+ goto err_irq_domain;
+ }
+ }
+
+ dev_info(&pdev->dev, "Setting up Kona GPIO\n");
+
+ bcm_kona_gpio_reset(kona_gpio);
+
+ ret = gpiochip_add(chip);
+ if (ret < 0) {
+ dev_err(dev, "Couldn't add GPIO chip -- %d\n", ret);
+ goto err_irq_domain;
+ }
+ for (i = 0; i < chip->ngpio; i++) {
+ int irq = bcm_kona_gpio_to_irq(chip, i);
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_and_handler(irq, &bcm_gpio_irq_chip,
+ handle_simple_irq);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ }
+ for (i = 0; i < kona_gpio->num_bank; i++) {
+ bank = &kona_gpio->banks[i];
+ irq_set_chained_handler(bank->irq, bcm_kona_gpio_irq_handler);
+ irq_set_handler_data(bank->irq, bank);
+ }
+
+ spin_lock_init(&kona_gpio->lock);
+
+ return 0;
+
+err_irq_domain:
+ irq_domain_remove(kona_gpio->irq_domain);
+
+ return ret;
+}
+
+static struct platform_driver bcm_kona_gpio_driver = {
+ .driver = {
+ .name = "bcm-kona-gpio",
+ .of_match_table = bcm_kona_gpio_of_match,
+ },
+ .probe = bcm_kona_gpio_probe,
+};
+
+module_platform_driver(bcm_kona_gpio_driver);
+
+MODULE_AUTHOR("Broadcom Corporation <bcm-kernel-feedback-list@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Kona GPIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c
new file mode 100644
index 000000000..7e4c43c18
--- /dev/null
+++ b/drivers/gpio/gpio-bt8xx.c
@@ -0,0 +1,323 @@
+/*
+
+ bt8xx GPIO abuser
+
+ Copyright (C) 2008 Michael Buesch <m@bues.ch>
+
+ Please do _only_ contact the people listed _above_ with issues related to this driver.
+ All the other people listed below are not related to this driver. Their names
+ are only here, because this driver is derived from the bt848 driver.
+
+
+ Derived from the bt848 driver:
+
+ Copyright (C) 1996,97,98 Ralph Metzler
+ & Marcus Metzler
+ (c) 1999-2002 Gerd Knorr
+
+ some v4l2 code lines are taken from Justin's bttv2 driver which is
+ (c) 2000 Justin Schoeman
+
+ V4L1 removal from:
+ (c) 2005-2006 Nickolay V. Shmyrev
+
+ Fixes to be fully V4L2 compliant by
+ (c) 2006 Mauro Carvalho Chehab
+
+ Cropping and overscan support
+ Copyright (C) 2005, 2006 Michael H. Schimek
+ Sponsored by OPQ Systems AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+/* Steal the hardware definitions from the bttv driver. */
+#include "../media/pci/bt8xx/bt848.h"
+
+
+#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */
+
+
+struct bt8xxgpio {
+ spinlock_t lock;
+
+ void __iomem *mmio;
+ struct pci_dev *pdev;
+ struct gpio_chip gpio;
+
+#ifdef CONFIG_PM
+ u32 saved_outen;
+ u32 saved_data;
+#endif
+};
+
+#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr))
+#define bgread(adr) readl(bg->mmio+(adr))
+
+
+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 bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio);
+ unsigned long flags;
+ u32 outen, data;
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ data = bgread(BT848_GPIO_DATA);
+ data &= ~(1 << nr);
+ bgwrite(data, BT848_GPIO_DATA);
+
+ outen = bgread(BT848_GPIO_OUT_EN);
+ outen &= ~(1 << nr);
+ bgwrite(outen, BT848_GPIO_OUT_EN);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ return 0;
+}
+
+static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+ struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&bg->lock, flags);
+ val = bgread(BT848_GPIO_DATA);
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ return !!(val & (1 << nr));
+}
+
+static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio,
+ unsigned nr, int val)
+{
+ struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio);
+ unsigned long flags;
+ u32 outen, data;
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ outen = bgread(BT848_GPIO_OUT_EN);
+ outen |= (1 << nr);
+ bgwrite(outen, BT848_GPIO_OUT_EN);
+
+ data = bgread(BT848_GPIO_DATA);
+ if (val)
+ data |= (1 << nr);
+ else
+ data &= ~(1 << nr);
+ bgwrite(data, BT848_GPIO_DATA);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ return 0;
+}
+
+static void bt8xxgpio_gpio_set(struct gpio_chip *gpio,
+ unsigned nr, int val)
+{
+ struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio);
+ unsigned long flags;
+ u32 data;
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ data = bgread(BT848_GPIO_DATA);
+ if (val)
+ data |= (1 << nr);
+ else
+ data &= ~(1 << nr);
+ bgwrite(data, BT848_GPIO_DATA);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+}
+
+static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg)
+{
+ struct gpio_chip *c = &bg->gpio;
+
+ c->label = dev_name(&bg->pdev->dev);
+ c->owner = THIS_MODULE;
+ c->direction_input = bt8xxgpio_gpio_direction_input;
+ c->get = bt8xxgpio_gpio_get;
+ c->direction_output = bt8xxgpio_gpio_direction_output;
+ c->set = bt8xxgpio_gpio_set;
+ c->dbg_show = NULL;
+ c->base = modparam_gpiobase;
+ c->ngpio = BT8XXGPIO_NR_GPIOS;
+ c->can_sleep = false;
+}
+
+static int bt8xxgpio_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ struct bt8xxgpio *bg;
+ int err;
+
+ bg = devm_kzalloc(&dev->dev, sizeof(struct bt8xxgpio), GFP_KERNEL);
+ if (!bg)
+ return -ENOMEM;
+
+ bg->pdev = dev;
+ spin_lock_init(&bg->lock);
+
+ err = pci_enable_device(dev);
+ if (err) {
+ printk(KERN_ERR "bt8xxgpio: 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",
+ (unsigned long long)pci_resource_start(dev, 0));
+ err = -EBUSY;
+ goto err_disable;
+ }
+ pci_set_master(dev);
+ pci_set_drvdata(dev, bg);
+
+ bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000);
+ if (!bg->mmio) {
+ printk(KERN_ERR "bt8xxgpio: ioremap() failed\n");
+ err = -EIO;
+ goto err_disable;
+ }
+
+ /* Disable interrupts */
+ bgwrite(0, BT848_INT_MASK);
+
+ /* gpio init */
+ bgwrite(0, BT848_GPIO_DMA_CTL);
+ bgwrite(0, BT848_GPIO_REG_INP);
+ bgwrite(0, BT848_GPIO_OUT_EN);
+
+ bt8xxgpio_gpio_setup(bg);
+ err = gpiochip_add(&bg->gpio);
+ if (err) {
+ printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n");
+ goto err_disable;
+ }
+
+ return 0;
+
+err_disable:
+ pci_disable_device(dev);
+
+ return err;
+}
+
+static void bt8xxgpio_remove(struct pci_dev *pdev)
+{
+ struct bt8xxgpio *bg = pci_get_drvdata(pdev);
+
+ gpiochip_remove(&bg->gpio);
+
+ bgwrite(0, BT848_INT_MASK);
+ bgwrite(~0x0, BT848_INT_STAT);
+ bgwrite(0x0, BT848_GPIO_OUT_EN);
+
+ pci_disable_device(pdev);
+}
+
+#ifdef CONFIG_PM
+static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct bt8xxgpio *bg = pci_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ bg->saved_outen = bgread(BT848_GPIO_OUT_EN);
+ bg->saved_data = bgread(BT848_GPIO_DATA);
+
+ bgwrite(0, BT848_INT_MASK);
+ bgwrite(~0x0, BT848_INT_STAT);
+ bgwrite(0x0, BT848_GPIO_OUT_EN);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static int bt8xxgpio_resume(struct pci_dev *pdev)
+{
+ struct bt8xxgpio *bg = pci_get_drvdata(pdev);
+ unsigned long flags;
+ int err;
+
+ pci_set_power_state(pdev, PCI_D0);
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+ pci_restore_state(pdev);
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ bgwrite(0, BT848_INT_MASK);
+ bgwrite(0, BT848_GPIO_DMA_CTL);
+ bgwrite(0, BT848_GPIO_REG_INP);
+ bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN);
+ bgwrite(bg->saved_data & bg->saved_outen,
+ BT848_GPIO_DATA);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ return 0;
+}
+#else
+#define bt8xxgpio_suspend NULL
+#define bt8xxgpio_resume NULL
+#endif /* CONFIG_PM */
+
+static const struct pci_device_id bt8xxgpio_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl);
+
+static struct pci_driver bt8xxgpio_pci_driver = {
+ .name = "bt8xxgpio",
+ .id_table = bt8xxgpio_pci_tbl,
+ .probe = bt8xxgpio_probe,
+ .remove = bt8xxgpio_remove,
+ .suspend = bt8xxgpio_suspend,
+ .resume = bt8xxgpio_resume,
+};
+
+module_pci_driver(bt8xxgpio_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card");
diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
new file mode 100644
index 000000000..b6908f1ff
--- /dev/null
+++ b/drivers/gpio/gpio-clps711x.c
@@ -0,0 +1,100 @@
+/*
+ * CLPS711X GPIO driver
+ *
+ * Copyright (C) 2012,2013 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/platform_device.h>
+
+static int clps711x_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ void __iomem *dat, *dir;
+ struct bgpio_chip *bgc;
+ struct resource *res;
+ int err, id = np ? of_alias_get_id(np, "gpio") : pdev->id;
+
+ if ((id < 0) || (id > 4))
+ return -ENODEV;
+
+ bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
+ if (!bgc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dat = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dat))
+ return PTR_ERR(dat);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ dir = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ switch (id) {
+ case 3:
+ /* PORTD is inverted logic for direction register */
+ err = bgpio_init(bgc, &pdev->dev, 1, dat, NULL, NULL,
+ NULL, dir, 0);
+ break;
+ default:
+ err = bgpio_init(bgc, &pdev->dev, 1, dat, NULL, NULL,
+ dir, NULL, 0);
+ break;
+ }
+
+ if (err)
+ return err;
+
+ switch (id) {
+ case 4:
+ /* PORTE is 3 lines only */
+ bgc->gc.ngpio = 3;
+ break;
+ default:
+ break;
+ }
+
+ bgc->gc.base = id * 8;
+ bgc->gc.owner = THIS_MODULE;
+ platform_set_drvdata(pdev, bgc);
+
+ return gpiochip_add(&bgc->gc);
+}
+
+static int clps711x_gpio_remove(struct platform_device *pdev)
+{
+ struct bgpio_chip *bgc = platform_get_drvdata(pdev);
+
+ return bgpio_remove(bgc);
+}
+
+static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = {
+ { .compatible = "cirrus,clps711x-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clps711x_gpio_ids);
+
+static struct platform_driver clps711x_gpio_driver = {
+ .driver = {
+ .name = "clps711x-gpio",
+ .of_match_table = of_match_ptr(clps711x_gpio_ids),
+ },
+ .probe = clps711x_gpio_probe,
+ .remove = clps711x_gpio_remove,
+};
+module_platform_driver(clps711x_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("CLPS711X GPIO driver");
+MODULE_ALIAS("platform:clps711x-gpio");
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c
new file mode 100644
index 000000000..ab457fc00
--- /dev/null
+++ b/drivers/gpio/gpio-crystalcove.c
@@ -0,0 +1,395 @@
+/*
+ * gpio-crystalcove.c - Intel Crystal Cove GPIO Driver
+ *
+ * Copyright (C) 2012, 2014 Intel Corporation. All rights reserved.
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Author: Yang, Bin <bin.yang@intel.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/seq_file.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/mfd/intel_soc_pmic.h>
+
+#define CRYSTALCOVE_GPIO_NUM 16
+#define CRYSTALCOVE_VGPIO_NUM 95
+
+#define UPDATE_IRQ_TYPE BIT(0)
+#define UPDATE_IRQ_MASK BIT(1)
+
+#define GPIO0IRQ 0x0b
+#define GPIO1IRQ 0x0c
+#define MGPIO0IRQS0 0x19
+#define MGPIO1IRQS0 0x1a
+#define MGPIO0IRQSX 0x1b
+#define MGPIO1IRQSX 0x1c
+#define GPIO0P0CTLO 0x2b
+#define GPIO0P0CTLI 0x33
+#define GPIO1P0CTLO 0x3b
+#define GPIO1P0CTLI 0x43
+#define GPIOPANELCTL 0x52
+
+#define CTLI_INTCNT_DIS (0)
+#define CTLI_INTCNT_NE (1 << 1)
+#define CTLI_INTCNT_PE (2 << 1)
+#define CTLI_INTCNT_BE (3 << 1)
+
+#define CTLO_DIR_IN (0)
+#define CTLO_DIR_OUT (1 << 5)
+
+#define CTLO_DRV_CMOS (0)
+#define CTLO_DRV_OD (1 << 4)
+
+#define CTLO_DRV_REN (1 << 3)
+
+#define CTLO_RVAL_2KDW (0)
+#define CTLO_RVAL_2KUP (1 << 1)
+#define CTLO_RVAL_50KDW (2 << 1)
+#define CTLO_RVAL_50KUP (3 << 1)
+
+#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP)
+#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET)
+
+enum ctrl_register {
+ CTRL_IN,
+ CTRL_OUT,
+};
+
+/**
+ * struct crystalcove_gpio - Crystal Cove GPIO controller
+ * @buslock: for bus lock/sync and unlock.
+ * @chip: the abstract gpio_chip structure.
+ * @regmap: the regmap from the parent device.
+ * @update: pending IRQ setting update, to be written to the chip upon unlock.
+ * @intcnt_value: the Interrupt Detect value to be written.
+ * @set_irq_mask: true if the IRQ mask needs to be set, false to clear.
+ */
+struct crystalcove_gpio {
+ struct mutex buslock; /* irq_bus_lock */
+ struct gpio_chip chip;
+ struct regmap *regmap;
+ int update;
+ int intcnt_value;
+ bool set_irq_mask;
+};
+
+static inline struct crystalcove_gpio *to_cg(struct gpio_chip *gc)
+{
+ return container_of(gc, struct crystalcove_gpio, chip);
+}
+
+static inline int to_reg(int gpio, enum ctrl_register reg_type)
+{
+ int reg;
+
+ if (gpio == 94) {
+ return GPIOPANELCTL;
+ }
+
+ if (reg_type == CTRL_IN) {
+ if (gpio < 8)
+ reg = GPIO0P0CTLI;
+ else
+ reg = GPIO1P0CTLI;
+ } else {
+ if (gpio < 8)
+ reg = GPIO0P0CTLO;
+ else
+ reg = GPIO1P0CTLO;
+ }
+
+ return reg + gpio % 8;
+}
+
+static void crystalcove_update_irq_mask(struct crystalcove_gpio *cg,
+ int gpio)
+{
+ u8 mirqs0 = gpio < 8 ? MGPIO0IRQS0 : MGPIO1IRQS0;
+ int mask = BIT(gpio % 8);
+
+ if (cg->set_irq_mask)
+ regmap_update_bits(cg->regmap, mirqs0, mask, mask);
+ else
+ regmap_update_bits(cg->regmap, mirqs0, mask, 0);
+}
+
+static void crystalcove_update_irq_ctrl(struct crystalcove_gpio *cg, int gpio)
+{
+ int reg = to_reg(gpio, CTRL_IN);
+
+ regmap_update_bits(cg->regmap, reg, CTLI_INTCNT_BE, cg->intcnt_value);
+}
+
+static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
+{
+ struct crystalcove_gpio *cg = to_cg(chip);
+
+ if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ return 0;
+
+ return regmap_write(cg->regmap, to_reg(gpio, CTRL_OUT),
+ CTLO_INPUT_SET);
+}
+
+static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct crystalcove_gpio *cg = to_cg(chip);
+
+ if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ return 0;
+
+ return regmap_write(cg->regmap, to_reg(gpio, CTRL_OUT),
+ CTLO_OUTPUT_SET | value);
+}
+
+static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio)
+{
+ struct crystalcove_gpio *cg = to_cg(chip);
+ int ret;
+ unsigned int val;
+
+ if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ return 0;
+
+ ret = regmap_read(cg->regmap, to_reg(gpio, CTRL_IN), &val);
+ if (ret)
+ return ret;
+
+ return val & 0x1;
+}
+
+static void crystalcove_gpio_set(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct crystalcove_gpio *cg = to_cg(chip);
+
+ if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ return;
+
+ if (value)
+ regmap_update_bits(cg->regmap, to_reg(gpio, CTRL_OUT), 1, 1);
+ else
+ regmap_update_bits(cg->regmap, to_reg(gpio, CTRL_OUT), 1, 0);
+}
+
+static int crystalcove_irq_type(struct irq_data *data, unsigned type)
+{
+ struct crystalcove_gpio *cg = to_cg(irq_data_get_irq_chip_data(data));
+
+ switch (type) {
+ case IRQ_TYPE_NONE:
+ cg->intcnt_value = CTLI_INTCNT_DIS;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ cg->intcnt_value = CTLI_INTCNT_BE;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ cg->intcnt_value = CTLI_INTCNT_PE;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ cg->intcnt_value = CTLI_INTCNT_NE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cg->update |= UPDATE_IRQ_TYPE;
+
+ return 0;
+}
+
+static void crystalcove_bus_lock(struct irq_data *data)
+{
+ struct crystalcove_gpio *cg = to_cg(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 = to_cg(irq_data_get_irq_chip_data(data));
+ int gpio = data->hwirq;
+
+ if (cg->update & UPDATE_IRQ_TYPE)
+ crystalcove_update_irq_ctrl(cg, gpio);
+ if (cg->update & UPDATE_IRQ_MASK)
+ crystalcove_update_irq_mask(cg, gpio);
+ cg->update = 0;
+
+ mutex_unlock(&cg->buslock);
+}
+
+static void crystalcove_irq_unmask(struct irq_data *data)
+{
+ struct crystalcove_gpio *cg = to_cg(irq_data_get_irq_chip_data(data));
+
+ cg->set_irq_mask = false;
+ cg->update |= UPDATE_IRQ_MASK;
+}
+
+static void crystalcove_irq_mask(struct irq_data *data)
+{
+ struct crystalcove_gpio *cg = to_cg(irq_data_get_irq_chip_data(data));
+
+ cg->set_irq_mask = true;
+ cg->update |= UPDATE_IRQ_MASK;
+}
+
+static 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,
+};
+
+static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
+{
+ struct crystalcove_gpio *cg = data;
+ unsigned int p0, p1;
+ int pending;
+ int gpio;
+ unsigned int virq;
+
+ if (regmap_read(cg->regmap, GPIO0IRQ, &p0) ||
+ regmap_read(cg->regmap, GPIO1IRQ, &p1))
+ return IRQ_NONE;
+
+ regmap_write(cg->regmap, GPIO0IRQ, p0);
+ regmap_write(cg->regmap, GPIO1IRQ, p1);
+
+ pending = p0 | p1 << 8;
+
+ for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) {
+ if (pending & BIT(gpio)) {
+ virq = irq_find_mapping(cg->chip.irqdomain, gpio);
+ handle_nested_irq(virq);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void crystalcove_gpio_dbg_show(struct seq_file *s,
+ struct gpio_chip *chip)
+{
+ struct crystalcove_gpio *cg = to_cg(chip);
+ int gpio, offset;
+ unsigned int ctlo, ctli, mirqs0, mirqsx, irq;
+
+ for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) {
+ regmap_read(cg->regmap, to_reg(gpio, CTRL_OUT), &ctlo);
+ regmap_read(cg->regmap, to_reg(gpio, CTRL_IN), &ctli);
+ regmap_read(cg->regmap, gpio < 8 ? MGPIO0IRQS0 : MGPIO1IRQS0,
+ &mirqs0);
+ regmap_read(cg->regmap, gpio < 8 ? MGPIO0IRQSX : MGPIO1IRQSX,
+ &mirqsx);
+ regmap_read(cg->regmap, gpio < 8 ? GPIO0IRQ : GPIO1IRQ,
+ &irq);
+
+ offset = gpio % 8;
+ seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %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,
+ mirqs0 & BIT(offset) ? "s0 mask " : "s0 unmask",
+ mirqsx & BIT(offset) ? "sx mask " : "sx unmask",
+ irq & BIT(offset) ? "pending" : " ");
+ }
+}
+
+static int crystalcove_gpio_probe(struct platform_device *pdev)
+{
+ int irq = platform_get_irq(pdev, 0);
+ struct crystalcove_gpio *cg;
+ int retval;
+ struct device *dev = pdev->dev.parent;
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+
+ if (irq < 0)
+ return irq;
+
+ cg = devm_kzalloc(&pdev->dev, sizeof(*cg), GFP_KERNEL);
+ 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;
+ cg->chip.direction_output = crystalcove_gpio_dir_out;
+ cg->chip.get = crystalcove_gpio_get;
+ cg->chip.set = crystalcove_gpio_set;
+ cg->chip.base = -1;
+ cg->chip.ngpio = CRYSTALCOVE_VGPIO_NUM;
+ cg->chip.can_sleep = true;
+ cg->chip.dev = dev;
+ cg->chip.dbg_show = crystalcove_gpio_dbg_show;
+ cg->regmap = pmic->regmap;
+
+ retval = gpiochip_add(&cg->chip);
+ if (retval) {
+ dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval);
+ return retval;
+ }
+
+ gpiochip_irqchip_add(&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);
+
+ if (retval) {
+ dev_warn(&pdev->dev, "request irq failed: %d\n", retval);
+ goto out_remove_gpio;
+ }
+
+ return 0;
+
+out_remove_gpio:
+ gpiochip_remove(&cg->chip);
+ 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);
+
+ gpiochip_remove(&cg->chip);
+ 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>");
+MODULE_DESCRIPTION("Intel Crystal Cove GPIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c
new file mode 100644
index 000000000..7b0b198a5
--- /dev/null
+++ b/drivers/gpio/gpio-cs5535.c
@@ -0,0 +1,380 @@
+/*
+ * AMD CS5535/CS5536 GPIO driver
+ * Copyright (C) 2006 Advanced Micro Devices, Inc.
+ * Copyright (C) 2007-2009 Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/cs5535.h>
+#include <asm/msr.h>
+
+#define DRV_NAME "cs5535-gpio"
+
+/*
+ * Some GPIO pins
+ * 31-29,23 : reserved (always mask out)
+ * 28 : Power Button
+ * 26 : PME#
+ * 22-16 : LPC
+ * 14,15 : SMBus
+ * 9,8 : UART1
+ * 7 : PCI INTB
+ * 3,4 : UART2/DDC
+ * 2 : IDE_IRQ0
+ * 1 : AC_BEEP
+ * 0 : PCI INTA
+ *
+ * If a mask was not specified, allow all except
+ * reserved and Power Button
+ */
+#define GPIO_DEFAULT_MASK 0x0F7FFFFF
+
+static ulong mask = GPIO_DEFAULT_MASK;
+module_param_named(mask, mask, ulong, 0444);
+MODULE_PARM_DESC(mask, "GPIO channel mask.");
+
+static struct cs5535_gpio_chip {
+ struct gpio_chip chip;
+ resource_size_t base;
+
+ struct platform_device *pdev;
+ spinlock_t lock;
+} cs5535_gpio_chip;
+
+/*
+ * The CS5535/CS5536 GPIOs support a number of extra features not defined
+ * by the gpio_chip API, so these are exported. For a full list of the
+ * registers, see include/linux/cs5535.h.
+ */
+
+static void errata_outl(struct cs5535_gpio_chip *chip, u32 val,
+ unsigned int reg)
+{
+ unsigned long addr = chip->base + 0x80 + reg;
+
+ /*
+ * According to the CS5536 errata (#36), after suspend
+ * a write to the high bank GPIO register will clear all
+ * non-selected bits; the recommended workaround is a
+ * read-modify-write operation.
+ *
+ * Don't apply this errata to the edge status GPIOs, as writing
+ * to their lower bits will clear them.
+ */
+ if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS) {
+ if (val & 0xffff)
+ val |= (inl(addr) & 0xffff); /* ignore the high bits */
+ else
+ val |= (inl(addr) ^ (val >> 16));
+ }
+ outl(val, addr);
+}
+
+static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
+ unsigned int reg)
+{
+ if (offset < 16)
+ /* low bank register */
+ outl(1 << offset, chip->base + reg);
+ else
+ /* high bank register */
+ errata_outl(chip, 1 << (offset - 16), reg);
+}
+
+void cs5535_gpio_set(unsigned offset, unsigned int reg)
+{
+ struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ __cs5535_gpio_set(chip, offset, reg);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_set);
+
+static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
+ unsigned int reg)
+{
+ if (offset < 16)
+ /* low bank register */
+ outl(1 << (offset + 16), chip->base + reg);
+ else
+ /* high bank register */
+ errata_outl(chip, 1 << offset, reg);
+}
+
+void cs5535_gpio_clear(unsigned offset, unsigned int reg)
+{
+ struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ __cs5535_gpio_clear(chip, offset, reg);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
+
+int cs5535_gpio_isset(unsigned offset, unsigned int reg)
+{
+ struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+ unsigned long flags;
+ long val;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ if (offset < 16)
+ /* low bank register */
+ val = inl(chip->base + reg);
+ else {
+ /* high bank register */
+ val = inl(chip->base + 0x80 + reg);
+ offset -= 16;
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return (val & (1 << offset)) ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
+
+int cs5535_gpio_set_irq(unsigned group, unsigned irq)
+{
+ uint32_t lo, hi;
+
+ if (group > 7 || irq > 15)
+ return -EINVAL;
+
+ rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
+
+ lo &= ~(0xF << (group * 4));
+ lo |= (irq & 0xF) << (group * 4);
+
+ wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_set_irq);
+
+void cs5535_gpio_setup_event(unsigned offset, int pair, int pme)
+{
+ struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+ uint32_t shift = (offset % 8) * 4;
+ unsigned long flags;
+ uint32_t val;
+
+ if (offset >= 24)
+ offset = GPIO_MAP_W;
+ else if (offset >= 16)
+ offset = GPIO_MAP_Z;
+ else if (offset >= 8)
+ offset = GPIO_MAP_Y;
+ else
+ offset = GPIO_MAP_X;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ val = inl(chip->base + offset);
+
+ /* Clear whatever was there before */
+ val &= ~(0xF << shift);
+
+ /* Set the new value */
+ val |= ((pair & 7) << shift);
+
+ /* Set the PME bit if this is a PME event */
+ if (pme)
+ val |= (1 << (shift + 3));
+
+ outl(val, chip->base + offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event);
+
+/*
+ * Generic gpio_chip API support.
+ */
+
+static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
+{
+ struct cs5535_gpio_chip *chip =
+ container_of(c, struct cs5535_gpio_chip, chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* check if this pin is available */
+ if ((mask & (1 << offset)) == 0) {
+ dev_info(&chip->pdev->dev,
+ "pin %u is not available (check mask)\n", offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ /* disable output aux 1 & 2 on this pin */
+ __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
+ __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
+
+ /* disable input aux 1 on this pin */
+ __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return cs5535_gpio_isset(offset, GPIO_READ_BACK);
+}
+
+static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ if (val)
+ cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
+ else
+ cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
+}
+
+static int chip_direction_input(struct gpio_chip *c, unsigned offset)
+{
+ struct cs5535_gpio_chip *chip =
+ container_of(c, struct cs5535_gpio_chip, chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
+ __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
+{
+ struct cs5535_gpio_chip *chip =
+ container_of(c, struct cs5535_gpio_chip, chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
+ __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
+ if (val)
+ __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
+ else
+ __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static const char * const cs5535_gpio_names[] = {
+ "GPIO0", "GPIO1", "GPIO2", "GPIO3",
+ "GPIO4", "GPIO5", "GPIO6", "GPIO7",
+ "GPIO8", "GPIO9", "GPIO10", "GPIO11",
+ "GPIO12", "GPIO13", "GPIO14", "GPIO15",
+ "GPIO16", "GPIO17", "GPIO18", "GPIO19",
+ "GPIO20", "GPIO21", "GPIO22", NULL,
+ "GPIO24", "GPIO25", "GPIO26", "GPIO27",
+ "GPIO28", NULL, NULL, NULL,
+};
+
+static struct cs5535_gpio_chip cs5535_gpio_chip = {
+ .chip = {
+ .owner = THIS_MODULE,
+ .label = DRV_NAME,
+
+ .base = 0,
+ .ngpio = 32,
+ .names = cs5535_gpio_names,
+ .request = chip_gpio_request,
+
+ .get = chip_gpio_get,
+ .set = chip_gpio_set,
+
+ .direction_input = chip_direction_input,
+ .direction_output = chip_direction_output,
+ },
+};
+
+static int cs5535_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int err = -EIO;
+ ulong mask_orig = mask;
+
+ /* There are two ways to get the GPIO base address; one is by
+ * fetching it from MSR_LBAR_GPIO, the other is by reading the
+ * PCI BAR info. The latter method is easier (especially across
+ * different architectures), so we'll stick with that for now. If
+ * it turns out to be unreliable in the face of crappy BIOSes, we
+ * can always go back to using MSRs.. */
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "can't fetch device resource info\n");
+ goto done;
+ }
+
+ if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name)) {
+ dev_err(&pdev->dev, "can't request region\n");
+ goto done;
+ }
+
+ /* set up the driver-specific struct */
+ cs5535_gpio_chip.base = res->start;
+ cs5535_gpio_chip.pdev = pdev;
+ spin_lock_init(&cs5535_gpio_chip.lock);
+
+ dev_info(&pdev->dev, "reserved resource region %pR\n", res);
+
+ /* mask out reserved pins */
+ mask &= 0x1F7FFFFF;
+
+ /* do not allow pin 28, Power Button, as there's special handling
+ * in the PMC needed. (note 12, p. 48) */
+ mask &= ~(1 << 28);
+
+ if (mask_orig != mask)
+ dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n",
+ mask_orig, mask);
+
+ /* finally, register with the generic GPIO API */
+ err = gpiochip_add(&cs5535_gpio_chip.chip);
+ if (err)
+ goto done;
+
+ return 0;
+
+done:
+ return err;
+}
+
+static int cs5535_gpio_remove(struct platform_device *pdev)
+{
+ gpiochip_remove(&cs5535_gpio_chip.chip);
+
+ return 0;
+}
+
+static struct platform_driver cs5535_gpio_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = cs5535_gpio_probe,
+ .remove = cs5535_gpio_remove,
+};
+
+module_platform_driver(cs5535_gpio_driver);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
+MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c
new file mode 100644
index 000000000..2e9578ec0
--- /dev/null
+++ b/drivers/gpio/gpio-da9052.c
@@ -0,0 +1,257 @@
+/*
+ * GPIO Driver for Dialog DA9052 PMICs.
+ *
+ * Copyright(c) 2011 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/syscalls.h>
+#include <linux/seq_file.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/pdata.h>
+
+#define DA9052_INPUT 1
+#define DA9052_OUTPUT_OPENDRAIN 2
+#define DA9052_OUTPUT_PUSHPULL 3
+
+#define DA9052_SUPPLY_VDD_IO1 0
+
+#define DA9052_DEBOUNCING_OFF 0
+#define DA9052_DEBOUNCING_ON 1
+
+#define DA9052_OUTPUT_LOWLEVEL 0
+
+#define DA9052_ACTIVE_LOW 0
+#define DA9052_ACTIVE_HIGH 1
+
+#define DA9052_GPIO_MAX_PORTS_PER_REGISTER 8
+#define DA9052_GPIO_SHIFT_COUNT(no) (no%8)
+#define DA9052_GPIO_MASK_UPPER_NIBBLE 0xF0
+#define DA9052_GPIO_MASK_LOWER_NIBBLE 0x0F
+#define DA9052_GPIO_NIBBLE_SHIFT 4
+#define DA9052_IRQ_GPI0 16
+#define DA9052_GPIO_ODD_SHIFT 7
+#define DA9052_GPIO_EVEN_SHIFT 3
+
+struct da9052_gpio {
+ struct da9052 *da9052;
+ struct gpio_chip gp;
+};
+
+static inline struct da9052_gpio *to_da9052_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct da9052_gpio, gp);
+}
+
+static unsigned char da9052_gpio_port_odd(unsigned offset)
+{
+ return offset % 2;
+}
+
+static int da9052_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ int da9052_port_direction = 0;
+ int ret;
+
+ ret = da9052_reg_read(gpio->da9052,
+ DA9052_GPIO_0_1_REG + (offset >> 1));
+ if (ret < 0)
+ return ret;
+
+ if (da9052_gpio_port_odd(offset)) {
+ da9052_port_direction = ret & DA9052_GPIO_ODD_PORT_PIN;
+ da9052_port_direction >>= 4;
+ } else {
+ da9052_port_direction = ret & DA9052_GPIO_EVEN_PORT_PIN;
+ }
+
+ switch (da9052_port_direction) {
+ case DA9052_INPUT:
+ if (offset < DA9052_GPIO_MAX_PORTS_PER_REGISTER)
+ ret = da9052_reg_read(gpio->da9052,
+ DA9052_STATUS_C_REG);
+ else
+ ret = da9052_reg_read(gpio->da9052,
+ DA9052_STATUS_D_REG);
+ if (ret < 0)
+ return ret;
+ if (ret & (1 << DA9052_GPIO_SHIFT_COUNT(offset)))
+ return 1;
+ else
+ return 0;
+ case DA9052_OUTPUT_PUSHPULL:
+ if (da9052_gpio_port_odd(offset))
+ return ret & DA9052_GPIO_ODD_PORT_MODE;
+ else
+ return ret & DA9052_GPIO_EVEN_PORT_MODE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void da9052_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ int ret;
+
+ if (da9052_gpio_port_odd(offset)) {
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_ODD_PORT_MODE,
+ value << DA9052_GPIO_ODD_SHIFT);
+ if (ret != 0)
+ dev_err(gpio->da9052->dev,
+ "Failed to updated gpio odd reg,%d",
+ ret);
+ } else {
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_EVEN_PORT_MODE,
+ value << DA9052_GPIO_EVEN_SHIFT);
+ if (ret != 0)
+ dev_err(gpio->da9052->dev,
+ "Failed to updated gpio even reg,%d",
+ ret);
+ }
+}
+
+static int da9052_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ unsigned char register_value;
+ int ret;
+
+ /* Format: function - 2 bits type - 1 bit mode - 1 bit */
+ register_value = DA9052_INPUT | DA9052_ACTIVE_LOW << 2 |
+ DA9052_DEBOUNCING_ON << 3;
+
+ if (da9052_gpio_port_odd(offset))
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_MASK_UPPER_NIBBLE,
+ (register_value <<
+ DA9052_GPIO_NIBBLE_SHIFT));
+ else
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_MASK_LOWER_NIBBLE,
+ register_value);
+
+ return ret;
+}
+
+static int da9052_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset, int value)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ unsigned char register_value;
+ int ret;
+
+ /* Format: Function - 2 bits Type - 1 bit Mode - 1 bit */
+ register_value = DA9052_OUTPUT_PUSHPULL | DA9052_SUPPLY_VDD_IO1 << 2 |
+ value << 3;
+
+ if (da9052_gpio_port_odd(offset))
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_MASK_UPPER_NIBBLE,
+ (register_value <<
+ DA9052_GPIO_NIBBLE_SHIFT));
+ else
+ ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
+ DA9052_GPIO_0_1_REG,
+ DA9052_GPIO_MASK_LOWER_NIBBLE,
+ register_value);
+
+ return ret;
+}
+
+static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset)
+{
+ struct da9052_gpio *gpio = to_da9052_gpio(gc);
+ struct da9052 *da9052 = gpio->da9052;
+
+ int irq;
+
+ irq = regmap_irq_get_virq(da9052->irq_data, DA9052_IRQ_GPI0 + offset);
+
+ return irq;
+}
+
+static struct gpio_chip reference_gp = {
+ .label = "da9052-gpio",
+ .owner = THIS_MODULE,
+ .get = da9052_gpio_get,
+ .set = da9052_gpio_set,
+ .direction_input = da9052_gpio_direction_input,
+ .direction_output = da9052_gpio_direction_output,
+ .to_irq = da9052_gpio_to_irq,
+ .can_sleep = true,
+ .ngpio = 16,
+ .base = -1,
+};
+
+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)
+ return -ENOMEM;
+
+ gpio->da9052 = dev_get_drvdata(pdev->dev.parent);
+ pdata = dev_get_platdata(gpio->da9052->dev);
+
+ gpio->gp = reference_gp;
+ if (pdata && pdata->gpio_base)
+ gpio->gp.base = pdata->gpio_base;
+
+ ret = gpiochip_add(&gpio->gp);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, gpio);
+
+ return 0;
+}
+
+static int da9052_gpio_remove(struct platform_device *pdev)
+{
+ struct da9052_gpio *gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gpio->gp);
+ return 0;
+}
+
+static struct platform_driver da9052_gpio_driver = {
+ .probe = da9052_gpio_probe,
+ .remove = da9052_gpio_remove,
+ .driver = {
+ .name = "da9052-gpio",
+ },
+};
+
+module_platform_driver(da9052_gpio_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 GPIO Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-gpio");
diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c
new file mode 100644
index 000000000..7227e6ed3
--- /dev/null
+++ b/drivers/gpio/gpio-da9055.c
@@ -0,0 +1,204 @@
+/*
+ * GPIO Driver for Dialog DA9055 PMICs.
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+#include <linux/mfd/da9055/pdata.h>
+
+#define DA9055_VDD_IO 0x0
+#define DA9055_PUSH_PULL 0x3
+#define DA9055_ACT_LOW 0x0
+#define DA9055_GPI 0x1
+#define DA9055_PORT_MASK 0x3
+#define DA9055_PORT_SHIFT(offset) (4 * (offset % 2))
+
+#define DA9055_INPUT DA9055_GPI
+#define DA9055_OUTPUT DA9055_PUSH_PULL
+#define DA9055_IRQ_GPI0 3
+
+struct da9055_gpio {
+ struct da9055 *da9055;
+ struct gpio_chip gp;
+};
+
+static inline struct da9055_gpio *to_da9055_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct da9055_gpio, gp);
+}
+
+static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct da9055_gpio *gpio = to_da9055_gpio(gc);
+ int gpio_direction = 0;
+ int ret;
+
+ /* Get GPIO direction */
+ ret = da9055_reg_read(gpio->da9055, (offset >> 1) + DA9055_REG_GPIO0_1);
+ if (ret < 0)
+ return ret;
+
+ gpio_direction = ret & (DA9055_PORT_MASK) << DA9055_PORT_SHIFT(offset);
+ gpio_direction >>= DA9055_PORT_SHIFT(offset);
+ switch (gpio_direction) {
+ case DA9055_INPUT:
+ ret = da9055_reg_read(gpio->da9055, DA9055_REG_STATUS_B);
+ if (ret < 0)
+ return ret;
+ break;
+ case DA9055_OUTPUT:
+ ret = da9055_reg_read(gpio->da9055, DA9055_REG_GPIO_MODE0_2);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret & (1 << offset);
+
+}
+
+static void da9055_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct da9055_gpio *gpio = to_da9055_gpio(gc);
+
+ da9055_reg_update(gpio->da9055,
+ DA9055_REG_GPIO_MODE0_2,
+ 1 << offset,
+ value << offset);
+}
+
+static int da9055_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct da9055_gpio *gpio = to_da9055_gpio(gc);
+ unsigned char reg_byte;
+
+ reg_byte = (DA9055_ACT_LOW | DA9055_GPI)
+ << DA9055_PORT_SHIFT(offset);
+
+ return da9055_reg_update(gpio->da9055, (offset >> 1) +
+ DA9055_REG_GPIO0_1,
+ DA9055_PORT_MASK <<
+ DA9055_PORT_SHIFT(offset),
+ reg_byte);
+}
+
+static int da9055_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset, int value)
+{
+ struct da9055_gpio *gpio = to_da9055_gpio(gc);
+ unsigned char reg_byte;
+ int ret;
+
+ reg_byte = (DA9055_VDD_IO | DA9055_PUSH_PULL)
+ << DA9055_PORT_SHIFT(offset);
+
+ ret = da9055_reg_update(gpio->da9055, (offset >> 1) +
+ DA9055_REG_GPIO0_1,
+ DA9055_PORT_MASK <<
+ DA9055_PORT_SHIFT(offset),
+ reg_byte);
+ if (ret < 0)
+ return ret;
+
+ da9055_gpio_set(gc, offset, value);
+
+ return 0;
+}
+
+static int da9055_gpio_to_irq(struct gpio_chip *gc, u32 offset)
+{
+ struct da9055_gpio *gpio = to_da9055_gpio(gc);
+ struct da9055 *da9055 = gpio->da9055;
+
+ return regmap_irq_get_virq(da9055->irq_data,
+ DA9055_IRQ_GPI0 + offset);
+}
+
+static struct gpio_chip reference_gp = {
+ .label = "da9055-gpio",
+ .owner = THIS_MODULE,
+ .get = da9055_gpio_get,
+ .set = da9055_gpio_set,
+ .direction_input = da9055_gpio_direction_input,
+ .direction_output = da9055_gpio_direction_output,
+ .to_irq = da9055_gpio_to_irq,
+ .can_sleep = true,
+ .ngpio = 3,
+ .base = -1,
+};
+
+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)
+ return -ENOMEM;
+
+ gpio->da9055 = dev_get_drvdata(pdev->dev.parent);
+ pdata = dev_get_platdata(gpio->da9055->dev);
+
+ gpio->gp = reference_gp;
+ if (pdata && pdata->gpio_base)
+ gpio->gp.base = pdata->gpio_base;
+
+ ret = gpiochip_add(&gpio->gp);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ goto err_mem;
+ }
+
+ platform_set_drvdata(pdev, gpio);
+
+ return 0;
+
+err_mem:
+ return ret;
+}
+
+static int da9055_gpio_remove(struct platform_device *pdev)
+{
+ struct da9055_gpio *gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gpio->gp);
+ return 0;
+}
+
+static struct platform_driver da9055_gpio_driver = {
+ .probe = da9055_gpio_probe,
+ .remove = da9055_gpio_remove,
+ .driver = {
+ .name = "da9055-gpio",
+ },
+};
+
+static int __init da9055_gpio_init(void)
+{
+ return platform_driver_register(&da9055_gpio_driver);
+}
+subsys_initcall(da9055_gpio_init);
+
+static void __exit da9055_gpio_exit(void)
+{
+ platform_driver_unregister(&da9055_gpio_driver);
+}
+module_exit(da9055_gpio_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9055 GPIO Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-gpio");
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
new file mode 100644
index 000000000..c5e05c82d
--- /dev/null
+++ b/drivers/gpio/gpio-davinci.c
@@ -0,0 +1,629 @@
+/*
+ * TI DaVinci GPIO Support
+ *
+ * Copyright (c) 2006-2007 David Brownell
+ * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/gpio-davinci.h>
+#include <linux/irqchip/chained_irq.h>
+
+struct davinci_gpio_regs {
+ u32 dir;
+ u32 out_data;
+ u32 set_data;
+ u32 clr_data;
+ u32 in_data;
+ u32 set_rising;
+ u32 clr_rising;
+ u32 set_falling;
+ u32 clr_falling;
+ u32 intstat;
+};
+
+typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq);
+
+#define BINTEN 0x8 /* GPIO Interrupt Per-Bank Enable Register */
+
+#define chip2controller(chip) \
+ container_of(chip, struct davinci_gpio_controller, chip)
+
+static void __iomem *gpio_base;
+
+static struct davinci_gpio_regs __iomem *gpio2regs(unsigned gpio)
+{
+ void __iomem *ptr;
+
+ if (gpio < 32 * 1)
+ ptr = gpio_base + 0x10;
+ else if (gpio < 32 * 2)
+ ptr = gpio_base + 0x38;
+ else if (gpio < 32 * 3)
+ ptr = gpio_base + 0x60;
+ else if (gpio < 32 * 4)
+ ptr = gpio_base + 0x88;
+ else if (gpio < 32 * 5)
+ ptr = gpio_base + 0xb0;
+ else
+ ptr = NULL;
+ return ptr;
+}
+
+static inline struct davinci_gpio_regs __iomem *irq2regs(int irq)
+{
+ struct davinci_gpio_regs __iomem *g;
+
+ g = (__force struct davinci_gpio_regs __iomem *)irq_get_chip_data(irq);
+
+ return g;
+}
+
+static int davinci_gpio_irq_setup(struct platform_device *pdev);
+
+/*--------------------------------------------------------------------------*/
+
+/* board setup code *MUST* setup pinmux and enable the GPIO clock. */
+static inline int __davinci_direction(struct gpio_chip *chip,
+ unsigned offset, bool out, int value)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+ unsigned long flags;
+ u32 temp;
+ u32 mask = 1 << offset;
+
+ spin_lock_irqsave(&d->lock, flags);
+ temp = readl_relaxed(&g->dir);
+ if (out) {
+ temp &= ~mask;
+ writel_relaxed(mask, value ? &g->set_data : &g->clr_data);
+ } else {
+ temp |= mask;
+ }
+ writel_relaxed(temp, &g->dir);
+ spin_unlock_irqrestore(&d->lock, flags);
+
+ return 0;
+}
+
+static int davinci_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ return __davinci_direction(chip, offset, false, 0);
+}
+
+static int
+davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ return __davinci_direction(chip, offset, true, value);
+}
+
+/*
+ * Read the pin's value (works even if it's set up as output);
+ * returns zero/nonzero.
+ *
+ * Note that changes are synched to the GPIO clock, so reading values back
+ * right after you've set them may give old values.
+ */
+static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+
+ return (1 << offset) & readl_relaxed(&g->in_data);
+}
+
+/*
+ * Assuming the pin is muxed as a gpio output, set its output value.
+ */
+static void
+davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+
+ writel_relaxed((1 << offset), value ? &g->set_data : &g->clr_data);
+}
+
+static struct davinci_gpio_platform_data *
+davinci_gpio_get_pdata(struct platform_device *pdev)
+{
+ struct device_node *dn = pdev->dev.of_node;
+ struct davinci_gpio_platform_data *pdata;
+ int ret;
+ u32 val;
+
+ if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
+ return pdev->dev.platform_data;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ ret = of_property_read_u32(dn, "ti,ngpio", &val);
+ if (ret)
+ goto of_err;
+
+ pdata->ngpio = val;
+
+ ret = of_property_read_u32(dn, "ti,davinci-gpio-unbanked", &val);
+ if (ret)
+ goto of_err;
+
+ pdata->gpio_unbanked = val;
+
+ return pdata;
+
+of_err:
+ dev_err(&pdev->dev, "Populating pdata from DT failed: err %d\n", ret);
+ return NULL;
+}
+
+#ifdef CONFIG_OF_GPIO
+static int davinci_gpio_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ struct davinci_gpio_controller *chips = dev_get_drvdata(gc->dev);
+ struct davinci_gpio_platform_data *pdata = dev_get_platdata(gc->dev);
+
+ if (gpiospec->args[0] > pdata->ngpio)
+ return -EINVAL;
+
+ if (gc != &chips[gpiospec->args[0] / 32].chip)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return gpiospec->args[0] % 32;
+}
+#endif
+
+static int davinci_gpio_probe(struct platform_device *pdev)
+{
+ int i, base;
+ unsigned ngpio;
+ struct davinci_gpio_controller *chips;
+ struct davinci_gpio_platform_data *pdata;
+ struct davinci_gpio_regs __iomem *regs;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+
+ pdata = davinci_gpio_get_pdata(pdev);
+ if (!pdata) {
+ dev_err(dev, "No platform data found\n");
+ return -EINVAL;
+ }
+
+ dev->platform_data = pdata;
+
+ /*
+ * The gpio banks conceptually expose a segmented bitmap,
+ * and "ngpio" is one more than the largest zero-based
+ * bit index that's valid.
+ */
+ ngpio = pdata->ngpio;
+ if (ngpio == 0) {
+ dev_err(dev, "How many GPIOs?\n");
+ return -EINVAL;
+ }
+
+ if (WARN_ON(ARCH_NR_GPIOS < ngpio))
+ ngpio = ARCH_NR_GPIOS;
+
+ chips = devm_kzalloc(dev,
+ ngpio * sizeof(struct davinci_gpio_controller),
+ GFP_KERNEL);
+ if (!chips)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gpio_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(gpio_base))
+ return PTR_ERR(gpio_base);
+
+ for (i = 0, base = 0; base < ngpio; i++, base += 32) {
+ chips[i].chip.label = "DaVinci";
+
+ chips[i].chip.direction_input = davinci_direction_in;
+ chips[i].chip.get = davinci_gpio_get;
+ chips[i].chip.direction_output = davinci_direction_out;
+ chips[i].chip.set = davinci_gpio_set;
+
+ chips[i].chip.base = base;
+ chips[i].chip.ngpio = ngpio - base;
+ if (chips[i].chip.ngpio > 32)
+ chips[i].chip.ngpio = 32;
+
+#ifdef CONFIG_OF_GPIO
+ chips[i].chip.of_gpio_n_cells = 2;
+ chips[i].chip.of_xlate = davinci_gpio_of_xlate;
+ chips[i].chip.dev = dev;
+ chips[i].chip.of_node = dev->of_node;
+#endif
+ spin_lock_init(&chips[i].lock);
+
+ regs = gpio2regs(base);
+ chips[i].regs = regs;
+ chips[i].set_data = &regs->set_data;
+ chips[i].clr_data = &regs->clr_data;
+ chips[i].in_data = &regs->in_data;
+
+ gpiochip_add(&chips[i].chip);
+ }
+
+ platform_set_drvdata(pdev, chips);
+ davinci_gpio_irq_setup(pdev);
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+/*
+ * We expect irqs will normally be set up as input pins, but they can also be
+ * used as output pins ... which is convenient for testing.
+ *
+ * NOTE: The first few GPIOs also have direct INTC hookups in addition
+ * to their GPIOBNK0 irq, with a bit less overhead.
+ *
+ * All those INTC hookups (direct, plus several IRQ banks) can also
+ * serve as EDMA event triggers.
+ */
+
+static void gpio_irq_disable(struct irq_data *d)
+{
+ struct davinci_gpio_regs __iomem *g = irq2regs(d->irq);
+ u32 mask = (u32) irq_data_get_irq_handler_data(d);
+
+ writel_relaxed(mask, &g->clr_falling);
+ writel_relaxed(mask, &g->clr_rising);
+}
+
+static void gpio_irq_enable(struct irq_data *d)
+{
+ struct davinci_gpio_regs __iomem *g = irq2regs(d->irq);
+ u32 mask = (u32) irq_data_get_irq_handler_data(d);
+ unsigned status = irqd_get_trigger_type(d);
+
+ status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
+ if (!status)
+ status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
+
+ if (status & IRQ_TYPE_EDGE_FALLING)
+ writel_relaxed(mask, &g->set_falling);
+ if (status & IRQ_TYPE_EDGE_RISING)
+ writel_relaxed(mask, &g->set_rising);
+}
+
+static int gpio_irq_type(struct irq_data *d, unsigned trigger)
+{
+ if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct irq_chip gpio_irqchip = {
+ .name = "GPIO",
+ .irq_enable = gpio_irq_enable,
+ .irq_disable = gpio_irq_disable,
+ .irq_set_type = gpio_irq_type,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static void
+gpio_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ struct davinci_gpio_regs __iomem *g;
+ u32 mask = 0xffff;
+ struct davinci_gpio_controller *d;
+
+ d = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc);
+ g = (struct davinci_gpio_regs __iomem *)d->regs;
+
+ /* we only care about one bank */
+ if (irq & 1)
+ mask <<= 16;
+
+ /* temporarily mask (level sensitive) parent IRQ */
+ chained_irq_enter(irq_desc_get_chip(desc), desc);
+ while (1) {
+ u32 status;
+ int bit;
+
+ /* ack any irqs */
+ status = readl_relaxed(&g->intstat) & mask;
+ if (!status)
+ break;
+ writel_relaxed(status, &g->intstat);
+
+ /* now demux them to the right lowlevel handler */
+
+ while (status) {
+ bit = __ffs(status);
+ status &= ~BIT(bit);
+ generic_handle_irq(
+ irq_find_mapping(d->irq_domain,
+ d->chip.base + bit));
+ }
+ }
+ chained_irq_exit(irq_desc_get_chip(desc), desc);
+ /* now it may re-trigger */
+}
+
+static int gpio_to_irq_banked(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+
+ if (d->irq_domain)
+ return irq_create_mapping(d->irq_domain, d->chip.base + offset);
+ else
+ return -ENXIO;
+}
+
+static int gpio_to_irq_unbanked(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+
+ /*
+ * NOTE: we assume for now that only irqs in the first gpio_chip
+ * can provide direct-mapped IRQs to AINTC (up to 32 GPIOs).
+ */
+ if (offset < d->gpio_unbanked)
+ return d->gpio_irq + offset;
+ else
+ return -ENODEV;
+}
+
+static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger)
+{
+ struct davinci_gpio_controller *d;
+ struct davinci_gpio_regs __iomem *g;
+ u32 mask;
+
+ d = (struct davinci_gpio_controller *)data->handler_data;
+ g = (struct davinci_gpio_regs __iomem *)d->regs;
+ mask = __gpio_mask(data->irq - d->gpio_irq);
+
+ if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ return -EINVAL;
+
+ writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_FALLING)
+ ? &g->set_falling : &g->clr_falling);
+ writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_RISING)
+ ? &g->set_rising : &g->clr_rising);
+
+ return 0;
+}
+
+static int
+davinci_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct davinci_gpio_regs __iomem *g = gpio2regs(hw);
+
+ irq_set_chip_and_handler_name(irq, &gpio_irqchip, handle_simple_irq,
+ "davinci_gpio");
+ irq_set_irq_type(irq, IRQ_TYPE_NONE);
+ irq_set_chip_data(irq, (__force void *)g);
+ irq_set_handler_data(irq, (void *)__gpio_mask(hw));
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static const struct irq_domain_ops davinci_gpio_irq_ops = {
+ .map = davinci_gpio_irq_map,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+static struct irq_chip *davinci_gpio_get_irq_chip(unsigned int irq)
+{
+ static struct irq_chip_type gpio_unbanked;
+
+ gpio_unbanked = *container_of(irq_get_chip(irq),
+ struct irq_chip_type, chip);
+
+ return &gpio_unbanked.chip;
+};
+
+static struct irq_chip *keystone_gpio_get_irq_chip(unsigned int irq)
+{
+ static struct irq_chip gpio_unbanked;
+
+ gpio_unbanked = *irq_get_chip(irq);
+ return &gpio_unbanked;
+};
+
+static const struct of_device_id davinci_gpio_ids[];
+
+/*
+ * NOTE: for suspend/resume, probably best to make a platform_device with
+ * suspend_late/resume_resume calls hooking into results of the set_wake()
+ * calls ... so if no gpios are wakeup events the clock can be disabled,
+ * with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0
+ * (dm6446) can be set appropriately for GPIOV33 pins.
+ */
+
+static int davinci_gpio_irq_setup(struct platform_device *pdev)
+{
+ unsigned gpio, bank;
+ int irq;
+ struct clk *clk;
+ u32 binten = 0;
+ unsigned ngpio, bank_irq;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct davinci_gpio_controller *chips = platform_get_drvdata(pdev);
+ struct davinci_gpio_platform_data *pdata = dev->platform_data;
+ struct davinci_gpio_regs __iomem *g;
+ struct irq_domain *irq_domain = NULL;
+ const struct of_device_id *match;
+ struct irq_chip *irq_chip;
+ gpio_get_irq_chip_cb_t gpio_get_irq_chip;
+
+ /*
+ * Use davinci_gpio_get_irq_chip by default to handle non DT cases
+ */
+ gpio_get_irq_chip = davinci_gpio_get_irq_chip;
+ match = of_match_device(of_match_ptr(davinci_gpio_ids),
+ dev);
+ if (match)
+ gpio_get_irq_chip = (gpio_get_irq_chip_cb_t)match->data;
+
+ ngpio = pdata->ngpio;
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "Invalid IRQ resource\n");
+ return -EBUSY;
+ }
+
+ bank_irq = res->start;
+
+ if (!bank_irq) {
+ dev_err(dev, "Invalid IRQ resource\n");
+ return -ENODEV;
+ }
+
+ clk = devm_clk_get(dev, "gpio");
+ if (IS_ERR(clk)) {
+ printk(KERN_ERR "Error %ld getting gpio clock?\n",
+ PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+ clk_prepare_enable(clk);
+
+ if (!pdata->gpio_unbanked) {
+ irq = irq_alloc_descs(-1, 0, ngpio, 0);
+ if (irq < 0) {
+ dev_err(dev, "Couldn't allocate IRQ numbers\n");
+ return irq;
+ }
+
+ irq_domain = irq_domain_add_legacy(NULL, ngpio, irq, 0,
+ &davinci_gpio_irq_ops,
+ chips);
+ if (!irq_domain) {
+ dev_err(dev, "Couldn't register an IRQ domain\n");
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Arrange gpio_to_irq() support, handling either direct IRQs or
+ * banked IRQs. Having GPIOs in the first GPIO bank use direct
+ * IRQs, while the others use banked IRQs, would need some setup
+ * tweaks to recognize hardware which can do that.
+ */
+ for (gpio = 0, bank = 0; gpio < ngpio; bank++, gpio += 32) {
+ chips[bank].chip.to_irq = gpio_to_irq_banked;
+ chips[bank].irq_domain = irq_domain;
+ }
+
+ /*
+ * AINTC can handle direct/unbanked IRQs for GPIOs, with the GPIO
+ * controller only handling trigger modes. We currently assume no
+ * IRQ mux conflicts; gpio_irq_type_unbanked() is only for GPIOs.
+ */
+ if (pdata->gpio_unbanked) {
+ /* pass "bank 0" GPIO IRQs to AINTC */
+ chips[0].chip.to_irq = gpio_to_irq_unbanked;
+ chips[0].gpio_irq = bank_irq;
+ chips[0].gpio_unbanked = pdata->gpio_unbanked;
+ binten = BIT(0);
+
+ /* AINTC handles mask/unmask; GPIO handles triggering */
+ irq = bank_irq;
+ irq_chip = gpio_get_irq_chip(irq);
+ irq_chip->name = "GPIO-AINTC";
+ irq_chip->irq_set_type = gpio_irq_type_unbanked;
+
+ /* default trigger: both edges */
+ g = gpio2regs(0);
+ writel_relaxed(~0, &g->set_falling);
+ writel_relaxed(~0, &g->set_rising);
+
+ /* set the direct IRQs up to use that irqchip */
+ for (gpio = 0; gpio < pdata->gpio_unbanked; gpio++, irq++) {
+ irq_set_chip(irq, irq_chip);
+ irq_set_handler_data(irq, &chips[gpio / 32]);
+ irq_set_status_flags(irq, IRQ_TYPE_EDGE_BOTH);
+ }
+
+ goto done;
+ }
+
+ /*
+ * Or, AINTC can handle IRQs for banks of 16 GPIO IRQs, which we
+ * then chain through our own handler.
+ */
+ for (gpio = 0, bank = 0; gpio < ngpio; bank++, bank_irq++, gpio += 16) {
+ /* disabled by default, enabled only as needed */
+ g = gpio2regs(gpio);
+ writel_relaxed(~0, &g->clr_falling);
+ writel_relaxed(~0, &g->clr_rising);
+
+ /* set up all irqs in this bank */
+ irq_set_chained_handler(bank_irq, gpio_irq_handler);
+
+ /*
+ * Each chip handles 32 gpios, and each irq bank consists of 16
+ * gpio irqs. Pass the irq bank's corresponding controller to
+ * the chained irq handler.
+ */
+ irq_set_handler_data(bank_irq, &chips[gpio / 32]);
+
+ binten |= BIT(bank);
+ }
+
+done:
+ /*
+ * BINTEN -- per-bank interrupt enable. genirq would also let these
+ * bits be set/cleared dynamically.
+ */
+ writel_relaxed(binten, gpio_base + BINTEN);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id davinci_gpio_ids[] = {
+ { .compatible = "ti,keystone-gpio", keystone_gpio_get_irq_chip},
+ { .compatible = "ti,dm6441-gpio", davinci_gpio_get_irq_chip},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, davinci_gpio_ids);
+#endif
+
+static struct platform_driver davinci_gpio_driver = {
+ .probe = davinci_gpio_probe,
+ .driver = {
+ .name = "davinci_gpio",
+ .of_match_table = of_match_ptr(davinci_gpio_ids),
+ },
+};
+
+/**
+ * GPIO driver registration needs to be done before machine_init functions
+ * access GPIO. Hence davinci_gpio_drv_reg() is a postcore_initcall.
+ */
+static int __init davinci_gpio_drv_reg(void)
+{
+ return platform_driver_register(&davinci_gpio_driver);
+}
+postcore_initcall(davinci_gpio_drv_reg);
diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c
new file mode 100644
index 000000000..dbdb4de82
--- /dev/null
+++ b/drivers/gpio/gpio-dln2.c
@@ -0,0 +1,532 @@
+/*
+ * Driver for the Diolan DLN-2 USB-GPIO adapter
+ *
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#define DLN2_GPIO_ID 0x01
+
+#define DLN2_GPIO_GET_PIN_COUNT DLN2_CMD(0x01, DLN2_GPIO_ID)
+#define DLN2_GPIO_SET_DEBOUNCE DLN2_CMD(0x04, DLN2_GPIO_ID)
+#define DLN2_GPIO_GET_DEBOUNCE DLN2_CMD(0x05, DLN2_GPIO_ID)
+#define DLN2_GPIO_PORT_GET_VAL DLN2_CMD(0x06, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_GET_VAL DLN2_CMD(0x0B, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_SET_OUT_VAL DLN2_CMD(0x0C, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_GET_OUT_VAL DLN2_CMD(0x0D, DLN2_GPIO_ID)
+#define DLN2_GPIO_CONDITION_MET_EV DLN2_CMD(0x0F, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_ENABLE DLN2_CMD(0x10, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_DISABLE DLN2_CMD(0x11, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_SET_DIRECTION DLN2_CMD(0x13, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_GET_DIRECTION DLN2_CMD(0x14, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_SET_EVENT_CFG DLN2_CMD(0x1E, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_GET_EVENT_CFG DLN2_CMD(0x1F, DLN2_GPIO_ID)
+
+#define DLN2_GPIO_EVENT_NONE 0
+#define DLN2_GPIO_EVENT_CHANGE 1
+#define DLN2_GPIO_EVENT_LVL_HIGH 2
+#define DLN2_GPIO_EVENT_LVL_LOW 3
+#define DLN2_GPIO_EVENT_CHANGE_RISING 0x11
+#define DLN2_GPIO_EVENT_CHANGE_FALLING 0x21
+#define DLN2_GPIO_EVENT_MASK 0x0F
+
+#define DLN2_GPIO_MAX_PINS 32
+
+struct dln2_gpio {
+ struct platform_device *pdev;
+ struct gpio_chip gpio;
+
+ /*
+ * Cache pin direction to save us one transfer, since the hardware has
+ * separate commands to read the in and out values.
+ */
+ DECLARE_BITMAP(output_enabled, DLN2_GPIO_MAX_PINS);
+
+ /* active IRQs - not synced to hardware */
+ DECLARE_BITMAP(unmasked_irqs, DLN2_GPIO_MAX_PINS);
+ /* active IRQS - synced to hardware */
+ DECLARE_BITMAP(enabled_irqs, DLN2_GPIO_MAX_PINS);
+ int irq_type[DLN2_GPIO_MAX_PINS];
+ struct mutex irq_lock;
+};
+
+struct dln2_gpio_pin {
+ __le16 pin;
+};
+
+struct dln2_gpio_pin_val {
+ __le16 pin __packed;
+ u8 value;
+};
+
+static int dln2_gpio_get_pin_count(struct platform_device *pdev)
+{
+ int ret;
+ __le16 count;
+ int len = sizeof(count);
+
+ ret = dln2_transfer_rx(pdev, DLN2_GPIO_GET_PIN_COUNT, &count, &len);
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(count))
+ return -EPROTO;
+
+ return le16_to_cpu(count);
+}
+
+static int dln2_gpio_pin_cmd(struct dln2_gpio *dln2, int cmd, unsigned pin)
+{
+ struct dln2_gpio_pin req = {
+ .pin = cpu_to_le16(pin),
+ };
+
+ return dln2_transfer_tx(dln2->pdev, cmd, &req, sizeof(req));
+}
+
+static int dln2_gpio_pin_val(struct dln2_gpio *dln2, int cmd, unsigned int pin)
+{
+ int ret;
+ struct dln2_gpio_pin req = {
+ .pin = cpu_to_le16(pin),
+ };
+ struct dln2_gpio_pin_val rsp;
+ int len = sizeof(rsp);
+
+ ret = dln2_transfer(dln2->pdev, cmd, &req, sizeof(req), &rsp, &len);
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(rsp) || req.pin != rsp.pin)
+ return -EPROTO;
+
+ return rsp.value;
+}
+
+static int dln2_gpio_pin_get_in_val(struct dln2_gpio *dln2, unsigned int pin)
+{
+ int ret;
+
+ ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_VAL, pin);
+ if (ret < 0)
+ return ret;
+ return !!ret;
+}
+
+static int dln2_gpio_pin_get_out_val(struct dln2_gpio *dln2, unsigned int pin)
+{
+ int ret;
+
+ ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_OUT_VAL, pin);
+ if (ret < 0)
+ return ret;
+ return !!ret;
+}
+
+static int dln2_gpio_pin_set_out_val(struct dln2_gpio *dln2,
+ unsigned int pin, int value)
+{
+ struct dln2_gpio_pin_val req = {
+ .pin = cpu_to_le16(pin),
+ .value = value,
+ };
+
+ return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_OUT_VAL, &req,
+ sizeof(req));
+}
+
+#define DLN2_GPIO_DIRECTION_IN 0
+#define DLN2_GPIO_DIRECTION_OUT 1
+
+static int dln2_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ struct dln2_gpio_pin req = {
+ .pin = cpu_to_le16(offset),
+ };
+ struct dln2_gpio_pin_val rsp;
+ int len = sizeof(rsp);
+ int ret;
+
+ ret = dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_ENABLE, offset);
+ if (ret < 0)
+ return ret;
+
+ /* cache the pin direction */
+ ret = dln2_transfer(dln2->pdev, DLN2_GPIO_PIN_GET_DIRECTION,
+ &req, sizeof(req), &rsp, &len);
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(rsp) || req.pin != rsp.pin) {
+ ret = -EPROTO;
+ goto out_disable;
+ }
+
+ switch (rsp.value) {
+ case DLN2_GPIO_DIRECTION_IN:
+ clear_bit(offset, dln2->output_enabled);
+ return 0;
+ case DLN2_GPIO_DIRECTION_OUT:
+ set_bit(offset, dln2->output_enabled);
+ return 0;
+ default:
+ ret = -EPROTO;
+ goto out_disable;
+ }
+
+out_disable:
+ dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset);
+ return ret;
+}
+
+static void dln2_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+
+ dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset);
+}
+
+static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+
+ if (test_bit(offset, dln2->output_enabled))
+ return GPIOF_DIR_OUT;
+
+ return GPIOF_DIR_IN;
+}
+
+static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ int dir;
+
+ dir = dln2_gpio_get_direction(chip, offset);
+ if (dir < 0)
+ return dir;
+
+ if (dir == GPIOF_DIR_IN)
+ return dln2_gpio_pin_get_in_val(dln2, offset);
+
+ return dln2_gpio_pin_get_out_val(dln2, offset);
+}
+
+static void dln2_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+
+ dln2_gpio_pin_set_out_val(dln2, offset, value);
+}
+
+static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset,
+ unsigned dir)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ struct dln2_gpio_pin_val req = {
+ .pin = cpu_to_le16(offset),
+ .value = dir,
+ };
+ int ret;
+
+ ret = dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_DIRECTION,
+ &req, sizeof(req));
+ if (ret < 0)
+ return ret;
+
+ if (dir == DLN2_GPIO_DIRECTION_OUT)
+ set_bit(offset, dln2->output_enabled);
+ else
+ clear_bit(offset, dln2->output_enabled);
+
+ return ret;
+}
+
+static int dln2_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_IN);
+}
+
+static int dln2_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ int ret;
+
+ ret = dln2_gpio_pin_set_out_val(dln2, offset, value);
+ if (ret < 0)
+ return ret;
+
+ return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_OUT);
+}
+
+static int dln2_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
+ unsigned debounce)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ __le32 duration = cpu_to_le32(debounce);
+
+ return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_SET_DEBOUNCE,
+ &duration, sizeof(duration));
+}
+
+static int dln2_gpio_set_event_cfg(struct dln2_gpio *dln2, unsigned pin,
+ unsigned type, unsigned period)
+{
+ struct {
+ __le16 pin;
+ u8 type;
+ __le16 period;
+ } __packed req = {
+ .pin = cpu_to_le16(pin),
+ .type = type,
+ .period = cpu_to_le16(period),
+ };
+
+ return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_EVENT_CFG,
+ &req, sizeof(req));
+}
+
+static void dln2_irq_unmask(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ int pin = irqd_to_hwirq(irqd);
+
+ set_bit(pin, dln2->unmasked_irqs);
+}
+
+static void dln2_irq_mask(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ int pin = irqd_to_hwirq(irqd);
+
+ clear_bit(pin, dln2->unmasked_irqs);
+}
+
+static int dln2_irq_set_type(struct irq_data *irqd, unsigned type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ int pin = irqd_to_hwirq(irqd);
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ dln2->irq_type[pin] = DLN2_GPIO_EVENT_LVL_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ dln2->irq_type[pin] = DLN2_GPIO_EVENT_LVL_LOW;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ dln2->irq_type[pin] = DLN2_GPIO_EVENT_CHANGE;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ dln2->irq_type[pin] = DLN2_GPIO_EVENT_CHANGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ dln2->irq_type[pin] = DLN2_GPIO_EVENT_CHANGE_FALLING;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void dln2_irq_bus_lock(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+
+ mutex_lock(&dln2->irq_lock);
+}
+
+static void dln2_irq_bus_unlock(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ int pin = irqd_to_hwirq(irqd);
+ int enabled, unmasked;
+ unsigned type;
+ int ret;
+
+ enabled = test_bit(pin, dln2->enabled_irqs);
+ unmasked = test_bit(pin, dln2->unmasked_irqs);
+
+ if (enabled != unmasked) {
+ if (unmasked) {
+ type = dln2->irq_type[pin] & DLN2_GPIO_EVENT_MASK;
+ set_bit(pin, dln2->enabled_irqs);
+ } else {
+ type = DLN2_GPIO_EVENT_NONE;
+ clear_bit(pin, dln2->enabled_irqs);
+ }
+
+ ret = dln2_gpio_set_event_cfg(dln2, pin, type, 0);
+ if (ret)
+ dev_err(dln2->gpio.dev, "failed to set event\n");
+ }
+
+ mutex_unlock(&dln2->irq_lock);
+}
+
+static struct irq_chip dln2_gpio_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,
+};
+
+static void dln2_gpio_event(struct platform_device *pdev, u16 echo,
+ const void *data, int len)
+{
+ int pin, irq;
+
+ const struct {
+ __le16 count;
+ __u8 type;
+ __le16 pin;
+ __u8 value;
+ } __packed *event = data;
+ struct dln2_gpio *dln2 = platform_get_drvdata(pdev);
+
+ if (len < sizeof(*event)) {
+ dev_err(dln2->gpio.dev, "short event message\n");
+ return;
+ }
+
+ pin = le16_to_cpu(event->pin);
+ if (pin >= dln2->gpio.ngpio) {
+ dev_err(dln2->gpio.dev, "out of bounds pin %d\n", pin);
+ return;
+ }
+
+ irq = irq_find_mapping(dln2->gpio.irqdomain, pin);
+ if (!irq) {
+ dev_err(dln2->gpio.dev, "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);
+ break;
+ case DLN2_GPIO_EVENT_CHANGE_FALLING:
+ if (!event->value)
+ generic_handle_irq(irq);
+ break;
+ default:
+ generic_handle_irq(irq);
+ }
+}
+
+static int dln2_gpio_probe(struct platform_device *pdev)
+{
+ struct dln2_gpio *dln2;
+ struct device *dev = &pdev->dev;
+ int pins;
+ int ret;
+
+ pins = dln2_gpio_get_pin_count(pdev);
+ if (pins < 0) {
+ dev_err(dev, "failed to get pin count: %d\n", pins);
+ return pins;
+ }
+ if (pins > DLN2_GPIO_MAX_PINS) {
+ pins = DLN2_GPIO_MAX_PINS;
+ dev_warn(dev, "clamping pins to %d\n", DLN2_GPIO_MAX_PINS);
+ }
+
+ dln2 = devm_kzalloc(&pdev->dev, sizeof(*dln2), GFP_KERNEL);
+ if (!dln2)
+ return -ENOMEM;
+
+ mutex_init(&dln2->irq_lock);
+
+ dln2->pdev = pdev;
+
+ dln2->gpio.label = "dln2";
+ dln2->gpio.dev = dev;
+ dln2->gpio.owner = THIS_MODULE;
+ dln2->gpio.base = -1;
+ dln2->gpio.ngpio = pins;
+ dln2->gpio.exported = true;
+ dln2->gpio.can_sleep = true;
+ dln2->gpio.irq_not_threaded = true;
+ dln2->gpio.set = dln2_gpio_set;
+ dln2->gpio.get = dln2_gpio_get;
+ dln2->gpio.request = dln2_gpio_request;
+ dln2->gpio.free = dln2_gpio_free;
+ dln2->gpio.get_direction = dln2_gpio_get_direction;
+ dln2->gpio.direction_input = dln2_gpio_direction_input;
+ dln2->gpio.direction_output = dln2_gpio_direction_output;
+ dln2->gpio.set_debounce = dln2_gpio_set_debounce;
+
+ platform_set_drvdata(pdev, dln2);
+
+ ret = gpiochip_add(&dln2->gpio);
+ if (ret < 0) {
+ dev_err(dev, "failed to add gpio chip: %d\n", ret);
+ goto out;
+ }
+
+ 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);
+ goto out_gpiochip_remove;
+ }
+
+ ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV,
+ dln2_gpio_event);
+ if (ret) {
+ dev_err(dev, "failed to register event cb: %d\n", ret);
+ goto out_gpiochip_remove;
+ }
+
+ return 0;
+
+out_gpiochip_remove:
+ gpiochip_remove(&dln2->gpio);
+out:
+ return ret;
+}
+
+static int dln2_gpio_remove(struct platform_device *pdev)
+{
+ struct dln2_gpio *dln2 = platform_get_drvdata(pdev);
+
+ dln2_unregister_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV);
+ gpiochip_remove(&dln2->gpio);
+
+ return 0;
+}
+
+static struct platform_driver dln2_gpio_driver = {
+ .driver.name = "dln2-gpio",
+ .probe = dln2_gpio_probe,
+ .remove = dln2_gpio_remove,
+};
+
+module_platform_driver(dln2_gpio_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 GPIO interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-gpio");
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
new file mode 100644
index 000000000..58faf04fc
--- /dev/null
+++ b/drivers/gpio/gpio-dwapb.c
@@ -0,0 +1,692 @@
+/*
+ * Copyright (c) 2011 Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support@picochip.com
+ */
+#include <linux/basic_mmio_gpio.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/platform_data/gpio-dwapb.h>
+#include <linux/slab.h>
+
+#define GPIO_SWPORTA_DR 0x00
+#define GPIO_SWPORTA_DDR 0x04
+#define GPIO_SWPORTB_DR 0x0c
+#define GPIO_SWPORTB_DDR 0x10
+#define GPIO_SWPORTC_DR 0x18
+#define GPIO_SWPORTC_DDR 0x1c
+#define GPIO_SWPORTD_DR 0x24
+#define GPIO_SWPORTD_DDR 0x28
+#define GPIO_INTEN 0x30
+#define GPIO_INTMASK 0x34
+#define GPIO_INTTYPE_LEVEL 0x38
+#define GPIO_INT_POLARITY 0x3c
+#define GPIO_INTSTATUS 0x40
+#define GPIO_PORTA_DEBOUNCE 0x48
+#define GPIO_PORTA_EOI 0x4c
+#define GPIO_EXT_PORTA 0x50
+#define GPIO_EXT_PORTB 0x54
+#define GPIO_EXT_PORTC 0x58
+#define GPIO_EXT_PORTD 0x5c
+
+#define DWAPB_MAX_PORTS 4
+#define GPIO_EXT_PORT_SIZE (GPIO_EXT_PORTB - GPIO_EXT_PORTA)
+#define GPIO_SWPORT_DR_SIZE (GPIO_SWPORTB_DR - GPIO_SWPORTA_DR)
+#define GPIO_SWPORT_DDR_SIZE (GPIO_SWPORTB_DDR - GPIO_SWPORTA_DDR)
+
+struct dwapb_gpio;
+
+#ifdef CONFIG_PM_SLEEP
+/* Store GPIO context across system-wide suspend/resume transitions */
+struct dwapb_context {
+ u32 data;
+ u32 dir;
+ u32 ext;
+ u32 int_en;
+ u32 int_mask;
+ u32 int_type;
+ u32 int_pol;
+ u32 int_deb;
+};
+#endif
+
+struct dwapb_gpio_port {
+ struct bgpio_chip bgc;
+ bool is_registered;
+ struct dwapb_gpio *gpio;
+#ifdef CONFIG_PM_SLEEP
+ struct dwapb_context *ctx;
+#endif
+ unsigned int idx;
+};
+
+struct dwapb_gpio {
+ struct device *dev;
+ void __iomem *regs;
+ struct dwapb_gpio_port *ports;
+ unsigned int nr_ports;
+ struct irq_domain *domain;
+};
+
+static inline struct dwapb_gpio_port *
+to_dwapb_gpio_port(struct bgpio_chip *bgc)
+{
+ return container_of(bgc, struct dwapb_gpio_port, bgc);
+}
+
+static inline u32 dwapb_read(struct dwapb_gpio *gpio, unsigned int offset)
+{
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ void __iomem *reg_base = gpio->regs;
+
+ return bgc->read_reg(reg_base + offset);
+}
+
+static inline void dwapb_write(struct dwapb_gpio *gpio, unsigned int offset,
+ u32 val)
+{
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ void __iomem *reg_base = gpio->regs;
+
+ bgc->write_reg(reg_base + offset, val);
+}
+
+static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct dwapb_gpio_port *port = to_dwapb_gpio_port(bgc);
+ struct dwapb_gpio *gpio = port->gpio;
+
+ return irq_find_mapping(gpio->domain, offset);
+}
+
+static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
+{
+ u32 v = dwapb_read(gpio, GPIO_INT_POLARITY);
+
+ if (gpio_get_value(gpio->ports[0].bgc.gc.base + offs))
+ v &= ~BIT(offs);
+ else
+ v |= BIT(offs);
+
+ dwapb_write(gpio, GPIO_INT_POLARITY, v);
+}
+
+static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
+{
+ u32 irq_status = readl_relaxed(gpio->regs + GPIO_INTSTATUS);
+ u32 ret = irq_status;
+
+ while (irq_status) {
+ int hwirq = fls(irq_status) - 1;
+ int gpio_irq = irq_find_mapping(gpio->domain, hwirq);
+
+ generic_handle_irq(gpio_irq);
+ irq_status &= ~BIT(hwirq);
+
+ if ((irq_get_trigger_type(gpio_irq) & IRQ_TYPE_SENSE_MASK)
+ == IRQ_TYPE_EDGE_BOTH)
+ dwapb_toggle_trigger(gpio, hwirq);
+ }
+
+ return ret;
+}
+
+static void dwapb_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ struct dwapb_gpio *gpio = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ dwapb_do_irq(gpio);
+
+ if (chip->irq_eoi)
+ chip->irq_eoi(irq_desc_get_irq_data(desc));
+}
+
+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 bgpio_chip *bgc = &gpio->ports[0].bgc;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+ val = dwapb_read(gpio, GPIO_INTEN);
+ val |= BIT(d->hwirq);
+ dwapb_write(gpio, GPIO_INTEN, val);
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+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 bgpio_chip *bgc = &gpio->ports[0].bgc;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+ val = dwapb_read(gpio, GPIO_INTEN);
+ val &= ~BIT(d->hwirq);
+ dwapb_write(gpio, GPIO_INTEN, val);
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static int dwapb_irq_reqres(struct irq_data *d)
+{
+ struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = igc->private;
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+
+ if (gpiochip_lock_as_irq(&bgc->gc, irqd_to_hwirq(d))) {
+ dev_err(gpio->dev, "unable to lock HW IRQ %lu for IRQ\n",
+ irqd_to_hwirq(d));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void dwapb_irq_relres(struct irq_data *d)
+{
+ struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = igc->private;
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+
+ gpiochip_unlock_as_irq(&bgc->gc, irqd_to_hwirq(d));
+}
+
+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 bgpio_chip *bgc = &gpio->ports[0].bgc;
+ int bit = d->hwirq;
+ 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(&bgc->lock, flags);
+ level = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
+ polarity = dwapb_read(gpio, GPIO_INT_POLARITY);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ level |= BIT(bit);
+ dwapb_toggle_trigger(gpio, bit);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ level |= BIT(bit);
+ polarity |= BIT(bit);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ level |= BIT(bit);
+ polarity &= ~BIT(bit);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ level &= ~BIT(bit);
+ polarity |= BIT(bit);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ level &= ~BIT(bit);
+ polarity &= ~BIT(bit);
+ break;
+ }
+
+ irq_setup_alt_chip(d, type);
+
+ dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level);
+ dwapb_write(gpio, GPIO_INT_POLARITY, polarity);
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int dwapb_gpio_set_debounce(struct gpio_chip *gc,
+ unsigned offset, unsigned debounce)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct dwapb_gpio_port *port = to_dwapb_gpio_port(bgc);
+ struct dwapb_gpio *gpio = port->gpio;
+ unsigned long flags, val_deb;
+ unsigned long mask = bgc->pin2mask(bgc, offset);
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ val_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
+ if (debounce)
+ dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb | mask);
+ else
+ dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb & ~mask);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id)
+{
+ u32 worked;
+ struct dwapb_gpio *gpio = dev_id;
+
+ worked = dwapb_do_irq(gpio);
+
+ return worked ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
+ struct dwapb_gpio_port *port,
+ struct dwapb_port_property *pp)
+{
+ struct gpio_chip *gc = &port->bgc.gc;
+ struct device_node *node = pp->node;
+ 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_add_linear(node, ngpio,
+ &irq_generic_chip_ops, gpio);
+ if (!gpio->domain)
+ return;
+
+ 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;
+ return;
+ }
+
+ irq_gc = irq_get_domain_generic_chip(gpio->domain, 0);
+ if (!irq_gc) {
+ irq_domain_remove(gpio->domain);
+ gpio->domain = NULL;
+ return;
+ }
+
+ 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;
+ ct->regs.ack = GPIO_PORTA_EOI;
+ ct->regs.mask = GPIO_INTMASK;
+ ct->type = IRQ_TYPE_LEVEL_MASK;
+ }
+
+ 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;
+
+ if (!pp->irq_shared) {
+ irq_set_chained_handler(pp->irq, dwapb_irq_handler);
+ irq_set_handler_data(pp->irq, 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,
+ dwapb_irq_handler_mfd,
+ IRQF_SHARED, "gpio-dwapb-mfd", gpio);
+ if (err) {
+ dev_err(gpio->dev, "error requesting IRQ\n");
+ irq_domain_remove(gpio->domain);
+ gpio->domain = NULL;
+ return;
+ }
+ }
+
+ for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
+ irq_create_mapping(gpio->domain, hwirq);
+
+ port->bgc.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->bgc.gc;
+ unsigned int ngpio = gc->ngpio;
+ irq_hw_number_t hwirq;
+
+ if (!gpio->domain)
+ return;
+
+ for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
+ irq_dispose_mapping(irq_find_mapping(gpio->domain, hwirq));
+
+ irq_domain_remove(gpio->domain);
+ gpio->domain = NULL;
+}
+
+static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
+ struct dwapb_port_property *pp,
+ unsigned int offs)
+{
+ struct dwapb_gpio_port *port;
+ void __iomem *dat, *set, *dirout;
+ int err;
+
+ port = &gpio->ports[offs];
+ port->gpio = gpio;
+ port->idx = pp->idx;
+
+#ifdef CONFIG_PM_SLEEP
+ port->ctx = devm_kzalloc(gpio->dev, sizeof(*port->ctx), GFP_KERNEL);
+ if (!port->ctx)
+ return -ENOMEM;
+#endif
+
+ dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE);
+ set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE);
+ dirout = gpio->regs + GPIO_SWPORTA_DDR +
+ (pp->idx * GPIO_SWPORT_DDR_SIZE);
+
+ err = bgpio_init(&port->bgc, gpio->dev, 4, dat, set, NULL, dirout,
+ NULL, false);
+ if (err) {
+ dev_err(gpio->dev, "failed to init gpio chip for %s\n",
+ pp->name);
+ return err;
+ }
+
+#ifdef CONFIG_OF_GPIO
+ port->bgc.gc.of_node = pp->node;
+#endif
+ port->bgc.gc.ngpio = pp->ngpio;
+ port->bgc.gc.base = pp->gpio_base;
+
+ /* Only port A support debounce */
+ if (pp->idx == 0)
+ port->bgc.gc.set_debounce = dwapb_gpio_set_debounce;
+
+ if (pp->irq)
+ dwapb_configure_irqs(gpio, port, pp);
+
+ err = gpiochip_add(&port->bgc.gc);
+ if (err)
+ dev_err(gpio->dev, "failed to register gpiochip for %s\n",
+ pp->name);
+ else
+ port->is_registered = true;
+
+ return err;
+}
+
+static void dwapb_gpio_unregister(struct dwapb_gpio *gpio)
+{
+ unsigned int m;
+
+ for (m = 0; m < gpio->nr_ports; ++m)
+ if (gpio->ports[m].is_registered)
+ gpiochip_remove(&gpio->ports[m].bgc.gc);
+}
+
+static struct dwapb_platform_data *
+dwapb_gpio_get_pdata_of(struct device *dev)
+{
+ struct device_node *node, *port_np;
+ struct dwapb_platform_data *pdata;
+ struct dwapb_port_property *pp;
+ int nports;
+ int i;
+
+ node = dev->of_node;
+ if (!IS_ENABLED(CONFIG_OF_GPIO) || !node)
+ return ERR_PTR(-ENODEV);
+
+ nports = of_get_child_count(node);
+ if (nports == 0)
+ return ERR_PTR(-ENODEV);
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->properties = devm_kcalloc(dev, nports, sizeof(*pp), GFP_KERNEL);
+ if (!pdata->properties)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->nports = nports;
+
+ i = 0;
+ for_each_child_of_node(node, port_np) {
+ pp = &pdata->properties[i++];
+ pp->node = port_np;
+
+ if (of_property_read_u32(port_np, "reg", &pp->idx) ||
+ pp->idx >= DWAPB_MAX_PORTS) {
+ dev_err(dev, "missing/invalid port index for %s\n",
+ port_np->full_name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(port_np, "snps,nr-gpios",
+ &pp->ngpio)) {
+ dev_info(dev, "failed to get number of gpios for %s\n",
+ port_np->full_name);
+ pp->ngpio = 32;
+ }
+
+ /*
+ * Only port A can provide interrupts in all configurations of
+ * the IP.
+ */
+ if (pp->idx == 0 &&
+ of_property_read_bool(port_np, "interrupt-controller")) {
+ pp->irq = irq_of_parse_and_map(port_np, 0);
+ if (!pp->irq) {
+ dev_warn(dev, "no irq for bank %s\n",
+ port_np->full_name);
+ }
+ }
+
+ pp->irq_shared = false;
+ pp->gpio_base = -1;
+ pp->name = port_np->full_name;
+ }
+
+ return pdata;
+}
+
+static int dwapb_gpio_probe(struct platform_device *pdev)
+{
+ unsigned int i;
+ struct resource *res;
+ struct dwapb_gpio *gpio;
+ int err;
+ struct device *dev = &pdev->dev;
+ struct dwapb_platform_data *pdata = dev_get_platdata(dev);
+
+ if (!pdata) {
+ pdata = dwapb_gpio_get_pdata_of(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ if (!pdata->nports)
+ return -ENODEV;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->dev = &pdev->dev;
+ gpio->nr_ports = pdata->nports;
+
+ gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
+ sizeof(*gpio->ports), GFP_KERNEL);
+ if (!gpio->ports)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gpio->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpio->regs))
+ return PTR_ERR(gpio->regs);
+
+ for (i = 0; i < gpio->nr_ports; i++) {
+ err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i);
+ if (err)
+ goto out_unregister;
+ }
+ platform_set_drvdata(pdev, gpio);
+
+ return 0;
+
+out_unregister:
+ dwapb_gpio_unregister(gpio);
+ dwapb_irq_teardown(gpio);
+
+ 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);
+
+ return 0;
+}
+
+static const struct of_device_id dwapb_of_match[] = {
+ { .compatible = "snps,dw-apb-gpio" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dwapb_of_match);
+
+#ifdef CONFIG_PM_SLEEP
+static int dwapb_gpio_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&bgc->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_SIZE;
+ ctx->dir = dwapb_read(gpio, offset);
+
+ offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+ ctx->data = dwapb_read(gpio, offset);
+
+ offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+ ctx->ext = dwapb_read(gpio, offset);
+
+ /* Only port A can provide interrupts */
+ if (idx == 0) {
+ ctx->int_mask = dwapb_read(gpio, GPIO_INTMASK);
+ ctx->int_en = dwapb_read(gpio, GPIO_INTEN);
+ ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY);
+ ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
+ ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
+
+ /* Mask out interrupts */
+ dwapb_write(gpio, GPIO_INTMASK, 0xffffffff);
+ }
+ }
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int dwapb_gpio_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&bgc->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_SIZE;
+ dwapb_write(gpio, offset, ctx->data);
+
+ offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+ dwapb_write(gpio, offset, ctx->dir);
+
+ offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+ dwapb_write(gpio, offset, ctx->ext);
+
+ /* Only port A can provide interrupts */
+ if (idx == 0) {
+ dwapb_write(gpio, GPIO_INTTYPE_LEVEL, ctx->int_type);
+ dwapb_write(gpio, GPIO_INT_POLARITY, ctx->int_pol);
+ dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, ctx->int_deb);
+ dwapb_write(gpio, GPIO_INTEN, ctx->int_en);
+ dwapb_write(gpio, GPIO_INTMASK, ctx->int_mask);
+
+ /* Clear out spurious interrupts */
+ dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff);
+ }
+ }
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend,
+ dwapb_gpio_resume);
+
+static struct platform_driver dwapb_gpio_driver = {
+ .driver = {
+ .name = "gpio-dwapb",
+ .pm = &dwapb_gpio_pm_ops,
+ .of_match_table = of_match_ptr(dwapb_of_match),
+ },
+ .probe = dwapb_gpio_probe,
+ .remove = dwapb_gpio_remove,
+};
+
+module_platform_driver(dwapb_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jamie Iles");
+MODULE_DESCRIPTION("Synopsys DesignWare APB GPIO driver");
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
new file mode 100644
index 000000000..3cfcfc620
--- /dev/null
+++ b/drivers/gpio/gpio-em.c
@@ -0,0 +1,443 @@
+/*
+ * Emma Mobile GPIO Support - GIO
+ *
+ * Copyright (C) 2012 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_data/gpio-em.h>
+
+struct em_gio_priv {
+ void __iomem *base0;
+ void __iomem *base1;
+ spinlock_t sense_lock;
+ struct platform_device *pdev;
+ struct gpio_chip gpio_chip;
+ struct irq_chip irq_chip;
+ struct irq_domain *irq_domain;
+};
+
+#define GIO_E1 0x00
+#define GIO_E0 0x04
+#define GIO_EM 0x04
+#define GIO_OL 0x08
+#define GIO_OH 0x0c
+#define GIO_I 0x10
+#define GIO_IIA 0x14
+#define GIO_IEN 0x18
+#define GIO_IDS 0x1c
+#define GIO_IIM 0x1c
+#define GIO_RAW 0x20
+#define GIO_MST 0x24
+#define GIO_IIR 0x28
+
+#define GIO_IDT0 0x40
+#define GIO_IDT1 0x44
+#define GIO_IDT2 0x48
+#define GIO_IDT3 0x4c
+#define GIO_RAWBL 0x50
+#define GIO_RAWBH 0x54
+#define GIO_IRBL 0x58
+#define GIO_IRBH 0x5c
+
+#define GIO_IDT(n) (GIO_IDT0 + ((n) * 4))
+
+static inline unsigned long em_gio_read(struct em_gio_priv *p, int offs)
+{
+ if (offs < GIO_IDT0)
+ return ioread32(p->base0 + offs);
+ else
+ return ioread32(p->base1 + (offs - GIO_IDT0));
+}
+
+static inline void em_gio_write(struct em_gio_priv *p, int offs,
+ unsigned long value)
+{
+ if (offs < GIO_IDT0)
+ iowrite32(value, p->base0 + offs);
+ else
+ iowrite32(value, p->base1 + (offs - GIO_IDT0));
+}
+
+static void em_gio_irq_disable(struct irq_data *d)
+{
+ struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
+
+ em_gio_write(p, GIO_IDS, BIT(irqd_to_hwirq(d)));
+}
+
+static void em_gio_irq_enable(struct irq_data *d)
+{
+ struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
+
+ em_gio_write(p, GIO_IEN, BIT(irqd_to_hwirq(d)));
+}
+
+static int em_gio_irq_reqres(struct irq_data *d)
+{
+ struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
+
+ if (gpiochip_lock_as_irq(&p->gpio_chip, irqd_to_hwirq(d))) {
+ dev_err(p->gpio_chip.dev,
+ "unable to lock HW IRQ %lu for IRQ\n",
+ irqd_to_hwirq(d));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void em_gio_irq_relres(struct irq_data *d)
+{
+ struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
+
+ gpiochip_unlock_as_irq(&p->gpio_chip, irqd_to_hwirq(d));
+}
+
+
+#define GIO_ASYNC(x) (x + 8)
+
+static unsigned char em_gio_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
+ [IRQ_TYPE_EDGE_RISING] = GIO_ASYNC(0x00),
+ [IRQ_TYPE_EDGE_FALLING] = GIO_ASYNC(0x01),
+ [IRQ_TYPE_LEVEL_HIGH] = GIO_ASYNC(0x02),
+ [IRQ_TYPE_LEVEL_LOW] = GIO_ASYNC(0x03),
+ [IRQ_TYPE_EDGE_BOTH] = GIO_ASYNC(0x04),
+};
+
+static int em_gio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ unsigned char value = em_gio_sense_table[type & IRQ_TYPE_SENSE_MASK];
+ struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
+ unsigned int reg, offset, shift;
+ unsigned long flags;
+ unsigned long tmp;
+
+ if (!value)
+ return -EINVAL;
+
+ offset = irqd_to_hwirq(d);
+
+ pr_debug("gio: sense irq = %d, mode = %d\n", offset, value);
+
+ /* 8 x 4 bit fields in 4 IDT registers */
+ reg = GIO_IDT(offset >> 3);
+ shift = (offset & 0x07) << 4;
+
+ spin_lock_irqsave(&p->sense_lock, flags);
+
+ /* disable the interrupt in IIA */
+ tmp = em_gio_read(p, GIO_IIA);
+ tmp &= ~BIT(offset);
+ em_gio_write(p, GIO_IIA, tmp);
+
+ /* change the sense setting in IDT */
+ tmp = em_gio_read(p, reg);
+ tmp &= ~(0xf << shift);
+ tmp |= value << shift;
+ em_gio_write(p, reg, tmp);
+
+ /* clear pending interrupts */
+ em_gio_write(p, GIO_IIR, BIT(offset));
+
+ /* enable the interrupt in IIA */
+ tmp = em_gio_read(p, GIO_IIA);
+ tmp |= BIT(offset);
+ em_gio_write(p, GIO_IIA, tmp);
+
+ spin_unlock_irqrestore(&p->sense_lock, flags);
+
+ return 0;
+}
+
+static irqreturn_t em_gio_irq_handler(int irq, void *dev_id)
+{
+ struct em_gio_priv *p = dev_id;
+ unsigned long pending;
+ unsigned int offset, irqs_handled = 0;
+
+ 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));
+ irqs_handled++;
+ }
+
+ return irqs_handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline struct em_gio_priv *gpio_to_priv(struct gpio_chip *chip)
+{
+ return container_of(chip, struct em_gio_priv, gpio_chip);
+}
+
+static int em_gio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ em_gio_write(gpio_to_priv(chip), GIO_E0, BIT(offset));
+ return 0;
+}
+
+static int em_gio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & BIT(offset));
+}
+
+static void __em_gio_set(struct gpio_chip *chip, unsigned int reg,
+ unsigned shift, int value)
+{
+ /* upper 16 bits contains mask and lower 16 actual value */
+ em_gio_write(gpio_to_priv(chip), reg,
+ (BIT(shift + 16)) | (value << shift));
+}
+
+static void em_gio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ /* output is split into two registers */
+ if (offset < 16)
+ __em_gio_set(chip, GIO_OL, offset, value);
+ else
+ __em_gio_set(chip, GIO_OH, offset - 16, value);
+}
+
+static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ /* write GPIO value to output before selecting output mode of pin */
+ em_gio_set(chip, offset, value);
+ em_gio_write(gpio_to_priv(chip), GIO_E1, BIT(offset));
+ return 0;
+}
+
+static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset);
+}
+
+static int em_gio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void em_gio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+
+ /* Set the GPIO as an input to ensure that the next GPIO request won't
+ * drive the GPIO pin as an output.
+ */
+ em_gio_direction_input(chip, offset);
+}
+
+static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct em_gio_priv *p = h->host_data;
+
+ pr_debug("gio: map hw irq = %d, irq = %d\n", (int)hwirq, irq);
+
+ irq_set_chip_data(irq, h->host_data);
+ irq_set_chip_and_handler(irq, &p->irq_chip, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID); /* kill me now */
+ return 0;
+}
+
+static struct irq_domain_ops em_gio_irq_domain_ops = {
+ .map = em_gio_irq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int em_gio_probe(struct platform_device *pdev)
+{
+ struct gpio_em_config pdata_dt;
+ struct gpio_em_config *pdata = dev_get_platdata(&pdev->dev);
+ 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);
+ int ret;
+
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ p->pdev = 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);
+
+ if (!io[0] || !io[1] || !irq[0] || !irq[1]) {
+ dev_err(&pdev->dev, "missing IRQ or IOMEM\n");
+ ret = -EINVAL;
+ goto err0;
+ }
+
+ p->base0 = devm_ioremap_nocache(&pdev->dev, io[0]->start,
+ resource_size(io[0]));
+ if (!p->base0) {
+ dev_err(&pdev->dev, "failed to remap low I/O memory\n");
+ ret = -ENXIO;
+ goto err0;
+ }
+
+ p->base1 = devm_ioremap_nocache(&pdev->dev, io[1]->start,
+ resource_size(io[1]));
+ if (!p->base1) {
+ dev_err(&pdev->dev, "failed to remap high I/O memory\n");
+ ret = -ENXIO;
+ goto err0;
+ }
+
+ if (!pdata) {
+ memset(&pdata_dt, 0, sizeof(pdata_dt));
+ pdata = &pdata_dt;
+
+ if (of_property_read_u32(pdev->dev.of_node, "ngpios",
+ &pdata->number_of_pins)) {
+ dev_err(&pdev->dev, "Missing ngpios OF property\n");
+ ret = -EINVAL;
+ goto err0;
+ }
+
+ pdata->gpio_base = -1;
+ }
+
+ 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;
+ gpio_chip->set = em_gio_set;
+ gpio_chip->to_irq = em_gio_to_irq;
+ gpio_chip->request = em_gio_request;
+ gpio_chip->free = em_gio_free;
+ gpio_chip->label = name;
+ gpio_chip->dev = &pdev->dev;
+ gpio_chip->owner = THIS_MODULE;
+ gpio_chip->base = pdata->gpio_base;
+ gpio_chip->ngpio = pdata->number_of_pins;
+
+ irq_chip = &p->irq_chip;
+ irq_chip->name = name;
+ 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;
+ irq_chip->irq_request_resources = em_gio_irq_reqres;
+ 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,
+ pdata->number_of_pins,
+ pdata->irq_base,
+ &em_gio_irq_domain_ops, p);
+ if (!p->irq_domain) {
+ ret = -ENXIO;
+ dev_err(&pdev->dev, "cannot initialize irq domain\n");
+ goto err0;
+ }
+
+ 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");
+ ret = -ENOENT;
+ goto err1;
+ }
+
+ 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");
+ ret = -ENOENT;
+ goto err1;
+ }
+
+ ret = gpiochip_add(gpio_chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add GPIO controller\n");
+ goto err1;
+ }
+
+ if (pdata->pctl_name) {
+ ret = gpiochip_add_pin_range(gpio_chip, pdata->pctl_name, 0,
+ gpio_chip->base, gpio_chip->ngpio);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed to add pin range\n");
+ }
+ return 0;
+
+err1:
+ irq_domain_remove(p->irq_domain);
+err0:
+ return ret;
+}
+
+static int em_gio_remove(struct platform_device *pdev)
+{
+ struct em_gio_priv *p = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&p->gpio_chip);
+
+ irq_domain_remove(p->irq_domain);
+ return 0;
+}
+
+static const struct of_device_id em_gio_dt_ids[] = {
+ { .compatible = "renesas,em-gio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, em_gio_dt_ids);
+
+static struct platform_driver em_gio_device_driver = {
+ .probe = em_gio_probe,
+ .remove = em_gio_remove,
+ .driver = {
+ .name = "em_gio",
+ .of_match_table = em_gio_dt_ids,
+ }
+};
+
+static int __init em_gio_init(void)
+{
+ return platform_driver_register(&em_gio_device_driver);
+}
+postcore_initcall(em_gio_init);
+
+static void __exit em_gio_exit(void)
+{
+ platform_driver_unregister(&em_gio_device_driver);
+}
+module_exit(em_gio_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas Emma Mobile GIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c
new file mode 100644
index 000000000..45684f36d
--- /dev/null
+++ b/drivers/gpio/gpio-ep93xx.c
@@ -0,0 +1,390 @@
+/*
+ * Generic EP93xx GPIO handling
+ *
+ * Copyright (c) 2008 Ryan Mallon
+ * Copyright (c) 2011 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on code originally from:
+ * linux/arch/arm/mach-ep93xx/core.c
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/basic_mmio_gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/gpio-ep93xx.h>
+
+#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0))
+
+struct ep93xx_gpio {
+ void __iomem *mmio_base;
+ struct bgpio_chip bgc[8];
+};
+
+/*************************************************************************
+ * 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(unsigned port)
+{
+ BUG_ON(port > 2);
+
+ writeb_relaxed(0, EP93XX_GPIO_REG(int_en_register_offset[port]));
+
+ writeb_relaxed(gpio_int_type2[port],
+ EP93XX_GPIO_REG(int_type2_register_offset[port]));
+
+ writeb_relaxed(gpio_int_type1[port],
+ EP93XX_GPIO_REG(int_type1_register_offset[port]));
+
+ writeb(gpio_int_unmasked[port] & gpio_int_enabled[port],
+ EP93XX_GPIO_REG(int_en_register_offset[port]));
+}
+
+static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable)
+{
+ int line = irq_to_gpio(irq);
+ int port = line >> 3;
+ int port_mask = 1 << (line & 7);
+
+ if (enable)
+ gpio_int_debounce[port] |= port_mask;
+ else
+ gpio_int_debounce[port] &= ~port_mask;
+
+ writeb(gpio_int_debounce[port],
+ EP93XX_GPIO_REG(int_debounce_register_offset[port]));
+}
+
+static void ep93xx_gpio_ab_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned char status;
+ int i;
+
+ status = readb(EP93XX_GPIO_A_INT_STATUS);
+ for (i = 0; i < 8; i++) {
+ if (status & (1 << i)) {
+ int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_A(0)) + i;
+ generic_handle_irq(gpio_irq);
+ }
+ }
+
+ status = readb(EP93XX_GPIO_B_INT_STATUS);
+ for (i = 0; i < 8; i++) {
+ if (status & (1 << i)) {
+ int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_B(0)) + i;
+ generic_handle_irq(gpio_irq);
+ }
+ }
+}
+
+static void ep93xx_gpio_f_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ /*
+ * map discontiguous hw irq range to continuous sw irq range:
+ *
+ * IRQ_EP93XX_GPIO{0..7}MUX -> gpio_to_irq(EP93XX_GPIO_LINE_F({0..7})
+ */
+ int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */
+ int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_F(0)) + port_f_idx;
+
+ generic_handle_irq(gpio_irq);
+}
+
+static void ep93xx_gpio_irq_ack(struct irq_data *d)
+{
+ int line = irq_to_gpio(d->irq);
+ int port = line >> 3;
+ int port_mask = 1 << (line & 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(port);
+ }
+
+ writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
+}
+
+static void ep93xx_gpio_irq_mask_ack(struct irq_data *d)
+{
+ int line = irq_to_gpio(d->irq);
+ int port = line >> 3;
+ int port_mask = 1 << (line & 7);
+
+ if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH)
+ gpio_int_type2[port] ^= port_mask; /* switch edge direction */
+
+ gpio_int_unmasked[port] &= ~port_mask;
+ ep93xx_gpio_update_int_params(port);
+
+ writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
+}
+
+static void ep93xx_gpio_irq_mask(struct irq_data *d)
+{
+ int line = irq_to_gpio(d->irq);
+ int port = line >> 3;
+
+ gpio_int_unmasked[port] &= ~(1 << (line & 7));
+ ep93xx_gpio_update_int_params(port);
+}
+
+static void ep93xx_gpio_irq_unmask(struct irq_data *d)
+{
+ int line = irq_to_gpio(d->irq);
+ int port = line >> 3;
+
+ gpio_int_unmasked[port] |= 1 << (line & 7);
+ ep93xx_gpio_update_int_params(port);
+}
+
+/*
+ * gpio_int_type1 controls whether the interrupt is level (0) or
+ * edge (1) triggered, while gpio_int_type2 controls whether it
+ * triggers on low/falling (0) or high/rising (1).
+ */
+static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+ const int gpio = irq_to_gpio(d->irq);
+ const int port = gpio >> 3;
+ const int port_mask = 1 << (gpio & 7);
+ irq_flow_handler_t handler;
+
+ gpio_direction_input(gpio);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ gpio_int_type1[port] |= port_mask;
+ gpio_int_type2[port] |= port_mask;
+ handler = handle_edge_irq;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ gpio_int_type1[port] |= port_mask;
+ gpio_int_type2[port] &= ~port_mask;
+ handler = handle_edge_irq;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ gpio_int_type1[port] &= ~port_mask;
+ gpio_int_type2[port] |= port_mask;
+ handler = handle_level_irq;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ gpio_int_type1[port] &= ~port_mask;
+ gpio_int_type2[port] &= ~port_mask;
+ handler = handle_level_irq;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ gpio_int_type1[port] |= port_mask;
+ /* set initial polarity based on current input level */
+ if (gpio_get_value(gpio))
+ gpio_int_type2[port] &= ~port_mask; /* falling */
+ else
+ gpio_int_type2[port] |= port_mask; /* rising */
+ handler = handle_edge_irq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ __irq_set_handler_locked(d->irq, handler);
+
+ gpio_int_enabled[port] |= port_mask;
+
+ ep93xx_gpio_update_int_params(port);
+
+ 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 void ep93xx_gpio_init_irq(void)
+{
+ int gpio_irq;
+
+ for (gpio_irq = gpio_to_irq(0);
+ gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) {
+ irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip,
+ handle_level_irq);
+ set_irq_flags(gpio_irq, IRQF_VALID);
+ }
+
+ irq_set_chained_handler(IRQ_EP93XX_GPIO_AB,
+ ep93xx_gpio_ab_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO0MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO1MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO2MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO3MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO4MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO5MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO6MUX,
+ ep93xx_gpio_f_irq_handler);
+ irq_set_chained_handler(IRQ_EP93XX_GPIO7MUX,
+ ep93xx_gpio_f_irq_handler);
+}
+
+
+/*************************************************************************
+ * gpiolib interface for EP93xx on-chip GPIOs
+ *************************************************************************/
+struct ep93xx_gpio_bank {
+ const char *label;
+ int data;
+ int dir;
+ int base;
+ bool has_debounce;
+};
+
+#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _debounce) \
+ { \
+ .label = _label, \
+ .data = _data, \
+ .dir = _dir, \
+ .base = _base, \
+ .has_debounce = _debounce, \
+ }
+
+static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = {
+ EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true),
+ EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true),
+ 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),
+ EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false),
+ EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false),
+};
+
+static int ep93xx_gpio_set_debounce(struct gpio_chip *chip,
+ unsigned offset, unsigned debounce)
+{
+ int gpio = chip->base + offset;
+ int irq = gpio_to_irq(gpio);
+
+ if (irq < 0)
+ return -EINVAL;
+
+ ep93xx_gpio_int_debounce(irq, debounce ? true : false);
+
+ return 0;
+}
+
+/*
+ * Map GPIO A0..A7 (0..7) to irq 64..71,
+ * B0..B7 (7..15) to irq 72..79, and
+ * F0..F7 (16..24) to irq 80..87.
+ */
+static int ep93xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+
+ if (gpio > EP93XX_GPIO_LINE_MAX_IRQ)
+ return -EINVAL;
+
+ return 64 + gpio;
+}
+
+static int ep93xx_gpio_add_bank(struct bgpio_chip *bgc, struct device *dev,
+ void __iomem *mmio_base, struct ep93xx_gpio_bank *bank)
+{
+ void __iomem *data = mmio_base + bank->data;
+ void __iomem *dir = mmio_base + bank->dir;
+ int err;
+
+ err = bgpio_init(bgc, dev, 1, data, NULL, NULL, dir, NULL, 0);
+ if (err)
+ return err;
+
+ bgc->gc.label = bank->label;
+ bgc->gc.base = bank->base;
+
+ if (bank->has_debounce) {
+ bgc->gc.set_debounce = ep93xx_gpio_set_debounce;
+ bgc->gc.to_irq = ep93xx_gpio_to_irq;
+ }
+
+ return gpiochip_add(&bgc->gc);
+}
+
+static int ep93xx_gpio_probe(struct platform_device *pdev)
+{
+ struct ep93xx_gpio *ep93xx_gpio;
+ struct resource *res;
+ int i;
+ struct device *dev = &pdev->dev;
+
+ ep93xx_gpio = devm_kzalloc(dev, sizeof(struct ep93xx_gpio), GFP_KERNEL);
+ if (!ep93xx_gpio)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ep93xx_gpio->mmio_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ep93xx_gpio->mmio_base))
+ return PTR_ERR(ep93xx_gpio->mmio_base);
+
+ for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) {
+ struct bgpio_chip *bgc = &ep93xx_gpio->bgc[i];
+ struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i];
+
+ if (ep93xx_gpio_add_bank(bgc, &pdev->dev,
+ ep93xx_gpio->mmio_base, bank))
+ dev_warn(&pdev->dev, "Unable to add gpio bank %s\n",
+ bank->label);
+ }
+
+ ep93xx_gpio_init_irq();
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_gpio_driver = {
+ .driver = {
+ .name = "gpio-ep93xx",
+ },
+ .probe = ep93xx_gpio_probe,
+};
+
+static int __init ep93xx_gpio_init(void)
+{
+ return platform_driver_register(&ep93xx_gpio_driver);
+}
+postcore_initcall(ep93xx_gpio_init);
+
+MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com> "
+ "H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("EP93XX GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
new file mode 100644
index 000000000..dbda8433c
--- /dev/null
+++ b/drivers/gpio/gpio-f7188x.c
@@ -0,0 +1,494 @@
+/*
+ * GPIO driver for Fintek Super-I/O F71869, F71869A, F71882 and F71889
+ *
+ * Copyright (C) 2010-2013 LaCie
+ *
+ * Author: Simon Guinot <simon.guinot@sequanux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#define DRVNAME "gpio-f7188x"
+
+/*
+ * Super-I/O registers
+ */
+#define SIO_LDSEL 0x07 /* Logical device select */
+#define SIO_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_DEVREV 0x22 /* Device revision */
+#define SIO_MANID 0x23 /* Fintek ID (2 bytes) */
+
+#define SIO_LD_GPIO 0x06 /* GPIO logical device */
+#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
+
+#define SIO_FINTEK_ID 0x1934 /* Manufacturer ID */
+#define SIO_F71869_ID 0x0814 /* F71869 chipset ID */
+#define SIO_F71869A_ID 0x1007 /* F71869A chipset ID */
+#define SIO_F71882_ID 0x0541 /* F71882 chipset ID */
+#define SIO_F71889_ID 0x0909 /* F71889 chipset ID */
+
+enum chips { f71869, f71869a, f71882fg, f71889f };
+
+static const char * const f7188x_names[] = {
+ "f71869",
+ "f71869a",
+ "f71882fg",
+ "f71889f",
+};
+
+struct f7188x_sio {
+ int addr;
+ enum chips type;
+};
+
+struct f7188x_gpio_bank {
+ struct gpio_chip chip;
+ unsigned int regbase;
+ struct f7188x_gpio_data *data;
+};
+
+struct f7188x_gpio_data {
+ struct f7188x_sio *sio;
+ int nr_bank;
+ struct f7188x_gpio_bank *bank;
+};
+
+/*
+ * Super-I/O functions.
+ */
+
+static inline int superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static int superio_inw(int base, int reg)
+{
+ int val;
+
+ outb(reg++, base);
+ val = inb(base + 1) << 8;
+ outb(reg, base);
+ val |= inb(base + 1);
+
+ return val;
+}
+
+static inline void superio_outb(int base, int reg, int val)
+{
+ outb(reg, base);
+ outb(val, base + 1);
+}
+
+static inline int superio_enter(int base)
+{
+ /* Don't step on other drivers' I/O space by accident. */
+ if (!request_muxed_region(base, 2, DRVNAME)) {
+ pr_err(DRVNAME "I/O address 0x%04x already in use\n", base);
+ return -EBUSY;
+ }
+
+ /* According to the datasheet the key must be send twice. */
+ outb(SIO_UNLOCK_KEY, base);
+ outb(SIO_UNLOCK_KEY, base);
+
+ return 0;
+}
+
+static inline void superio_select(int base, int ld)
+{
+ outb(SIO_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+ outb(SIO_LOCK_KEY, base);
+ release_region(base, 2);
+}
+
+/*
+ * GPIO chip.
+ */
+
+static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);
+static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);
+static int f7188x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value);
+static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value);
+
+#define F7188X_GPIO_BANK(_base, _ngpio, _regbase) \
+ { \
+ .chip = { \
+ .label = DRVNAME, \
+ .owner = THIS_MODULE, \
+ .direction_input = f7188x_gpio_direction_in, \
+ .get = f7188x_gpio_get, \
+ .direction_output = f7188x_gpio_direction_out, \
+ .set = f7188x_gpio_set, \
+ .base = _base, \
+ .ngpio = _ngpio, \
+ .can_sleep = true, \
+ }, \
+ .regbase = _regbase, \
+ }
+
+#define gpio_dir(base) (base + 0)
+#define gpio_data_out(base) (base + 1)
+#define gpio_data_in(base) (base + 2)
+/* Output mode register (0:open drain 1:push-pull). */
+#define gpio_out_mode(base) (base + 3)
+
+static struct f7188x_gpio_bank f71869_gpio_bank[] = {
+ F7188X_GPIO_BANK(0, 6, 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, 5, 0xA0),
+ F7188X_GPIO_BANK(60, 6, 0x90),
+};
+
+static struct f7188x_gpio_bank f71869a_gpio_bank[] = {
+ F7188X_GPIO_BANK(0, 6, 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, 5, 0xA0),
+ F7188X_GPIO_BANK(60, 8, 0x90),
+ F7188X_GPIO_BANK(70, 8, 0x80),
+};
+
+static struct f7188x_gpio_bank f71882_gpio_bank[] = {
+ F7188X_GPIO_BANK(0 , 8, 0xF0),
+ F7188X_GPIO_BANK(10, 8, 0xE0),
+ F7188X_GPIO_BANK(20, 8, 0xD0),
+ F7188X_GPIO_BANK(30, 4, 0xC0),
+ F7188X_GPIO_BANK(40, 4, 0xB0),
+};
+
+static struct f7188x_gpio_bank f71889_gpio_bank[] = {
+ F7188X_GPIO_BANK(0 , 7, 0xF0),
+ F7188X_GPIO_BANK(10, 7, 0xE0),
+ F7188X_GPIO_BANK(20, 8, 0xD0),
+ F7188X_GPIO_BANK(30, 8, 0xC0),
+ F7188X_GPIO_BANK(40, 8, 0xB0),
+ F7188X_GPIO_BANK(50, 5, 0xA0),
+ F7188X_GPIO_BANK(60, 8, 0x90),
+ F7188X_GPIO_BANK(70, 8, 0x80),
+};
+
+static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 dir;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return err;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
+ dir &= ~(1 << offset);
+ superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
+
+ superio_exit(sio->addr);
+
+ return 0;
+}
+
+static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 dir, data;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return err;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
+ dir = !!(dir & (1 << offset));
+ if (dir)
+ data = superio_inb(sio->addr, gpio_data_out(bank->regbase));
+ else
+ data = superio_inb(sio->addr, gpio_data_in(bank->regbase));
+
+ superio_exit(sio->addr);
+
+ return !!(data & 1 << offset);
+}
+
+static int f7188x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 dir, data_out;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return err;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
+ if (value)
+ data_out |= (1 << offset);
+ else
+ data_out &= ~(1 << offset);
+ superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
+
+ dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
+ dir |= (1 << offset);
+ superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
+
+ superio_exit(sio->addr);
+
+ return 0;
+}
+
+static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 data_out;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
+ if (value)
+ data_out |= (1 << offset);
+ else
+ data_out &= ~(1 << offset);
+ superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
+
+ superio_exit(sio->addr);
+}
+
+/*
+ * Platform device and driver.
+ */
+
+static int f7188x_gpio_probe(struct platform_device *pdev)
+{
+ int err;
+ int i;
+ struct f7188x_sio *sio = pdev->dev.platform_data;
+ struct f7188x_gpio_data *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ switch (sio->type) {
+ case f71869:
+ data->nr_bank = ARRAY_SIZE(f71869_gpio_bank);
+ data->bank = f71869_gpio_bank;
+ break;
+ case f71869a:
+ data->nr_bank = ARRAY_SIZE(f71869a_gpio_bank);
+ data->bank = f71869a_gpio_bank;
+ break;
+ case f71882fg:
+ data->nr_bank = ARRAY_SIZE(f71882_gpio_bank);
+ data->bank = f71882_gpio_bank;
+ break;
+ case f71889f:
+ data->nr_bank = ARRAY_SIZE(f71889_gpio_bank);
+ data->bank = f71889_gpio_bank;
+ break;
+ default:
+ return -ENODEV;
+ }
+ data->sio = sio;
+
+ platform_set_drvdata(pdev, data);
+
+ /* For each GPIO bank, register a GPIO chip. */
+ for (i = 0; i < data->nr_bank; i++) {
+ struct f7188x_gpio_bank *bank = &data->bank[i];
+
+ bank->chip.dev = &pdev->dev;
+ bank->data = data;
+
+ err = gpiochip_add(&bank->chip);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Failed to register gpiochip %d: %d\n",
+ i, err);
+ goto err_gpiochip;
+ }
+ }
+
+ return 0;
+
+err_gpiochip:
+ for (i = i - 1; i >= 0; i--) {
+ struct f7188x_gpio_bank *bank = &data->bank[i];
+ gpiochip_remove(&bank->chip);
+ }
+
+ return err;
+}
+
+static int f7188x_gpio_remove(struct platform_device *pdev)
+{
+ int i;
+ struct f7188x_gpio_data *data = platform_get_drvdata(pdev);
+
+ for (i = 0; i < data->nr_bank; i++) {
+ struct f7188x_gpio_bank *bank = &data->bank[i];
+ gpiochip_remove(&bank->chip);
+ }
+
+ return 0;
+}
+
+static int __init f7188x_find(int addr, struct f7188x_sio *sio)
+{
+ int err;
+ u16 devid;
+
+ err = superio_enter(addr);
+ if (err)
+ return err;
+
+ err = -ENODEV;
+ devid = superio_inw(addr, SIO_MANID);
+ if (devid != SIO_FINTEK_ID) {
+ pr_debug(DRVNAME ": Not a Fintek device at 0x%08x\n", addr);
+ goto err;
+ }
+
+ devid = superio_inw(addr, SIO_DEVID);
+ switch (devid) {
+ case SIO_F71869_ID:
+ sio->type = f71869;
+ break;
+ case SIO_F71869A_ID:
+ sio->type = f71869a;
+ break;
+ case SIO_F71882_ID:
+ sio->type = f71882fg;
+ break;
+ case SIO_F71889_ID:
+ sio->type = f71889f;
+ break;
+ default:
+ pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
+ goto err;
+ }
+ sio->addr = addr;
+ err = 0;
+
+ pr_info(DRVNAME ": Found %s at %#x, revision %d\n",
+ f7188x_names[sio->type],
+ (unsigned int) addr,
+ (int) superio_inb(addr, SIO_DEVREV));
+
+err:
+ superio_exit(addr);
+ return err;
+}
+
+static struct platform_device *f7188x_gpio_pdev;
+
+static int __init
+f7188x_gpio_device_add(const struct f7188x_sio *sio)
+{
+ int err;
+
+ f7188x_gpio_pdev = platform_device_alloc(DRVNAME, -1);
+ if (!f7188x_gpio_pdev)
+ return -ENOMEM;
+
+ err = platform_device_add_data(f7188x_gpio_pdev,
+ sio, sizeof(*sio));
+ if (err) {
+ pr_err(DRVNAME "Platform data allocation failed\n");
+ goto err;
+ }
+
+ err = platform_device_add(f7188x_gpio_pdev);
+ if (err) {
+ pr_err(DRVNAME "Device addition failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ platform_device_put(f7188x_gpio_pdev);
+
+ return err;
+}
+
+/*
+ * Try to match a supported Fintek device by reading the (hard-wired)
+ * configuration I/O ports. If available, then register both the platform
+ * device and driver to support the GPIOs.
+ */
+
+static struct platform_driver f7188x_gpio_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe = f7188x_gpio_probe,
+ .remove = f7188x_gpio_remove,
+};
+
+static int __init f7188x_gpio_init(void)
+{
+ int err;
+ struct f7188x_sio sio;
+
+ if (f7188x_find(0x2e, &sio) &&
+ f7188x_find(0x4e, &sio))
+ return -ENODEV;
+
+ err = platform_driver_register(&f7188x_gpio_driver);
+ if (!err) {
+ err = f7188x_gpio_device_add(&sio);
+ if (err)
+ platform_driver_unregister(&f7188x_gpio_driver);
+ }
+
+ return err;
+}
+subsys_initcall(f7188x_gpio_init);
+
+static void __exit f7188x_gpio_exit(void)
+{
+ platform_device_unregister(f7188x_gpio_pdev);
+ platform_driver_unregister(&f7188x_gpio_driver);
+}
+module_exit(f7188x_gpio_exit);
+
+MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG and F71889F");
+MODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c
new file mode 100644
index 000000000..f9ac3f351
--- /dev/null
+++ b/drivers/gpio/gpio-ge.c
@@ -0,0 +1,114 @@
+/*
+ * Driver for GE FPGA based GPIO
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ *
+ * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * 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.
+ */
+
+/* TODO
+ *
+ * Configuration of output modes (totem-pole/open-drain)
+ * Interrupt configuration - interrupts are always generated the FPGA relies on
+ * the I/O interrupt controllers mask to stop them propergating
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_address.h>
+#include <linux/module.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define GEF_GPIO_DIRECT 0x00
+#define GEF_GPIO_IN 0x04
+#define GEF_GPIO_OUT 0x08
+#define GEF_GPIO_TRIG 0x0C
+#define GEF_GPIO_POLAR_A 0x10
+#define GEF_GPIO_POLAR_B 0x14
+#define GEF_GPIO_INT_STAT 0x18
+#define GEF_GPIO_OVERRUN 0x1C
+#define GEF_GPIO_MODE 0x20
+
+static const struct of_device_id gef_gpio_ids[] = {
+ {
+ .compatible = "gef,sbc610-gpio",
+ .data = (void *)19,
+ }, {
+ .compatible = "gef,sbc310-gpio",
+ .data = (void *)6,
+ }, {
+ .compatible = "ge,imp3a-gpio",
+ .data = (void *)16,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gef_gpio_ids);
+
+static int __init gef_gpio_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(gef_gpio_ids, &pdev->dev);
+ struct bgpio_chip *bgc;
+ void __iomem *regs;
+ int ret;
+
+ bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
+ if (!bgc)
+ return -ENOMEM;
+
+ regs = of_iomap(pdev->dev.of_node, 0);
+ if (!regs)
+ return -ENOMEM;
+
+ ret = bgpio_init(bgc, &pdev->dev, 4, regs + GEF_GPIO_IN,
+ regs + GEF_GPIO_OUT, NULL, NULL,
+ regs + GEF_GPIO_DIRECT, BGPIOF_BIG_ENDIAN_BYTE_ORDER);
+ if (ret) {
+ dev_err(&pdev->dev, "bgpio_init failed\n");
+ goto err0;
+ }
+
+ /* Setup pointers to chip functions */
+ bgc->gc.label = devm_kstrdup(&pdev->dev, pdev->dev.of_node->full_name,
+ GFP_KERNEL);
+ if (!bgc->gc.label) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ bgc->gc.base = -1;
+ bgc->gc.ngpio = (u16)(uintptr_t)of_id->data;
+ bgc->gc.of_gpio_n_cells = 2;
+ bgc->gc.of_node = pdev->dev.of_node;
+
+ /* This function adds a memory mapped GPIO chip */
+ ret = gpiochip_add(&bgc->gc);
+ if (ret)
+ goto err0;
+
+ return 0;
+err0:
+ iounmap(regs);
+ pr_err("%s: GPIO chip registration failed\n",
+ pdev->dev.of_node->full_name);
+ return ret;
+};
+
+static struct platform_driver gef_gpio_driver = {
+ .driver = {
+ .name = "gef-gpio",
+ .of_match_table = gef_gpio_ids,
+ },
+};
+module_platform_driver_probe(gef_gpio_driver, gef_gpio_probe);
+
+MODULE_DESCRIPTION("GE I/O FPGA GPIO driver");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
new file mode 100644
index 000000000..b92a690f5
--- /dev/null
+++ b/drivers/gpio/gpio-generic.c
@@ -0,0 +1,663 @@
+/*
+ * Generic driver for memory-mapped GPIO controllers.
+ *
+ * Copyright 2008 MontaVista Software, Inc.
+ * Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`.......
+ * ...`` ```````..
+ * ..The simplest form of a GPIO controller that the driver supports is``
+ * `.just a single "data" register, where GPIO state can be read and/or `
+ * `,..written. ,,..``~~~~ .....``.`.`.~~.```.`.........``````.```````
+ * `````````
+ ___
+_/~~|___/~| . ```~~~~~~ ___/___\___ ,~.`.`.`.`````.~~...,,,,...
+__________|~$@~~~ %~ /o*o*o*o*o*o\ .. Implementing such a GPIO .
+o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
+ `....trivial..'~`.```.```
+ * ```````
+ * .```````~~~~`..`.``.``.
+ * . The driver supports `... ,..```.`~~~```````````````....````.``,,
+ * . big-endian notation, just`. .. A bit more sophisticated controllers ,
+ * . register the device with -be`. .with a pair of set/clear-bit registers ,
+ * `.. suffix. ```~~`````....`.` . affecting the data register and the .`
+ * ``.`.``...``` ```.. output pins are also supported.`
+ * ^^ `````.`````````.,``~``~``~~``````
+ * . ^^
+ * ,..`.`.`...````````````......`.`.`.`.`.`..`.`.`..
+ * .. The expectation is that in at least some cases . ,-~~~-,
+ * .this will be used with roll-your-own ASIC/FPGA .` \ /
+ * .logic in Verilog or VHDL. ~~~`````````..`````~~` \ /
+ * ..````````......``````````` \o_
+ * |
+ * ^^ / \
+ *
+ * ...`````~~`.....``.`..........``````.`.``.```........``.
+ * ` 8, 16, 32 and 64 bits registers are supported, and``.
+ * . the number of GPIOs is determined by the width of ~
+ * .. the registers. ,............```.`.`..`.`.~~~.`.`.`~
+ * `.......````.```
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/log2.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/basic_mmio_gpio.h>
+
+static void bgpio_write8(void __iomem *reg, unsigned long data)
+{
+ writeb(data, reg);
+}
+
+static unsigned long bgpio_read8(void __iomem *reg)
+{
+ return readb(reg);
+}
+
+static void bgpio_write16(void __iomem *reg, unsigned long data)
+{
+ writew(data, reg);
+}
+
+static unsigned long bgpio_read16(void __iomem *reg)
+{
+ return readw(reg);
+}
+
+static void bgpio_write32(void __iomem *reg, unsigned long data)
+{
+ writel(data, reg);
+}
+
+static unsigned long bgpio_read32(void __iomem *reg)
+{
+ return readl(reg);
+}
+
+#if BITS_PER_LONG >= 64
+static void bgpio_write64(void __iomem *reg, unsigned long data)
+{
+ writeq(data, reg);
+}
+
+static unsigned long bgpio_read64(void __iomem *reg)
+{
+ return readq(reg);
+}
+#endif /* BITS_PER_LONG >= 64 */
+
+static void bgpio_write16be(void __iomem *reg, unsigned long data)
+{
+ iowrite16be(data, reg);
+}
+
+static unsigned long bgpio_read16be(void __iomem *reg)
+{
+ return ioread16be(reg);
+}
+
+static void bgpio_write32be(void __iomem *reg, unsigned long data)
+{
+ iowrite32be(data, reg);
+}
+
+static unsigned long bgpio_read32be(void __iomem *reg)
+{
+ return ioread32be(reg);
+}
+
+static unsigned long bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin)
+{
+ return 1 << pin;
+}
+
+static unsigned long bgpio_pin2mask_be(struct bgpio_chip *bgc,
+ unsigned int pin)
+{
+ return 1 << (bgc->bits - 1 - pin);
+}
+
+static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ return !!(bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio));
+}
+
+static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long mask = bgc->pin2mask(bgc, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ if (val)
+ bgc->data |= mask;
+ else
+ bgc->data &= ~mask;
+
+ bgc->write_reg(bgc->reg_dat, bgc->data);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long mask = bgc->pin2mask(bgc, gpio);
+
+ if (val)
+ bgc->write_reg(bgc->reg_set, mask);
+ else
+ bgc->write_reg(bgc->reg_clr, mask);
+}
+
+static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long mask = bgc->pin2mask(bgc, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ if (val)
+ bgc->data |= mask;
+ else
+ bgc->data &= ~mask;
+
+ bgc->write_reg(bgc->reg_set, bgc->data);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void bgpio_multiple_get_masks(struct bgpio_chip *bgc,
+ unsigned long *mask, unsigned long *bits,
+ unsigned long *set_mask,
+ unsigned long *clear_mask)
+{
+ int i;
+
+ *set_mask = 0;
+ *clear_mask = 0;
+
+ for (i = 0; i < bgc->bits; i++) {
+ if (*mask == 0)
+ break;
+ if (__test_and_clear_bit(i, mask)) {
+ if (test_bit(i, bits))
+ *set_mask |= bgc->pin2mask(bgc, i);
+ else
+ *clear_mask |= bgc->pin2mask(bgc, i);
+ }
+ }
+}
+
+static void bgpio_set_multiple_single_reg(struct bgpio_chip *bgc,
+ unsigned long *mask,
+ unsigned long *bits,
+ void __iomem *reg)
+{
+ unsigned long flags;
+ unsigned long set_mask, clear_mask;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgpio_multiple_get_masks(bgc, mask, bits, &set_mask, &clear_mask);
+
+ bgc->data |= set_mask;
+ bgc->data &= ~clear_mask;
+
+ bgc->write_reg(reg, bgc->data);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ bgpio_set_multiple_single_reg(bgc, mask, bits, bgc->reg_dat);
+}
+
+static void bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ bgpio_set_multiple_single_reg(bgc, mask, bits, bgc->reg_set);
+}
+
+static void bgpio_set_multiple_with_clear(struct gpio_chip *gc,
+ unsigned long *mask,
+ unsigned long *bits)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long set_mask, clear_mask;
+
+ bgpio_multiple_get_masks(bgc, mask, bits, &set_mask, &clear_mask);
+
+ if (set_mask)
+ bgc->write_reg(bgc->reg_set, set_mask);
+ if (clear_mask)
+ bgc->write_reg(bgc->reg_clr, clear_mask);
+}
+
+static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ return 0;
+}
+
+static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ gc->set(gc, gpio, val);
+
+ return 0;
+}
+
+static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->dir &= ~bgc->pin2mask(bgc, gpio);
+ bgc->write_reg(bgc->reg_dir, bgc->dir);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
+
+ gc->set(gc, gpio, val);
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->dir |= bgc->pin2mask(bgc, gpio);
+ bgc->write_reg(bgc->reg_dir, bgc->dir);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->dir |= bgc->pin2mask(bgc, gpio);
+ bgc->write_reg(bgc->reg_dir, bgc->dir);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
+
+ gc->set(gc, gpio, val);
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->dir &= ~bgc->pin2mask(bgc, gpio);
+ bgc->write_reg(bgc->reg_dir, bgc->dir);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int bgpio_setup_accessors(struct device *dev,
+ struct bgpio_chip *bgc,
+ bool bit_be,
+ bool byte_be)
+{
+
+ switch (bgc->bits) {
+ case 8:
+ bgc->read_reg = bgpio_read8;
+ bgc->write_reg = bgpio_write8;
+ break;
+ case 16:
+ if (byte_be) {
+ bgc->read_reg = bgpio_read16be;
+ bgc->write_reg = bgpio_write16be;
+ } else {
+ bgc->read_reg = bgpio_read16;
+ bgc->write_reg = bgpio_write16;
+ }
+ break;
+ case 32:
+ if (byte_be) {
+ bgc->read_reg = bgpio_read32be;
+ bgc->write_reg = bgpio_write32be;
+ } else {
+ bgc->read_reg = bgpio_read32;
+ bgc->write_reg = bgpio_write32;
+ }
+ break;
+#if BITS_PER_LONG >= 64
+ case 64:
+ if (byte_be) {
+ dev_err(dev,
+ "64 bit big endian byte order unsupported\n");
+ return -EINVAL;
+ } else {
+ bgc->read_reg = bgpio_read64;
+ bgc->write_reg = bgpio_write64;
+ }
+ break;
+#endif /* BITS_PER_LONG >= 64 */
+ default:
+ dev_err(dev, "unsupported data width %u bits\n", bgc->bits);
+ return -EINVAL;
+ }
+
+ bgc->pin2mask = bit_be ? bgpio_pin2mask_be : bgpio_pin2mask;
+
+ return 0;
+}
+
+/*
+ * Create the device and allocate the resources. For setting GPIO's there are
+ * three supported configurations:
+ *
+ * - single input/output register resource (named "dat").
+ * - set/clear pair (named "set" and "clr").
+ * - single output register resource and single input resource ("set" and
+ * dat").
+ *
+ * For the single output register, this drives a 1 by setting a bit and a zero
+ * by clearing a bit. For the set clr pair, this drives a 1 by setting a bit
+ * in the set register and clears it by setting a bit in the clear register.
+ * The configuration is detected by which resources are present.
+ *
+ * For setting the GPIO direction, there are three supported configurations:
+ *
+ * - simple bidirection GPIO that requires no configuration.
+ * - an output direction register (named "dirout") where a 1 bit
+ * indicates the GPIO is an output.
+ * - an input direction register (named "dirin") where a 1 bit indicates
+ * the GPIO is an input.
+ */
+static int bgpio_setup_io(struct bgpio_chip *bgc,
+ void __iomem *dat,
+ void __iomem *set,
+ void __iomem *clr)
+{
+
+ bgc->reg_dat = dat;
+ if (!bgc->reg_dat)
+ return -EINVAL;
+
+ if (set && clr) {
+ bgc->reg_set = set;
+ bgc->reg_clr = clr;
+ bgc->gc.set = bgpio_set_with_clear;
+ bgc->gc.set_multiple = bgpio_set_multiple_with_clear;
+ } else if (set && !clr) {
+ bgc->reg_set = set;
+ bgc->gc.set = bgpio_set_set;
+ bgc->gc.set_multiple = bgpio_set_multiple_set;
+ } else {
+ bgc->gc.set = bgpio_set;
+ bgc->gc.set_multiple = bgpio_set_multiple;
+ }
+
+ bgc->gc.get = bgpio_get;
+
+ return 0;
+}
+
+static int bgpio_setup_direction(struct bgpio_chip *bgc,
+ void __iomem *dirout,
+ void __iomem *dirin)
+{
+ if (dirout && dirin) {
+ return -EINVAL;
+ } else if (dirout) {
+ bgc->reg_dir = dirout;
+ bgc->gc.direction_output = bgpio_dir_out;
+ bgc->gc.direction_input = bgpio_dir_in;
+ } else if (dirin) {
+ bgc->reg_dir = dirin;
+ bgc->gc.direction_output = bgpio_dir_out_inv;
+ bgc->gc.direction_input = bgpio_dir_in_inv;
+ } else {
+ bgc->gc.direction_output = bgpio_simple_dir_out;
+ bgc->gc.direction_input = bgpio_simple_dir_in;
+ }
+
+ return 0;
+}
+
+static int bgpio_request(struct gpio_chip *chip, unsigned gpio_pin)
+{
+ if (gpio_pin < chip->ngpio)
+ return 0;
+
+ return -EINVAL;
+}
+
+int bgpio_remove(struct bgpio_chip *bgc)
+{
+ gpiochip_remove(&bgc->gc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bgpio_remove);
+
+int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
+ unsigned long sz, void __iomem *dat, void __iomem *set,
+ void __iomem *clr, void __iomem *dirout, void __iomem *dirin,
+ unsigned long flags)
+{
+ int ret;
+
+ if (!is_power_of_2(sz))
+ return -EINVAL;
+
+ bgc->bits = sz * 8;
+ if (bgc->bits > BITS_PER_LONG)
+ return -EINVAL;
+
+ spin_lock_init(&bgc->lock);
+ bgc->gc.dev = dev;
+ bgc->gc.label = dev_name(dev);
+ bgc->gc.base = -1;
+ bgc->gc.ngpio = bgc->bits;
+ bgc->gc.request = bgpio_request;
+
+ ret = bgpio_setup_io(bgc, dat, set, clr);
+ if (ret)
+ return ret;
+
+ ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN,
+ flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER);
+ if (ret)
+ return ret;
+
+ ret = bgpio_setup_direction(bgc, dirout, dirin);
+ if (ret)
+ return ret;
+
+ bgc->data = bgc->read_reg(bgc->reg_dat);
+ if (bgc->gc.set == bgpio_set_set &&
+ !(flags & BGPIOF_UNREADABLE_REG_SET))
+ bgc->data = bgc->read_reg(bgc->reg_set);
+ if (bgc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR))
+ bgc->dir = bgc->read_reg(bgc->reg_dir);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(bgpio_init);
+
+#ifdef CONFIG_GPIO_GENERIC_PLATFORM
+
+static void __iomem *bgpio_map(struct platform_device *pdev,
+ const char *name,
+ resource_size_t sane_sz,
+ int *err)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ resource_size_t start;
+ resource_size_t sz;
+ void __iomem *ret;
+
+ *err = 0;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!r)
+ return NULL;
+
+ sz = resource_size(r);
+ if (sz != sane_sz) {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ start = r->start;
+ if (!devm_request_mem_region(dev, start, sz, r->name)) {
+ *err = -EBUSY;
+ return NULL;
+ }
+
+ ret = devm_ioremap(dev, start, sz);
+ if (!ret) {
+ *err = -ENOMEM;
+ return NULL;
+ }
+
+ return ret;
+}
+
+static int bgpio_pdev_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ void __iomem *dat;
+ void __iomem *set;
+ void __iomem *clr;
+ void __iomem *dirout;
+ void __iomem *dirin;
+ unsigned long sz;
+ unsigned long flags = pdev->id_entry->driver_data;
+ int err;
+ struct bgpio_chip *bgc;
+ struct bgpio_pdata *pdata = dev_get_platdata(dev);
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
+ if (!r)
+ return -EINVAL;
+
+ sz = resource_size(r);
+
+ dat = bgpio_map(pdev, "dat", sz, &err);
+ if (!dat)
+ return err ? err : -EINVAL;
+
+ set = bgpio_map(pdev, "set", sz, &err);
+ if (err)
+ return err;
+
+ clr = bgpio_map(pdev, "clr", sz, &err);
+ if (err)
+ return err;
+
+ dirout = bgpio_map(pdev, "dirout", sz, &err);
+ if (err)
+ return err;
+
+ dirin = bgpio_map(pdev, "dirin", sz, &err);
+ if (err)
+ return err;
+
+ bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
+ if (!bgc)
+ return -ENOMEM;
+
+ err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, flags);
+ if (err)
+ return err;
+
+ if (pdata) {
+ if (pdata->label)
+ bgc->gc.label = pdata->label;
+ bgc->gc.base = pdata->base;
+ if (pdata->ngpio > 0)
+ bgc->gc.ngpio = pdata->ngpio;
+ }
+
+ platform_set_drvdata(pdev, bgc);
+
+ return gpiochip_add(&bgc->gc);
+}
+
+static int bgpio_pdev_remove(struct platform_device *pdev)
+{
+ struct bgpio_chip *bgc = platform_get_drvdata(pdev);
+
+ return bgpio_remove(bgc);
+}
+
+static const struct platform_device_id bgpio_id_table[] = {
+ {
+ .name = "basic-mmio-gpio",
+ .driver_data = 0,
+ }, {
+ .name = "basic-mmio-gpio-be",
+ .driver_data = BGPIOF_BIG_ENDIAN,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, bgpio_id_table);
+
+static struct platform_driver bgpio_driver = {
+ .driver = {
+ .name = "basic-mmio-gpio",
+ },
+ .id_table = bgpio_id_table,
+ .probe = bgpio_pdev_probe,
+ .remove = bgpio_pdev_remove,
+};
+
+module_platform_driver(bgpio_driver);
+
+#endif /* CONFIG_GPIO_GENERIC_PLATFORM */
+
+MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers");
+MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c
new file mode 100644
index 000000000..35a02770c
--- /dev/null
+++ b/drivers/gpio/gpio-grgpio.c
@@ -0,0 +1,504 @@
+/*
+ * Driver for Aeroflex Gaisler GRGPIO General Purpose I/O cores.
+ *
+ * 2013 (c) Aeroflex Gaisler AB
+ *
+ * This driver supports the GRGPIO GPIO core available in the GRLIB VHDL
+ * IP core library.
+ *
+ * Full documentation of the GRGPIO core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * See "Documentation/devicetree/bindings/gpio/gpio-grgpio.txt" for
+ * information on open firmware properties.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Contributors: Andreas Larsson <andreas@gaisler.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+
+#define GRGPIO_MAX_NGPIO 32
+
+#define GRGPIO_DATA 0x00
+#define GRGPIO_OUTPUT 0x04
+#define GRGPIO_DIR 0x08
+#define GRGPIO_IMASK 0x0c
+#define GRGPIO_IPOL 0x10
+#define GRGPIO_IEDGE 0x14
+#define GRGPIO_BYPASS 0x18
+#define GRGPIO_IMAP_BASE 0x20
+
+/* Structure for an irq of the core - called an underlying irq */
+struct grgpio_uirq {
+ u8 refcnt; /* Reference counter to manage requesting/freeing of uirq */
+ u8 uirq; /* Underlying irq of the gpio driver */
+};
+
+/*
+ * Structure for an irq of a gpio line handed out by this driver. The index is
+ * used to map to the corresponding underlying irq.
+ */
+struct grgpio_lirq {
+ s8 index; /* Index into struct grgpio_priv's uirqs, or -1 */
+ u8 irq; /* irq for the gpio line */
+};
+
+struct grgpio_priv {
+ struct bgpio_chip bgc;
+ void __iomem *regs;
+ struct device *dev;
+
+ u32 imask; /* irq mask shadow register */
+
+ /*
+ * The grgpio core can have multiple "underlying" irqs. The gpio lines
+ * can be mapped to any one or none of these underlying irqs
+ * independently of each other. This driver sets up an irq domain and
+ * hands out separate irqs to each gpio line
+ */
+ struct irq_domain *domain;
+
+ /*
+ * This array contains information on each underlying irq, each
+ * irq of the grgpio core itself.
+ */
+ struct grgpio_uirq uirqs[GRGPIO_MAX_NGPIO];
+
+ /*
+ * This array contains information for each gpio line on the irqs
+ * obtains from this driver. An index value of -1 for a certain gpio
+ * line indicates that the line has no irq. Otherwise the index connects
+ * the irq to the underlying irq by pointing into the uirqs array.
+ */
+ struct grgpio_lirq lirqs[GRGPIO_MAX_NGPIO];
+};
+
+static inline struct grgpio_priv *grgpio_gc_to_priv(struct gpio_chip *gc)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ return container_of(bgc, struct grgpio_priv, bgc);
+}
+
+static void grgpio_set_imask(struct grgpio_priv *priv, unsigned int offset,
+ int val)
+{
+ struct bgpio_chip *bgc = &priv->bgc;
+ unsigned long mask = bgc->pin2mask(bgc, offset);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ if (val)
+ priv->imask |= mask;
+ else
+ priv->imask &= ~mask;
+ bgc->write_reg(priv->regs + GRGPIO_IMASK, priv->imask);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct grgpio_priv *priv = grgpio_gc_to_priv(gc);
+
+ if (offset >= gc->ngpio)
+ return -ENXIO;
+
+ if (priv->lirqs[offset].index < 0)
+ return -ENXIO;
+
+ return irq_create_mapping(priv->domain, offset);
+}
+
+/* -------------------- IRQ chip functions -------------------- */
+
+static int grgpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
+ unsigned long flags;
+ u32 mask = BIT(d->hwirq);
+ u32 ipol;
+ u32 iedge;
+ u32 pol;
+ u32 edge;
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_LOW:
+ pol = 0;
+ edge = 0;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ pol = mask;
+ edge = 0;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ pol = 0;
+ edge = mask;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ pol = mask;
+ edge = mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ ipol = priv->bgc.read_reg(priv->regs + GRGPIO_IPOL) & ~mask;
+ iedge = priv->bgc.read_reg(priv->regs + GRGPIO_IEDGE) & ~mask;
+
+ priv->bgc.write_reg(priv->regs + GRGPIO_IPOL, ipol | pol);
+ priv->bgc.write_reg(priv->regs + GRGPIO_IEDGE, iedge | edge);
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ return 0;
+}
+
+static void grgpio_irq_mask(struct irq_data *d)
+{
+ struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
+ int offset = d->hwirq;
+
+ grgpio_set_imask(priv, offset, 0);
+}
+
+static void grgpio_irq_unmask(struct irq_data *d)
+{
+ struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
+ int offset = d->hwirq;
+
+ grgpio_set_imask(priv, offset, 1);
+}
+
+static struct irq_chip grgpio_irq_chip = {
+ .name = "grgpio",
+ .irq_mask = grgpio_irq_mask,
+ .irq_unmask = grgpio_irq_unmask,
+ .irq_set_type = grgpio_irq_set_type,
+};
+
+static irqreturn_t grgpio_irq_handler(int irq, void *dev)
+{
+ struct grgpio_priv *priv = dev;
+ int ngpio = priv->bgc.gc.ngpio;
+ unsigned long flags;
+ int i;
+ int match = 0;
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ /*
+ * For each gpio line, call its interrupt handler if it its underlying
+ * irq matches the current irq that is handled.
+ */
+ for (i = 0; i < ngpio; i++) {
+ struct grgpio_lirq *lirq = &priv->lirqs[i];
+
+ if (priv->imask & BIT(i) && lirq->index >= 0 &&
+ priv->uirqs[lirq->index].uirq == irq) {
+ generic_handle_irq(lirq->irq);
+ match = 1;
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ if (!match)
+ dev_warn(priv->dev, "No gpio line matched irq %d\n", irq);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * This function will be called as a consequence of the call to
+ * irq_create_mapping in grgpio_to_irq
+ */
+static int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct grgpio_priv *priv = d->host_data;
+ struct grgpio_lirq *lirq;
+ struct grgpio_uirq *uirq;
+ unsigned long flags;
+ int offset = hwirq;
+ int ret = 0;
+
+ if (!priv)
+ return -EINVAL;
+
+ lirq = &priv->lirqs[offset];
+ if (lirq->index < 0)
+ return -EINVAL;
+
+ dev_dbg(priv->dev, "Mapping irq %d for gpio line %d\n",
+ irq, offset);
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ /* Request underlying irq if not already requested */
+ lirq->irq = irq;
+ uirq = &priv->uirqs[lirq->index];
+ if (uirq->refcnt == 0) {
+ 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->bgc.lock, flags);
+
+ return ret;
+ }
+ }
+ uirq->refcnt++;
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ /* Setup irq */
+ irq_set_chip_data(irq, priv);
+ irq_set_chip_and_handler(irq, &grgpio_irq_chip,
+ handle_simple_irq);
+ irq_clear_status_flags(irq, IRQ_NOREQUEST);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+
+ return ret;
+}
+
+static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+ struct grgpio_priv *priv = d->host_data;
+ int index;
+ struct grgpio_lirq *lirq;
+ struct grgpio_uirq *uirq;
+ unsigned long flags;
+ int ngpio = priv->bgc.gc.ngpio;
+ int i;
+
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ /* Free underlying irq if last user unmapped */
+ index = -1;
+ for (i = 0; i < ngpio; i++) {
+ lirq = &priv->lirqs[i];
+ if (lirq->irq == irq) {
+ grgpio_set_imask(priv, i, 0);
+ lirq->irq = 0;
+ index = lirq->index;
+ break;
+ }
+ }
+ WARN_ON(index < 0);
+
+ if (index >= 0) {
+ uirq = &priv->uirqs[lirq->index];
+ uirq->refcnt--;
+ if (uirq->refcnt == 0)
+ free_irq(uirq->uirq, priv);
+ }
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+}
+
+static struct irq_domain_ops grgpio_irq_domain_ops = {
+ .map = grgpio_irq_map,
+ .unmap = grgpio_irq_unmap,
+};
+
+/* ------------------------------------------------------------ */
+
+static int grgpio_probe(struct platform_device *ofdev)
+{
+ struct device_node *np = ofdev->dev.of_node;
+ void __iomem *regs;
+ struct gpio_chip *gc;
+ struct bgpio_chip *bgc;
+ struct grgpio_priv *priv;
+ struct resource *res;
+ int err;
+ u32 prop;
+ s32 *irqmap;
+ int size;
+ int i;
+
+ priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&ofdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ bgc = &priv->bgc;
+ err = bgpio_init(bgc, &ofdev->dev, 4, regs + GRGPIO_DATA,
+ regs + GRGPIO_OUTPUT, NULL, regs + GRGPIO_DIR, NULL,
+ BGPIOF_BIG_ENDIAN_BYTE_ORDER);
+ if (err) {
+ dev_err(&ofdev->dev, "bgpio_init() failed\n");
+ return err;
+ }
+
+ priv->regs = regs;
+ priv->imask = bgc->read_reg(regs + GRGPIO_IMASK);
+ priv->dev = &ofdev->dev;
+
+ gc = &bgc->gc;
+ gc->of_node = np;
+ gc->owner = THIS_MODULE;
+ gc->to_irq = grgpio_to_irq;
+ gc->label = np->full_name;
+ gc->base = -1;
+
+ err = of_property_read_u32(np, "nbits", &prop);
+ if (err || prop <= 0 || prop > GRGPIO_MAX_NGPIO) {
+ gc->ngpio = GRGPIO_MAX_NGPIO;
+ dev_dbg(&ofdev->dev,
+ "No or invalid nbits property: assume %d\n", gc->ngpio);
+ } else {
+ gc->ngpio = prop;
+ }
+
+ /*
+ * The irqmap contains the index values indicating which underlying irq,
+ * if anyone, is connected to that line
+ */
+ irqmap = (s32 *)of_get_property(np, "irqmap", &size);
+ if (irqmap) {
+ if (size < gc->ngpio) {
+ dev_err(&ofdev->dev,
+ "irqmap shorter than ngpio (%d < %d)\n",
+ size, gc->ngpio);
+ return -EINVAL;
+ }
+
+ priv->domain = irq_domain_add_linear(np, gc->ngpio,
+ &grgpio_irq_domain_ops,
+ priv);
+ if (!priv->domain) {
+ dev_err(&ofdev->dev, "Could not add irq domain\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < gc->ngpio; i++) {
+ struct grgpio_lirq *lirq;
+ int ret;
+
+ lirq = &priv->lirqs[i];
+ lirq->index = irqmap[i];
+
+ if (lirq->index < 0)
+ continue;
+
+ ret = platform_get_irq(ofdev, lirq->index);
+ if (ret <= 0) {
+ /*
+ * 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;
+ }
+ }
+
+ platform_set_drvdata(ofdev, priv);
+
+ err = gpiochip_add(gc);
+ if (err) {
+ dev_err(&ofdev->dev, "Could not add gpiochip\n");
+ if (priv->domain)
+ irq_domain_remove(priv->domain);
+ return err;
+ }
+
+ dev_info(&ofdev->dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n",
+ priv->regs, gc->base, gc->ngpio, priv->domain ? "on" : "off");
+
+ return 0;
+}
+
+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->bgc.lock, flags);
+
+ if (priv->domain) {
+ for (i = 0; i < GRGPIO_MAX_NGPIO; i++) {
+ if (priv->uirqs[i].refcnt != 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+ }
+
+ gpiochip_remove(&priv->bgc.gc);
+
+ if (priv->domain)
+ irq_domain_remove(priv->domain);
+
+out:
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ return ret;
+}
+
+static const struct of_device_id grgpio_match[] = {
+ {.name = "GAISLER_GPIO"},
+ {.name = "01_01a"},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, grgpio_match);
+
+static struct platform_driver grgpio_driver = {
+ .driver = {
+ .name = "grgpio",
+ .of_match_table = grgpio_match,
+ },
+ .probe = grgpio_probe,
+ .remove = grgpio_remove,
+};
+module_platform_driver(grgpio_driver);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB.");
+MODULE_DESCRIPTION("Driver for Aeroflex Gaisler GRGPIO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
new file mode 100644
index 000000000..4ba7ed502
--- /dev/null
+++ b/drivers/gpio/gpio-ich.c
@@ -0,0 +1,546 @@
+/*
+ * Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver
+ *
+ * Copyright (C) 2010 Extreme Engineering Solutions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/lpc_ich.h>
+
+#define DRV_NAME "gpio_ich"
+
+/*
+ * GPIO register offsets in GPIO I/O space.
+ * Each chunk of 32 GPIOs is manipulated via its own USE_SELx, IO_SELx, and
+ * LVLx registers. Logic in the read/write functions takes a register and
+ * an absolute bit number and determines the proper register offset and bit
+ * number in that register. For example, to read the value of GPIO bit 50
+ * the code would access offset ichx_regs[2(=GPIO_LVL)][1(=50/32)],
+ * bit 18 (50%32).
+ */
+enum GPIO_REG {
+ GPIO_USE_SEL = 0,
+ GPIO_IO_SEL,
+ GPIO_LVL,
+ GPO_BLINK
+};
+
+static const u8 ichx_regs[4][3] = {
+ {0x00, 0x30, 0x40}, /* USE_SEL[1-3] offsets */
+ {0x04, 0x34, 0x44}, /* IO_SEL[1-3] offsets */
+ {0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */
+ {0x18, 0x18, 0x18}, /* BLINK offset */
+};
+
+static const u8 ichx_reglen[3] = {
+ 0x30, 0x10, 0x10,
+};
+
+static const u8 avoton_regs[4][3] = {
+ {0x00, 0x80, 0x00},
+ {0x04, 0x84, 0x00},
+ {0x08, 0x88, 0x00},
+};
+
+static const u8 avoton_reglen[3] = {
+ 0x10, 0x10, 0x00,
+};
+
+#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start)
+#define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start)
+
+struct ichx_desc {
+ /* Max GPIO pins the chipset can have */
+ uint ngpio;
+
+ /* chipset registers */
+ const u8 (*regs)[3];
+ const u8 *reglen;
+
+ /* GPO_BLINK is available on this chipset */
+ bool have_blink;
+
+ /* Whether the chipset has GPIO in GPE0_STS in the PM IO region */
+ bool uses_gpe0;
+
+ /* USE_SEL is bogus on some chipsets, eg 3100 */
+ 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);
+
+ /*
+ * Some chipsets don't let reading output values on GPIO_LVL register
+ * this option allows driver caching written output values
+ */
+ bool use_outlvl_cache;
+};
+
+static struct {
+ spinlock_t lock;
+ struct platform_device *dev;
+ struct gpio_chip chip;
+ struct resource *gpio_base; /* GPIO IO base */
+ struct resource *pm_base; /* Power Mangagment 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 */
+ int outlvl_cache[3]; /* cached output values */
+} ichx_priv;
+
+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)
+{
+ unsigned long flags;
+ u32 data, tmp;
+ int reg_nr = nr / 32;
+ int bit = nr & 0x1f;
+ int ret = 0;
+
+ spin_lock_irqsave(&ichx_priv.lock, flags);
+
+ if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
+ data = ichx_priv.outlvl_cache[reg_nr];
+ else
+ data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
+ ichx_priv.gpio_base);
+
+ if (val)
+ data |= 1 << bit;
+ else
+ data &= ~(1 << bit);
+ ICHX_WRITE(data, ichx_priv.desc->regs[reg][reg_nr],
+ ichx_priv.gpio_base);
+ if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
+ ichx_priv.outlvl_cache[reg_nr] = data;
+
+ tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
+ ichx_priv.gpio_base);
+ if (verify && data != tmp)
+ ret = -EPERM;
+
+ spin_unlock_irqrestore(&ichx_priv.lock, flags);
+
+ return ret;
+}
+
+static int ichx_read_bit(int reg, unsigned nr)
+{
+ unsigned long flags;
+ u32 data;
+ int reg_nr = nr / 32;
+ int bit = nr & 0x1f;
+
+ spin_lock_irqsave(&ichx_priv.lock, flags);
+
+ data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
+ ichx_priv.gpio_base);
+
+ if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
+ data = ichx_priv.outlvl_cache[reg_nr] | data;
+
+ spin_unlock_irqrestore(&ichx_priv.lock, flags);
+
+ return data & (1 << bit) ? 1 : 0;
+}
+
+static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
+{
+ return !!(ichx_priv.use_gpio & (1 << (nr / 32)));
+}
+
+static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr)
+{
+ return ichx_read_bit(GPIO_IO_SEL, nr) ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
+}
+
+static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ /*
+ * Try setting pin as an input and verify it worked since many pins
+ * are output-only.
+ */
+ if (ichx_write_bit(GPIO_IO_SEL, nr, 1, 1))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+ int val)
+{
+ /* Disable blink hardware which is available for GPIOs from 0 to 31. */
+ if (nr < 32 && ichx_priv.desc->have_blink)
+ ichx_write_bit(GPO_BLINK, nr, 0, 0);
+
+ /* Set GPIO output value. */
+ ichx_write_bit(GPIO_LVL, nr, val, 0);
+
+ /*
+ * Try setting pin as an output and verify it worked since many pins
+ * are input-only.
+ */
+ if (ichx_write_bit(GPIO_IO_SEL, nr, 0, 1))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
+{
+ return ichx_read_bit(GPIO_LVL, nr);
+}
+
+static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
+{
+ unsigned long flags;
+ u32 data;
+
+ /*
+ * GPI 0 - 15 need to be read from the power management registers on
+ * a ICH6/3100 bridge.
+ */
+ if (nr < 16) {
+ if (!ichx_priv.pm_base)
+ return -ENXIO;
+
+ spin_lock_irqsave(&ichx_priv.lock, flags);
+
+ /* GPI 0 - 15 are latched, write 1 to clear*/
+ ICHX_WRITE(1 << (16 + nr), 0, ichx_priv.pm_base);
+ data = ICHX_READ(0, ichx_priv.pm_base);
+
+ spin_unlock_irqrestore(&ichx_priv.lock, flags);
+
+ return (data >> 16) & (1 << nr) ? 1 : 0;
+ } else {
+ return ichx_gpio_get(chip, nr);
+ }
+}
+
+static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)
+{
+ if (!ichx_gpio_check_available(chip, nr))
+ return -ENXIO;
+
+ /*
+ * Note we assume the BIOS properly set a bridge's USE value. Some
+ * chips (eg Intel 3100) have bogus USE values though, so first see if
+ * the chipset's USE value can be trusted for this specific bit.
+ * If it can't be trusted, assume that the pin can be used as a GPIO.
+ */
+ if (ichx_priv.desc->use_sel_ignore[nr / 32] & (1 << (nr & 0x1f)))
+ return 0;
+
+ return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV;
+}
+
+static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr)
+{
+ /*
+ * Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100
+ * bridge as they are controlled by USE register bits 0 and 1. See
+ * "Table 704 GPIO_USE_SEL1 register" in the i3100 datasheet for
+ * additional info.
+ */
+ if (nr == 16 || nr == 17)
+ nr -= 16;
+
+ return ichx_gpio_request(chip, nr);
+}
+
+static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
+{
+ ichx_write_bit(GPIO_LVL, nr, val, 0);
+}
+
+static void ichx_gpiolib_setup(struct gpio_chip *chip)
+{
+ chip->owner = THIS_MODULE;
+ chip->label = DRV_NAME;
+ chip->dev = &ichx_priv.dev->dev;
+
+ /* Allow chip-specific overrides of request()/get() */
+ chip->request = ichx_priv.desc->request ?
+ ichx_priv.desc->request : ichx_gpio_request;
+ chip->get = ichx_priv.desc->get ?
+ ichx_priv.desc->get : ichx_gpio_get;
+
+ chip->set = ichx_gpio_set;
+ chip->get_direction = ichx_gpio_get_direction;
+ chip->direction_input = ichx_gpio_direction_input;
+ chip->direction_output = ichx_gpio_direction_output;
+ chip->base = modparam_gpiobase;
+ chip->ngpio = ichx_priv.desc->ngpio;
+ chip->can_sleep = false;
+ chip->dbg_show = NULL;
+}
+
+/* ICH6-based, 631xesb-based */
+static struct ichx_desc ich6_desc = {
+ /* Bridges using the ICH6 controller need fixups for GPIO 0 - 17 */
+ .request = ich6_gpio_request,
+ .get = ich6_gpio_get,
+
+ /* GPIO 0-15 are read in the GPE0_STS PM register */
+ .uses_gpe0 = true,
+
+ .ngpio = 50,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
+};
+
+/* Intel 3100 */
+static struct ichx_desc i3100_desc = {
+ /*
+ * Bits 16,17, 20 of USE_SEL and bit 16 of USE_SEL2 always read 0 on
+ * the Intel 3100. See "Table 712. GPIO Summary Table" of 3100
+ * Datasheet for more info.
+ */
+ .use_sel_ignore = {0x00130000, 0x00010000, 0x0},
+
+ /* The 3100 needs fixups for GPIO 0 - 17 */
+ .request = ich6_gpio_request,
+ .get = ich6_gpio_get,
+
+ /* GPIO 0-15 are read in the GPE0_STS PM register */
+ .uses_gpe0 = true,
+
+ .ngpio = 50,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
+};
+
+/* ICH7 and ICH8-based */
+static struct ichx_desc ich7_desc = {
+ .ngpio = 50,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
+};
+
+/* ICH9-based */
+static struct ichx_desc ich9_desc = {
+ .ngpio = 61,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
+};
+
+/* ICH10-based - Consumer/corporate versions have different amount of GPIO */
+static struct ichx_desc ich10_cons_desc = {
+ .ngpio = 61,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
+};
+static struct ichx_desc ich10_corp_desc = {
+ .ngpio = 72,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
+};
+
+/* Intel 5 series, 6 series, 3400 series, and C200 series */
+static struct ichx_desc intel5_desc = {
+ .ngpio = 76,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
+};
+
+/* Avoton */
+static struct ichx_desc avoton_desc = {
+ /* Avoton has only 59 GPIOs, but we assume the first set of register
+ * (Core) has 32 instead of 31 to keep gpio-ich compliance
+ */
+ .ngpio = 60,
+ .regs = avoton_regs,
+ .reglen = avoton_reglen,
+ .use_outlvl_cache = true,
+};
+
+static int ichx_gpio_request_regions(struct resource *res_base,
+ const char *name, u8 use_gpio)
+{
+ int i;
+
+ if (!res_base || !res_base->start || !res_base->end)
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
+ if (!(use_gpio & (1 << i)))
+ continue;
+ if (!request_region(
+ res_base->start + ichx_priv.desc->regs[0][i],
+ ichx_priv.desc->reglen[i], name))
+ goto request_err;
+ }
+ return 0;
+
+request_err:
+ /* Clean up: release already requested regions, if any */
+ for (i--; i >= 0; i--) {
+ if (!(use_gpio & (1 << i)))
+ continue;
+ release_region(res_base->start + ichx_priv.desc->regs[0][i],
+ ichx_priv.desc->reglen[i]);
+ }
+ return -EBUSY;
+}
+
+static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
+ if (!(use_gpio & (1 << i)))
+ continue;
+ release_region(res_base->start + ichx_priv.desc->regs[0][i],
+ ichx_priv.desc->reglen[i]);
+ }
+}
+
+static int ichx_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res_base, *res_pm;
+ int err;
+ struct lpc_ich_info *ich_info = dev_get_platdata(&pdev->dev);
+
+ if (!ich_info)
+ return -ENODEV;
+
+ ichx_priv.dev = pdev;
+
+ switch (ich_info->gpio_version) {
+ case ICH_I3100_GPIO:
+ ichx_priv.desc = &i3100_desc;
+ break;
+ case ICH_V5_GPIO:
+ ichx_priv.desc = &intel5_desc;
+ break;
+ case ICH_V6_GPIO:
+ ichx_priv.desc = &ich6_desc;
+ break;
+ case ICH_V7_GPIO:
+ ichx_priv.desc = &ich7_desc;
+ break;
+ case ICH_V9_GPIO:
+ ichx_priv.desc = &ich9_desc;
+ break;
+ case ICH_V10CORP_GPIO:
+ ichx_priv.desc = &ich10_corp_desc;
+ break;
+ case ICH_V10CONS_GPIO:
+ ichx_priv.desc = &ich10_cons_desc;
+ break;
+ case AVOTON_GPIO:
+ ichx_priv.desc = &avoton_desc;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ spin_lock_init(&ichx_priv.lock);
+ res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
+ ichx_priv.use_gpio = ich_info->use_gpio;
+ err = ichx_gpio_request_regions(res_base, pdev->name,
+ ichx_priv.use_gpio);
+ if (err)
+ return err;
+
+ ichx_priv.gpio_base = res_base;
+
+ /*
+ * If necessary, determine the I/O address of ACPI/power management
+ * registers which are needed to read the the GPE0 register for GPI pins
+ * 0 - 15 on some chipsets.
+ */
+ if (!ichx_priv.desc->uses_gpe0)
+ goto init;
+
+ res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0);
+ if (!res_pm) {
+ pr_warn("ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");
+ goto init;
+ }
+
+ if (!request_region(res_pm->start, resource_size(res_pm),
+ pdev->name)) {
+ pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n");
+ goto init;
+ }
+
+ ichx_priv.pm_base = res_pm;
+
+init:
+ ichx_gpiolib_setup(&ichx_priv.chip);
+ err = gpiochip_add(&ichx_priv.chip);
+ if (err) {
+ pr_err("Failed to register GPIOs\n");
+ goto add_err;
+ }
+
+ pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base,
+ ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME);
+
+ return 0;
+
+add_err:
+ ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
+ if (ichx_priv.pm_base)
+ release_region(ichx_priv.pm_base->start,
+ resource_size(ichx_priv.pm_base));
+ return err;
+}
+
+static int ichx_gpio_remove(struct platform_device *pdev)
+{
+ gpiochip_remove(&ichx_priv.chip);
+
+ ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
+ if (ichx_priv.pm_base)
+ release_region(ichx_priv.pm_base->start,
+ resource_size(ichx_priv.pm_base));
+
+ return 0;
+}
+
+static struct platform_driver ichx_gpio_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = ichx_gpio_probe,
+ .remove = ichx_gpio_remove,
+};
+
+module_platform_driver(ichx_gpio_driver);
+
+MODULE_AUTHOR("Peter Tyser <ptyser@xes-inc.com>");
+MODULE_DESCRIPTION("GPIO interface for Intel ICH series");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c
new file mode 100644
index 000000000..aa28c65eb
--- /dev/null
+++ b/drivers/gpio/gpio-intel-mid.c
@@ -0,0 +1,453 @@
+/*
+ * Intel MID GPIO driver
+ *
+ * Copyright (c) 2008-2014 Intel Corporation.
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* Supports:
+ * Moorestown platform Langwell chip.
+ * Medfield platform Penwell chip.
+ * Clovertrail platform Cloverview chip.
+ * Merrifield platform Tangier chip.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/stddef.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/gpio/driver.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.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 gplr_offset; /* offset of first GPLR register from base */
+ u32 flis_base; /* base address of FLIS registers */
+ u32 flis_len; /* length of FLIS registers */
+ u32 (*get_flis_offset)(int gpio);
+ 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 inline struct intel_mid_gpio *to_intel_gpio_priv(struct gpio_chip *gc)
+{
+ return container_of(gc, struct intel_mid_gpio, chip);
+}
+
+static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
+ enum GPIO_REG reg_type)
+{
+ struct intel_mid_gpio *priv = to_intel_gpio_priv(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 = to_intel_gpio_priv(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 = to_intel_gpio_priv(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 = to_intel_gpio_priv(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 = to_intel_gpio_priv(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 intel_mid_gpio_ddata gpio_tangier = {
+ .ngpio = 192,
+ .gplr_offset = 4,
+ .flis_base = 0xff0c0000,
+ .flis_len = 0x8000,
+ .get_flis_offset = NULL,
+ .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,
+ },
+ {
+ /* Tangier */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1199),
+ .driver_data = (kernel_ulong_t)&gpio_tangier,
+ },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, intel_gpio_ids);
+
+static void intel_mid_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct intel_mid_gpio *priv = to_intel_gpio_priv(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->irqdomain,
+ 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 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) {
+ dev_err(&pdev->dev, "can't allocate chip data\n");
+ return -ENOMEM;
+ }
+
+ priv->reg_base = pcim_iomap_table(pdev)[0];
+ priv->chip.label = dev_name(&pdev->dev);
+ priv->chip.dev = &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 = gpiochip_add(&priv->chip);
+ 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,
+ },
+};
+
+static int __init intel_gpio_init(void)
+{
+ return pci_register_driver(&intel_gpio_driver);
+}
+
+device_initcall(intel_gpio_init);
diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c
new file mode 100644
index 000000000..2ed0237a8
--- /dev/null
+++ b/drivers/gpio/gpio-iop.c
@@ -0,0 +1,131 @@
+/*
+ * arch/arm/plat-iop/gpio.c
+ * GPIO handling for Intel IOP3xx processors.
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#define IOP3XX_N_GPIOS 8
+
+#define GPIO_IN 0
+#define GPIO_OUT 1
+#define GPIO_LOW 0
+#define GPIO_HIGH 1
+
+/* Memory base offset */
+static void __iomem *base;
+
+#define IOP3XX_GPIO_REG(reg) (base + (reg))
+#define IOP3XX_GPOE IOP3XX_GPIO_REG(0x0000)
+#define IOP3XX_GPID IOP3XX_GPIO_REG(0x0004)
+#define IOP3XX_GPOD IOP3XX_GPIO_REG(0x0008)
+
+static void gpio_line_config(int line, int direction)
+{
+ unsigned long flags;
+ u32 val;
+
+ local_irq_save(flags);
+ val = readl(IOP3XX_GPOE);
+ if (direction == GPIO_IN) {
+ val |= BIT(line);
+ } else if (direction == GPIO_OUT) {
+ val &= ~BIT(line);
+ }
+ writel(val, IOP3XX_GPOE);
+ local_irq_restore(flags);
+}
+
+static int gpio_line_get(int line)
+{
+ return !!(readl(IOP3XX_GPID) & BIT(line));
+}
+
+static void gpio_line_set(int line, int value)
+{
+ unsigned long flags;
+ u32 val;
+
+ local_irq_save(flags);
+ val = readl(IOP3XX_GPOD);
+ if (value == GPIO_LOW) {
+ val &= ~BIT(line);
+ } else if (value == GPIO_HIGH) {
+ val |= BIT(line);
+ }
+ writel(val, IOP3XX_GPOD);
+ local_irq_restore(flags);
+}
+
+static int iop3xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ gpio_line_config(gpio, GPIO_IN);
+ return 0;
+}
+
+static int iop3xx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level)
+{
+ gpio_line_set(gpio, level);
+ gpio_line_config(gpio, GPIO_OUT);
+ return 0;
+}
+
+static int iop3xx_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ return gpio_line_get(gpio);
+}
+
+static void iop3xx_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ gpio_line_set(gpio, value);
+}
+
+static struct gpio_chip iop3xx_chip = {
+ .label = "iop3xx",
+ .direction_input = iop3xx_gpio_direction_input,
+ .get = iop3xx_gpio_get_value,
+ .direction_output = iop3xx_gpio_direction_output,
+ .set = iop3xx_gpio_set_value,
+ .base = 0,
+ .ngpio = IOP3XX_N_GPIOS,
+};
+
+static int iop3xx_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ return gpiochip_add(&iop3xx_chip);
+}
+
+static struct platform_driver iop3xx_gpio_driver = {
+ .driver = {
+ .name = "gpio-iop",
+ },
+ .probe = iop3xx_gpio_probe,
+};
+
+static int __init iop3xx_gpio_init(void)
+{
+ return platform_driver_register(&iop3xx_gpio_driver);
+}
+arch_initcall(iop3xx_gpio_init);
diff --git a/drivers/gpio/gpio-it8761e.c b/drivers/gpio/gpio-it8761e.c
new file mode 100644
index 000000000..dadfc245c
--- /dev/null
+++ b/drivers/gpio/gpio-it8761e.c
@@ -0,0 +1,230 @@
+/*
+ * GPIO interface for IT8761E Super I/O chip
+ *
+ * Author: Denis Turischev <denis@compulab.co.il>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+
+#include <linux/gpio.h>
+
+#define SIO_CHIP_ID 0x8761
+#define CHIP_ID_HIGH_BYTE 0x20
+#define CHIP_ID_LOW_BYTE 0x21
+
+static u8 ports[2] = { 0x2e, 0x4e };
+static u8 port;
+
+static DEFINE_SPINLOCK(sio_lock);
+
+#define GPIO_NAME "it8761-gpio"
+#define GPIO_BA_HIGH_BYTE 0x60
+#define GPIO_BA_LOW_BYTE 0x61
+#define GPIO_IOSIZE 4
+#define GPIO1X_IO 0xf0
+#define GPIO2X_IO 0xf1
+
+static u16 gpio_ba;
+
+static u8 read_reg(u8 addr, u8 port)
+{
+ outb(addr, port);
+ return inb(port + 1);
+}
+
+static void write_reg(u8 data, u8 addr, u8 port)
+{
+ outb(addr, port);
+ outb(data, port + 1);
+}
+
+static void enter_conf_mode(u8 port)
+{
+ outb(0x87, port);
+ outb(0x61, port);
+ outb(0x55, port);
+ outb((port == 0x2e) ? 0x55 : 0xaa, port);
+}
+
+static void exit_conf_mode(u8 port)
+{
+ outb(0x2, port);
+ outb(0x2, port + 1);
+}
+
+static void enter_gpio_mode(u8 port)
+{
+ write_reg(0x2, 0x7, port);
+}
+
+static int it8761e_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+ u16 reg;
+ u8 bit;
+
+ bit = gpio_num % 8;
+ reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba;
+
+ return !!(inb(reg) & (1 << bit));
+}
+
+static int it8761e_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
+{
+ u8 curr_dirs;
+ u8 io_reg, bit;
+
+ bit = gpio_num % 8;
+ io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO;
+
+ spin_lock(&sio_lock);
+
+ enter_conf_mode(port);
+ enter_gpio_mode(port);
+
+ curr_dirs = read_reg(io_reg, port);
+
+ if (curr_dirs & (1 << bit))
+ write_reg(curr_dirs & ~(1 << bit), io_reg, port);
+
+ exit_conf_mode(port);
+
+ spin_unlock(&sio_lock);
+ return 0;
+}
+
+static void it8761e_gpio_set(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_vals, bit;
+ u16 reg;
+
+ bit = gpio_num % 8;
+ reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba;
+
+ spin_lock(&sio_lock);
+
+ curr_vals = inb(reg);
+ if (val)
+ outb(curr_vals | (1 << bit) , reg);
+ else
+ outb(curr_vals & ~(1 << bit), reg);
+
+ spin_unlock(&sio_lock);
+}
+
+static int it8761e_gpio_direction_out(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_dirs, io_reg, bit;
+
+ bit = gpio_num % 8;
+ io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO;
+
+ it8761e_gpio_set(gc, gpio_num, val);
+
+ spin_lock(&sio_lock);
+
+ enter_conf_mode(port);
+ enter_gpio_mode(port);
+
+ curr_dirs = read_reg(io_reg, port);
+
+ if (!(curr_dirs & (1 << bit)))
+ write_reg(curr_dirs | (1 << bit), io_reg, port);
+
+ exit_conf_mode(port);
+
+ spin_unlock(&sio_lock);
+ return 0;
+}
+
+static struct gpio_chip it8761e_gpio_chip = {
+ .label = GPIO_NAME,
+ .owner = THIS_MODULE,
+ .get = it8761e_gpio_get,
+ .direction_input = it8761e_gpio_direction_in,
+ .set = it8761e_gpio_set,
+ .direction_output = it8761e_gpio_direction_out,
+};
+
+static int __init it8761e_gpio_init(void)
+{
+ int i, id, err;
+
+ /* chip and port detection */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ spin_lock(&sio_lock);
+ enter_conf_mode(ports[i]);
+
+ id = (read_reg(CHIP_ID_HIGH_BYTE, ports[i]) << 8) +
+ read_reg(CHIP_ID_LOW_BYTE, ports[i]);
+
+ exit_conf_mode(ports[i]);
+ spin_unlock(&sio_lock);
+
+ if (id == SIO_CHIP_ID) {
+ port = ports[i];
+ break;
+ }
+ }
+
+ if (!port)
+ return -ENODEV;
+
+ /* fetch GPIO base address */
+ enter_conf_mode(port);
+ enter_gpio_mode(port);
+ gpio_ba = (read_reg(GPIO_BA_HIGH_BYTE, port) << 8) +
+ read_reg(GPIO_BA_LOW_BYTE, port);
+ exit_conf_mode(port);
+
+ if (!request_region(gpio_ba, GPIO_IOSIZE, GPIO_NAME))
+ return -EBUSY;
+
+ it8761e_gpio_chip.base = -1;
+ it8761e_gpio_chip.ngpio = 16;
+
+ err = gpiochip_add(&it8761e_gpio_chip);
+ if (err < 0)
+ goto gpiochip_add_err;
+
+ return 0;
+
+gpiochip_add_err:
+ release_region(gpio_ba, GPIO_IOSIZE);
+ gpio_ba = 0;
+ return err;
+}
+
+static void __exit it8761e_gpio_exit(void)
+{
+ if (gpio_ba) {
+ gpiochip_remove(&it8761e_gpio_chip);
+ release_region(gpio_ba, GPIO_IOSIZE);
+ gpio_ba = 0;
+ }
+}
+module_init(it8761e_gpio_init);
+module_exit(it8761e_gpio_exit);
+
+MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
+MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c
new file mode 100644
index 000000000..3a1664335
--- /dev/null
+++ b/drivers/gpio/gpio-janz-ttl.c
@@ -0,0 +1,216 @@
+/*
+ * Janz MODULbus VMOD-TTL GPIO Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/janz.h>
+
+#define DRV_NAME "janz-ttl"
+
+#define PORTA_DIRECTION 0x23
+#define PORTB_DIRECTION 0x2B
+#define PORTC_DIRECTION 0x06
+#define PORTA_IOCTL 0x24
+#define PORTB_IOCTL 0x2C
+#define PORTC_IOCTL 0x07
+
+#define MASTER_INT_CTL 0x00
+#define MASTER_CONF_CTL 0x01
+
+#define CONF_PAE (1 << 2)
+#define CONF_PBE (1 << 7)
+#define CONF_PCE (1 << 4)
+
+struct ttl_control_regs {
+ __be16 portc;
+ __be16 portb;
+ __be16 porta;
+ __be16 control;
+};
+
+struct ttl_module {
+ struct gpio_chip gpio;
+
+ /* base address of registers */
+ struct ttl_control_regs __iomem *regs;
+
+ u8 portc_shadow;
+ u8 portb_shadow;
+ u8 porta_shadow;
+
+ spinlock_t lock;
+};
+
+static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)
+{
+ struct ttl_module *mod = dev_get_drvdata(gpio->dev);
+ u8 *shadow;
+ int ret;
+
+ if (offset < 8) {
+ shadow = &mod->porta_shadow;
+ } else if (offset < 16) {
+ shadow = &mod->portb_shadow;
+ offset -= 8;
+ } else {
+ shadow = &mod->portc_shadow;
+ offset -= 16;
+ }
+
+ spin_lock(&mod->lock);
+ ret = *shadow & (1 << offset);
+ spin_unlock(&mod->lock);
+ return ret;
+}
+
+static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)
+{
+ struct ttl_module *mod = dev_get_drvdata(gpio->dev);
+ void __iomem *port;
+ u8 *shadow;
+
+ if (offset < 8) {
+ port = &mod->regs->porta;
+ shadow = &mod->porta_shadow;
+ } else if (offset < 16) {
+ port = &mod->regs->portb;
+ shadow = &mod->portb_shadow;
+ offset -= 8;
+ } else {
+ port = &mod->regs->portc;
+ shadow = &mod->portc_shadow;
+ offset -= 16;
+ }
+
+ spin_lock(&mod->lock);
+ if (value)
+ *shadow |= (1 << offset);
+ else
+ *shadow &= ~(1 << offset);
+
+ iowrite16be(*shadow, port);
+ spin_unlock(&mod->lock);
+}
+
+static void ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val)
+{
+ iowrite16be(reg, &mod->regs->control);
+ iowrite16be(val, &mod->regs->control);
+}
+
+static void ttl_setup_device(struct ttl_module *mod)
+{
+ /* reset the device to a known state */
+ iowrite16be(0x0000, &mod->regs->control);
+ iowrite16be(0x0001, &mod->regs->control);
+ iowrite16be(0x0000, &mod->regs->control);
+
+ /* put all ports in open-drain mode */
+ ttl_write_reg(mod, PORTA_IOCTL, 0x00ff);
+ ttl_write_reg(mod, PORTB_IOCTL, 0x00ff);
+ ttl_write_reg(mod, PORTC_IOCTL, 0x000f);
+
+ /* set all ports as outputs */
+ ttl_write_reg(mod, PORTA_DIRECTION, 0x0000);
+ ttl_write_reg(mod, PORTB_DIRECTION, 0x0000);
+ ttl_write_reg(mod, PORTC_DIRECTION, 0x0000);
+
+ /* set all ports to drive zeroes */
+ iowrite16be(0x0000, &mod->regs->porta);
+ iowrite16be(0x0000, &mod->regs->portb);
+ iowrite16be(0x0000, &mod->regs->portc);
+
+ /* enable all ports */
+ ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE);
+}
+
+static int ttl_probe(struct platform_device *pdev)
+{
+ struct janz_platform_data *pdata;
+ struct device *dev = &pdev->dev;
+ struct ttl_module *mod;
+ struct gpio_chip *gpio;
+ struct resource *res;
+ int ret;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(dev, "no platform data\n");
+ return -ENXIO;
+ }
+
+ mod = devm_kzalloc(dev, sizeof(*mod), GFP_KERNEL);
+ if (!mod)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mod);
+ spin_lock_init(&mod->lock);
+
+ /* get access to the MODULbus registers for this module */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mod->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mod->regs))
+ return PTR_ERR(mod->regs);
+
+ ttl_setup_device(mod);
+
+ /* Initialize the GPIO data structures */
+ gpio = &mod->gpio;
+ gpio->dev = &pdev->dev;
+ gpio->label = pdev->name;
+ gpio->get = ttl_get_value;
+ gpio->set = ttl_set_value;
+ gpio->owner = THIS_MODULE;
+
+ /* request dynamic allocation */
+ gpio->base = -1;
+ gpio->ngpio = 20;
+
+ ret = gpiochip_add(gpio);
+ if (ret) {
+ dev_err(dev, "unable to add GPIO chip\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ttl_remove(struct platform_device *pdev)
+{
+ struct ttl_module *mod = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&mod->gpio);
+
+ return 0;
+}
+
+static struct platform_driver ttl_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = ttl_probe,
+ .remove = ttl_remove,
+};
+
+module_platform_driver(ttl_driver);
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:janz-ttl");
diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c
new file mode 100644
index 000000000..83f281dda
--- /dev/null
+++ b/drivers/gpio/gpio-kempld.c
@@ -0,0 +1,219 @@
+/*
+ * Kontron PLD GPIO driver
+ *
+ * Copyright (c) 2010-2013 Kontron Europe GmbH
+ * Author: Michael Brunner <michael.brunner@kontron.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/kempld.h>
+
+#define KEMPLD_GPIO_MAX_NUM 16
+#define KEMPLD_GPIO_MASK(x) (BIT((x) % 8))
+#define KEMPLD_GPIO_DIR_NUM(x) (0x40 + (x) / 8)
+#define KEMPLD_GPIO_LVL_NUM(x) (0x42 + (x) / 8)
+#define KEMPLD_GPIO_EVT_LVL_EDGE 0x46
+#define KEMPLD_GPIO_IEN 0x4A
+
+struct kempld_gpio_data {
+ struct gpio_chip chip;
+ struct kempld_device_data *pld;
+};
+
+/*
+ * Set or clear GPIO bit
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+static void kempld_gpio_bitop(struct kempld_device_data *pld,
+ u8 reg, u8 bit, u8 val)
+{
+ u8 status;
+
+ status = kempld_read8(pld, reg);
+ if (val)
+ status |= KEMPLD_GPIO_MASK(bit);
+ else
+ status &= ~KEMPLD_GPIO_MASK(bit);
+ kempld_write8(pld, reg, status);
+}
+
+static int kempld_gpio_get_bit(struct kempld_device_data *pld, u8 reg, u8 bit)
+{
+ u8 status;
+
+ kempld_get_mutex(pld);
+ status = kempld_read8(pld, reg);
+ kempld_release_mutex(pld);
+
+ return !!(status & KEMPLD_GPIO_MASK(bit));
+}
+
+static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ return kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL_NUM(offset), offset);
+}
+
+static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ kempld_get_mutex(pld);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);
+ kempld_release_mutex(pld);
+}
+
+static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ kempld_get_mutex(pld);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 0);
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+static int kempld_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ kempld_get_mutex(pld);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 1);
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ return !kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset);
+}
+
+static int kempld_gpio_pincount(struct kempld_device_data *pld)
+{
+ u16 evt, evt_back;
+
+ kempld_get_mutex(pld);
+
+ /* Backup event register as it might be already initialized */
+ evt_back = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
+ /* Clear event register */
+ kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, 0x0000);
+ /* Read back event register */
+ evt = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
+ /* Restore event register */
+ kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, evt_back);
+
+ kempld_release_mutex(pld);
+
+ return evt ? __ffs(evt) : 16;
+}
+
+static int kempld_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct kempld_device_data *pld = dev_get_drvdata(dev->parent);
+ struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
+ struct kempld_gpio_data *gpio;
+ struct gpio_chip *chip;
+ int ret;
+
+ if (pld->info.spec_major < 2) {
+ dev_err(dev,
+ "Driver only supports GPIO devices compatible to PLD spec. rev. 2.0 or higher\n");
+ return -ENODEV;
+ }
+
+ gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->pld = pld;
+
+ platform_set_drvdata(pdev, gpio);
+
+ chip = &gpio->chip;
+ chip->label = "gpio-kempld";
+ chip->owner = THIS_MODULE;
+ chip->dev = dev;
+ chip->can_sleep = true;
+ if (pdata && pdata->gpio_base)
+ chip->base = pdata->gpio_base;
+ else
+ chip->base = -1;
+ chip->direction_input = kempld_gpio_direction_input;
+ chip->direction_output = kempld_gpio_direction_output;
+ chip->get_direction = kempld_gpio_get_direction;
+ chip->get = kempld_gpio_get;
+ chip->set = kempld_gpio_set;
+ chip->ngpio = kempld_gpio_pincount(pld);
+ if (chip->ngpio == 0) {
+ dev_err(dev, "No GPIO pins detected\n");
+ return -ENODEV;
+ }
+
+ ret = gpiochip_add(chip);
+ if (ret) {
+ dev_err(dev, "Could not register GPIO chip\n");
+ return ret;
+ }
+
+ dev_info(dev, "GPIO functionality initialized with %d pins\n",
+ chip->ngpio);
+
+ return 0;
+}
+
+static int kempld_gpio_remove(struct platform_device *pdev)
+{
+ struct kempld_gpio_data *gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gpio->chip);
+ return 0;
+}
+
+static struct platform_driver kempld_gpio_driver = {
+ .driver = {
+ .name = "kempld-gpio",
+ },
+ .probe = kempld_gpio_probe,
+ .remove = kempld_gpio_remove,
+};
+
+module_platform_driver(kempld_gpio_driver);
+
+MODULE_DESCRIPTION("KEM PLD GPIO Driver");
+MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:kempld-gpio");
diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c
new file mode 100644
index 000000000..cc09b237e
--- /dev/null
+++ b/drivers/gpio/gpio-ks8695.c
@@ -0,0 +1,317 @@
+/*
+ * arch/arm/mach-ks8695/gpio.c
+ *
+ * Copyright (C) 2006 Andrew Victor
+ * Updated to GPIOLIB, Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/gpio.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];
+}
+
+/*
+ * Map IRQ number to GPIO line.
+ */
+int irq_to_gpio(unsigned int irq)
+{
+ if ((irq < KS8695_IRQ_EXTERN0) || (irq > KS8695_IRQ_EXTERN3))
+ return -EINVAL;
+
+ return (irq - KS8695_IRQ_EXTERN0);
+}
+EXPORT_SYMBOL(irq_to_gpio);
+
+/* 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(&ks8695_gpio_chip))
+ 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;
+}
+
+static int ks8695_gpio_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ks8695_gpio_show, NULL);
+}
+
+static const struct file_operations ks8695_gpio_operations = {
+ .open = ks8695_gpio_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init ks8695_gpio_debugfs_init(void)
+{
+ /* /sys/kernel/debug/ks8695_gpio */
+ (void) debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL, &ks8695_gpio_operations);
+ return 0;
+}
+postcore_initcall(ks8695_gpio_debugfs_init);
+
+#endif
diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c
new file mode 100644
index 000000000..ccc65a1ae
--- /dev/null
+++ b/drivers/gpio/gpio-loongson.c
@@ -0,0 +1,115 @@
+/*
+ * Loongson-2F/3A/3B GPIO Support
+ *
+ * Copyright (c) 2008 Richard Liu, STMicroelectronics <richard.liu@st.com>
+ * Copyright (c) 2008-2010 Arnaud Patard <apatard@mandriva.com>
+ * Copyright (c) 2013 Hongbing Hu <huhb@lemote.com>
+ * Copyright (c) 2014 Huacai Chen <chenhc@lemote.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <asm/types.h>
+#include <loongson.h>
+#include <linux/gpio.h>
+
+#define STLS2F_N_GPIO 4
+#define STLS3A_N_GPIO 16
+
+#ifdef CONFIG_CPU_LOONGSON3
+#define LOONGSON_N_GPIO STLS3A_N_GPIO
+#else
+#define LOONGSON_N_GPIO STLS2F_N_GPIO
+#endif
+
+#define LOONGSON_GPIO_IN_OFFSET 16
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ u32 temp;
+ u32 mask;
+
+ spin_lock(&gpio_lock);
+ mask = 1 << gpio;
+ temp = LOONGSON_GPIOIE;
+ temp |= mask;
+ LOONGSON_GPIOIE = temp;
+ spin_unlock(&gpio_lock);
+
+ return 0;
+}
+
+static int loongson_gpio_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int level)
+{
+ u32 temp;
+ u32 mask;
+
+ gpio_set_value(gpio, level);
+ spin_lock(&gpio_lock);
+ mask = 1 << gpio;
+ temp = LOONGSON_GPIOIE;
+ temp &= (~mask);
+ LOONGSON_GPIOIE = temp;
+ spin_unlock(&gpio_lock);
+
+ return 0;
+}
+
+static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ u32 val;
+ u32 mask;
+
+ mask = 1 << (gpio + LOONGSON_GPIO_IN_OFFSET);
+ spin_lock(&gpio_lock);
+ val = LOONGSON_GPIODATA;
+ spin_unlock(&gpio_lock);
+
+ return (val & mask) != 0;
+}
+
+static void loongson_gpio_set_value(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ u32 val;
+ u32 mask;
+
+ mask = 1 << gpio;
+
+ spin_lock(&gpio_lock);
+ val = LOONGSON_GPIODATA;
+ if (value)
+ val |= mask;
+ else
+ val &= (~mask);
+ LOONGSON_GPIODATA = val;
+ spin_unlock(&gpio_lock);
+}
+
+static struct gpio_chip loongson_chip = {
+ .label = "Loongson-gpio-chip",
+ .direction_input = loongson_gpio_direction_input,
+ .get = loongson_gpio_get_value,
+ .direction_output = loongson_gpio_direction_output,
+ .set = loongson_gpio_set_value,
+ .base = 0,
+ .ngpio = LOONGSON_N_GPIO,
+ .can_sleep = false,
+};
+
+static int __init loongson_gpio_setup(void)
+{
+ return gpiochip_add(&loongson_chip);
+}
+postcore_initcall(loongson_gpio_setup);
diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c
new file mode 100644
index 000000000..cfc5b12b4
--- /dev/null
+++ b/drivers/gpio/gpio-lp3943.c
@@ -0,0 +1,242 @@
+/*
+ * TI/National Semiconductor LP3943 GPIO driver
+ *
+ * Copyright 2013 Texas Instruments
+ *
+ * Author: Milo Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/lp3943.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+enum lp3943_gpios {
+ LP3943_GPIO1,
+ LP3943_GPIO2,
+ LP3943_GPIO3,
+ LP3943_GPIO4,
+ LP3943_GPIO5,
+ LP3943_GPIO6,
+ LP3943_GPIO7,
+ LP3943_GPIO8,
+ LP3943_GPIO9,
+ LP3943_GPIO10,
+ LP3943_GPIO11,
+ LP3943_GPIO12,
+ LP3943_GPIO13,
+ LP3943_GPIO14,
+ LP3943_GPIO15,
+ LP3943_GPIO16,
+ LP3943_MAX_GPIO,
+};
+
+struct lp3943_gpio {
+ struct gpio_chip chip;
+ struct lp3943 *lp3943;
+ u16 input_mask; /* 1 = GPIO is input direction, 0 = output */
+};
+
+static inline struct lp3943_gpio *to_lp3943_gpio(struct gpio_chip *_chip)
+{
+ return container_of(_chip, struct lp3943_gpio, chip);
+}
+
+static int lp3943_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct lp3943_gpio *lp3943_gpio = to_lp3943_gpio(chip);
+ struct lp3943 *lp3943 = lp3943_gpio->lp3943;
+
+ /* Return an error if the pin is already assigned */
+ if (test_and_set_bit(offset, &lp3943->pin_used))
+ return -EBUSY;
+
+ return 0;
+}
+
+static void lp3943_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct lp3943_gpio *lp3943_gpio = to_lp3943_gpio(chip);
+ struct lp3943 *lp3943 = lp3943_gpio->lp3943;
+
+ clear_bit(offset, &lp3943->pin_used);
+}
+
+static int lp3943_gpio_set_mode(struct lp3943_gpio *lp3943_gpio, u8 offset,
+ u8 val)
+{
+ struct lp3943 *lp3943 = lp3943_gpio->lp3943;
+ const struct lp3943_reg_cfg *mux = lp3943->mux_cfg;
+
+ return lp3943_update_bits(lp3943, mux[offset].reg, mux[offset].mask,
+ val << mux[offset].shift);
+}
+
+static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct lp3943_gpio *lp3943_gpio = to_lp3943_gpio(chip);
+
+ lp3943_gpio->input_mask |= BIT(offset);
+
+ return lp3943_gpio_set_mode(lp3943_gpio, offset, LP3943_GPIO_IN);
+}
+
+static int lp3943_get_gpio_in_status(struct lp3943_gpio *lp3943_gpio,
+ struct gpio_chip *chip, unsigned offset)
+{
+ u8 addr, read;
+ int err;
+
+ switch (offset) {
+ case LP3943_GPIO1 ... LP3943_GPIO8:
+ addr = LP3943_REG_GPIO_A;
+ break;
+ case LP3943_GPIO9 ... LP3943_GPIO16:
+ addr = LP3943_REG_GPIO_B;
+ offset = offset - 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = lp3943_read_byte(lp3943_gpio->lp3943, addr, &read);
+ if (err)
+ return err;
+
+ return !!(read & BIT(offset));
+}
+
+static int lp3943_get_gpio_out_status(struct lp3943_gpio *lp3943_gpio,
+ struct gpio_chip *chip, unsigned offset)
+{
+ struct lp3943 *lp3943 = lp3943_gpio->lp3943;
+ const struct lp3943_reg_cfg *mux = lp3943->mux_cfg;
+ u8 read;
+ int err;
+
+ err = lp3943_read_byte(lp3943, mux[offset].reg, &read);
+ if (err)
+ return err;
+
+ read = (read & mux[offset].mask) >> mux[offset].shift;
+
+ if (read == LP3943_GPIO_OUT_HIGH)
+ return 1;
+ else if (read == LP3943_GPIO_OUT_LOW)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int lp3943_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct lp3943_gpio *lp3943_gpio = to_lp3943_gpio(chip);
+
+ /*
+ * Limitation:
+ * LP3943 doesn't have the GPIO direction register. It provides
+ * only input and output status registers.
+ * So, direction info is required to handle the 'get' operation.
+ * This variable is updated whenever the direction is changed and
+ * it is used here.
+ */
+
+ if (lp3943_gpio->input_mask & BIT(offset))
+ return lp3943_get_gpio_in_status(lp3943_gpio, chip, offset);
+ else
+ return lp3943_get_gpio_out_status(lp3943_gpio, chip, offset);
+}
+
+static void lp3943_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct lp3943_gpio *lp3943_gpio = to_lp3943_gpio(chip);
+ u8 data;
+
+ if (value)
+ data = LP3943_GPIO_OUT_HIGH;
+ else
+ data = LP3943_GPIO_OUT_LOW;
+
+ lp3943_gpio_set_mode(lp3943_gpio, offset, data);
+}
+
+static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct lp3943_gpio *lp3943_gpio = to_lp3943_gpio(chip);
+
+ lp3943_gpio_set(chip, offset, value);
+ lp3943_gpio->input_mask &= ~BIT(offset);
+
+ return 0;
+}
+
+static const struct gpio_chip lp3943_gpio_chip = {
+ .label = "lp3943",
+ .owner = THIS_MODULE,
+ .request = lp3943_gpio_request,
+ .free = lp3943_gpio_free,
+ .direction_input = lp3943_gpio_direction_input,
+ .get = lp3943_gpio_get,
+ .direction_output = lp3943_gpio_direction_output,
+ .set = lp3943_gpio_set,
+ .base = -1,
+ .ngpio = LP3943_MAX_GPIO,
+ .can_sleep = 1,
+};
+
+static int lp3943_gpio_probe(struct platform_device *pdev)
+{
+ struct lp3943 *lp3943 = dev_get_drvdata(pdev->dev.parent);
+ struct lp3943_gpio *lp3943_gpio;
+
+ lp3943_gpio = devm_kzalloc(&pdev->dev, sizeof(*lp3943_gpio),
+ GFP_KERNEL);
+ if (!lp3943_gpio)
+ return -ENOMEM;
+
+ lp3943_gpio->lp3943 = lp3943;
+ lp3943_gpio->chip = lp3943_gpio_chip;
+ lp3943_gpio->chip.dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, lp3943_gpio);
+
+ return gpiochip_add(&lp3943_gpio->chip);
+}
+
+static int lp3943_gpio_remove(struct platform_device *pdev)
+{
+ struct lp3943_gpio *lp3943_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&lp3943_gpio->chip);
+ return 0;
+}
+
+static const struct of_device_id lp3943_gpio_of_match[] = {
+ { .compatible = "ti,lp3943-gpio", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lp3943_gpio_of_match);
+
+static struct platform_driver lp3943_gpio_driver = {
+ .probe = lp3943_gpio_probe,
+ .remove = lp3943_gpio_remove,
+ .driver = {
+ .name = "lp3943-gpio",
+ .of_match_table = lp3943_gpio_of_match,
+ },
+};
+module_platform_driver(lp3943_gpio_driver);
+
+MODULE_DESCRIPTION("LP3943 GPIO driver");
+MODULE_ALIAS("platform:lp3943-gpio");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
new file mode 100644
index 000000000..47e2dde63
--- /dev/null
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -0,0 +1,577 @@
+/*
+ * GPIO driver for LPC32xx SoC
+ *
+ * Author: Kevin Wells <kevin.wells@nxp.com>
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/platform_data/gpio-lpc32xx.h>
+
+#include <mach/hardware.h>
+#include <mach/platform.h>
+#include <mach/irqs.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 GPIO012_PIN_TO_BIT(x) (1 << (x))
+#define GPIO3_PIN_TO_BIT(x) (1 << ((x) + 25))
+#define GPO3_PIN_TO_BIT(x) (1 << (x))
+#define GPIO012_PIN_IN_SEL(x, y) (((x) >> (y)) & 1)
+#define GPIO3_PIN_IN_SHIFT(x) ((x) == 5 ? 24 : 10 + (x))
+#define GPIO3_PIN_IN_SEL(x, y) (((x) >> GPIO3_PIN_IN_SHIFT(y)) & 1)
+#define GPIO3_PIN5_IN_SEL(x) (((x) >> 24) & 1)
+#define GPI3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1)
+#define GPO3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1)
+
+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;
+};
+
+/*
+ * GPIO names
+ */
+static const char *gpio_p0_names[LPC32XX_GPIO_P0_MAX] = {
+ "p0.0", "p0.1", "p0.2", "p0.3",
+ "p0.4", "p0.5", "p0.6", "p0.7"
+};
+
+static const char *gpio_p1_names[LPC32XX_GPIO_P1_MAX] = {
+ "p1.0", "p1.1", "p1.2", "p1.3",
+ "p1.4", "p1.5", "p1.6", "p1.7",
+ "p1.8", "p1.9", "p1.10", "p1.11",
+ "p1.12", "p1.13", "p1.14", "p1.15",
+ "p1.16", "p1.17", "p1.18", "p1.19",
+ "p1.20", "p1.21", "p1.22", "p1.23",
+};
+
+static const char *gpio_p2_names[LPC32XX_GPIO_P2_MAX] = {
+ "p2.0", "p2.1", "p2.2", "p2.3",
+ "p2.4", "p2.5", "p2.6", "p2.7",
+ "p2.8", "p2.9", "p2.10", "p2.11",
+ "p2.12"
+};
+
+static const char *gpio_p3_names[LPC32XX_GPIO_P3_MAX] = {
+ "gpio00", "gpio01", "gpio02", "gpio03",
+ "gpio04", "gpio05"
+};
+
+static const char *gpi_p3_names[LPC32XX_GPI_P3_MAX] = {
+ "gpi00", "gpi01", "gpi02", "gpi03",
+ "gpi04", "gpi05", "gpi06", "gpi07",
+ "gpi08", "gpi09", NULL, NULL,
+ NULL, NULL, NULL, "gpi15",
+ "gpi16", "gpi17", "gpi18", "gpi19",
+ "gpi20", "gpi21", "gpi22", "gpi23",
+ "gpi24", "gpi25", "gpi26", "gpi27",
+ "gpi28"
+};
+
+static const char *gpo_p3_names[LPC32XX_GPO_P3_MAX] = {
+ "gpo00", "gpo01", "gpo02", "gpo03",
+ "gpo04", "gpo05", "gpo06", "gpo07",
+ "gpo08", "gpo09", "gpo10", "gpo11",
+ "gpo12", "gpo13", "gpo14", "gpo15",
+ "gpo16", "gpo17", "gpo18", "gpo19",
+ "gpo20", "gpo21", "gpo22", "gpo23"
+};
+
+static struct gpio_regs gpio_grp_regs_p0 = {
+ .inp_state = LPC32XX_GPIO_P0_INP_STATE,
+ .outp_set = LPC32XX_GPIO_P0_OUTP_SET,
+ .outp_clr = LPC32XX_GPIO_P0_OUTP_CLR,
+ .dir_set = LPC32XX_GPIO_P0_DIR_SET,
+ .dir_clr = LPC32XX_GPIO_P0_DIR_CLR,
+};
+
+static struct gpio_regs gpio_grp_regs_p1 = {
+ .inp_state = LPC32XX_GPIO_P1_INP_STATE,
+ .outp_set = LPC32XX_GPIO_P1_OUTP_SET,
+ .outp_clr = LPC32XX_GPIO_P1_OUTP_CLR,
+ .dir_set = LPC32XX_GPIO_P1_DIR_SET,
+ .dir_clr = LPC32XX_GPIO_P1_DIR_CLR,
+};
+
+static struct gpio_regs gpio_grp_regs_p2 = {
+ .inp_state = LPC32XX_GPIO_P2_INP_STATE,
+ .outp_set = LPC32XX_GPIO_P2_OUTP_SET,
+ .outp_clr = LPC32XX_GPIO_P2_OUTP_CLR,
+ .dir_set = LPC32XX_GPIO_P2_DIR_SET,
+ .dir_clr = LPC32XX_GPIO_P2_DIR_CLR,
+};
+
+static struct gpio_regs gpio_grp_regs_p3 = {
+ .inp_state = LPC32XX_GPIO_P3_INP_STATE,
+ .outp_state = LPC32XX_GPIO_P3_OUTP_STATE,
+ .outp_set = LPC32XX_GPIO_P3_OUTP_SET,
+ .outp_clr = LPC32XX_GPIO_P3_OUTP_CLR,
+ .dir_set = LPC32XX_GPIO_P2_DIR_SET,
+ .dir_clr = LPC32XX_GPIO_P2_DIR_CLR,
+};
+
+struct lpc32xx_gpio_chip {
+ struct gpio_chip chip;
+ struct gpio_regs *gpio_grp;
+};
+
+static inline struct lpc32xx_gpio_chip *to_lpc32xx_gpio(
+ struct gpio_chip *gpc)
+{
+ return container_of(gpc, struct lpc32xx_gpio_chip, chip);
+}
+
+static void __set_gpio_dir_p012(struct lpc32xx_gpio_chip *group,
+ unsigned pin, int input)
+{
+ if (input)
+ __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ group->gpio_grp->dir_clr);
+ else
+ __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ group->gpio_grp->dir_set);
+}
+
+static void __set_gpio_dir_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin, int input)
+{
+ u32 u = GPIO3_PIN_TO_BIT(pin);
+
+ if (input)
+ __raw_writel(u, group->gpio_grp->dir_clr);
+ else
+ __raw_writel(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),
+ group->gpio_grp->outp_set);
+ else
+ __raw_writel(GPIO012_PIN_TO_BIT(pin),
+ group->gpio_grp->outp_clr);
+}
+
+static void __set_gpio_level_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin, int high)
+{
+ u32 u = GPIO3_PIN_TO_BIT(pin);
+
+ if (high)
+ __raw_writel(u, group->gpio_grp->outp_set);
+ else
+ __raw_writel(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);
+ else
+ __raw_writel(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),
+ pin);
+}
+
+static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin)
+{
+ int state = __raw_readl(group->gpio_grp->inp_state);
+
+ /*
+ * P3 GPIO pin input mapping is not contiguous, GPIOP3-0..4 is mapped
+ * to bits 10..14, while GPIOP3-5 is mapped to bit 24.
+ */
+ return GPIO3_PIN_IN_SEL(state, pin);
+}
+
+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);
+}
+
+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);
+}
+
+/*
+ * GPIO primitives.
+ */
+static int lpc32xx_gpio_dir_input_p012(struct gpio_chip *chip,
+ unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_dir_p012(group, pin, 1);
+
+ return 0;
+}
+
+static int lpc32xx_gpio_dir_input_p3(struct gpio_chip *chip,
+ unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_dir_p3(group, pin, 1);
+
+ return 0;
+}
+
+static int lpc32xx_gpio_dir_in_always(struct gpio_chip *chip,
+ unsigned pin)
+{
+ return 0;
+}
+
+static int lpc32xx_gpio_get_value_p012(struct gpio_chip *chip, unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ return __get_gpio_state_p012(group, pin);
+}
+
+static int lpc32xx_gpio_get_value_p3(struct gpio_chip *chip, unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ return __get_gpio_state_p3(group, pin);
+}
+
+static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ return __get_gpi_state_p3(group, pin);
+}
+
+static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_level_p012(group, pin, value);
+ __set_gpio_dir_p012(group, pin, 0);
+
+ return 0;
+}
+
+static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_level_p3(group, pin, value);
+ __set_gpio_dir_p3(group, pin, 0);
+
+ return 0;
+}
+
+static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpo_level_p3(group, pin, value);
+ return 0;
+}
+
+static void lpc32xx_gpio_set_value_p012(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_level_p012(group, pin, value);
+}
+
+static void lpc32xx_gpio_set_value_p3(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpio_level_p3(group, pin, value);
+}
+
+static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpo_level_p3(group, pin, value);
+}
+
+static int lpc32xx_gpo_get_value(struct gpio_chip *chip, unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ return __get_gpo_state_p3(group, pin);
+}
+
+static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
+{
+ if (pin < chip->ngpio)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int lpc32xx_gpio_to_irq_p01(struct gpio_chip *chip, unsigned offset)
+{
+ return IRQ_LPC32XX_P0_P1_IRQ;
+}
+
+static const char lpc32xx_gpio_to_irq_gpio_p3_table[] = {
+ IRQ_LPC32XX_GPIO_00,
+ IRQ_LPC32XX_GPIO_01,
+ IRQ_LPC32XX_GPIO_02,
+ IRQ_LPC32XX_GPIO_03,
+ IRQ_LPC32XX_GPIO_04,
+ IRQ_LPC32XX_GPIO_05,
+};
+
+static int lpc32xx_gpio_to_irq_gpio_p3(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset < ARRAY_SIZE(lpc32xx_gpio_to_irq_gpio_p3_table))
+ return lpc32xx_gpio_to_irq_gpio_p3_table[offset];
+ return -ENXIO;
+}
+
+static const char lpc32xx_gpio_to_irq_gpi_p3_table[] = {
+ IRQ_LPC32XX_GPI_00,
+ IRQ_LPC32XX_GPI_01,
+ IRQ_LPC32XX_GPI_02,
+ IRQ_LPC32XX_GPI_03,
+ IRQ_LPC32XX_GPI_04,
+ IRQ_LPC32XX_GPI_05,
+ IRQ_LPC32XX_GPI_06,
+ IRQ_LPC32XX_GPI_07,
+ IRQ_LPC32XX_GPI_08,
+ IRQ_LPC32XX_GPI_09,
+ -ENXIO, /* 10 */
+ -ENXIO, /* 11 */
+ -ENXIO, /* 12 */
+ -ENXIO, /* 13 */
+ -ENXIO, /* 14 */
+ -ENXIO, /* 15 */
+ -ENXIO, /* 16 */
+ -ENXIO, /* 17 */
+ -ENXIO, /* 18 */
+ IRQ_LPC32XX_GPI_19,
+ -ENXIO, /* 20 */
+ -ENXIO, /* 21 */
+ -ENXIO, /* 22 */
+ -ENXIO, /* 23 */
+ -ENXIO, /* 24 */
+ -ENXIO, /* 25 */
+ -ENXIO, /* 26 */
+ -ENXIO, /* 27 */
+ IRQ_LPC32XX_GPI_28,
+};
+
+static int lpc32xx_gpio_to_irq_gpi_p3(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset < ARRAY_SIZE(lpc32xx_gpio_to_irq_gpi_p3_table))
+ return lpc32xx_gpio_to_irq_gpi_p3_table[offset];
+ return -ENXIO;
+}
+
+static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = {
+ {
+ .chip = {
+ .label = "gpio_p0",
+ .direction_input = lpc32xx_gpio_dir_input_p012,
+ .get = lpc32xx_gpio_get_value_p012,
+ .direction_output = lpc32xx_gpio_dir_output_p012,
+ .set = lpc32xx_gpio_set_value_p012,
+ .request = lpc32xx_gpio_request,
+ .to_irq = lpc32xx_gpio_to_irq_p01,
+ .base = LPC32XX_GPIO_P0_GRP,
+ .ngpio = LPC32XX_GPIO_P0_MAX,
+ .names = gpio_p0_names,
+ .can_sleep = false,
+ },
+ .gpio_grp = &gpio_grp_regs_p0,
+ },
+ {
+ .chip = {
+ .label = "gpio_p1",
+ .direction_input = lpc32xx_gpio_dir_input_p012,
+ .get = lpc32xx_gpio_get_value_p012,
+ .direction_output = lpc32xx_gpio_dir_output_p012,
+ .set = lpc32xx_gpio_set_value_p012,
+ .request = lpc32xx_gpio_request,
+ .to_irq = lpc32xx_gpio_to_irq_p01,
+ .base = LPC32XX_GPIO_P1_GRP,
+ .ngpio = LPC32XX_GPIO_P1_MAX,
+ .names = gpio_p1_names,
+ .can_sleep = false,
+ },
+ .gpio_grp = &gpio_grp_regs_p1,
+ },
+ {
+ .chip = {
+ .label = "gpio_p2",
+ .direction_input = lpc32xx_gpio_dir_input_p012,
+ .get = lpc32xx_gpio_get_value_p012,
+ .direction_output = lpc32xx_gpio_dir_output_p012,
+ .set = lpc32xx_gpio_set_value_p012,
+ .request = lpc32xx_gpio_request,
+ .base = LPC32XX_GPIO_P2_GRP,
+ .ngpio = LPC32XX_GPIO_P2_MAX,
+ .names = gpio_p2_names,
+ .can_sleep = false,
+ },
+ .gpio_grp = &gpio_grp_regs_p2,
+ },
+ {
+ .chip = {
+ .label = "gpio_p3",
+ .direction_input = lpc32xx_gpio_dir_input_p3,
+ .get = lpc32xx_gpio_get_value_p3,
+ .direction_output = lpc32xx_gpio_dir_output_p3,
+ .set = lpc32xx_gpio_set_value_p3,
+ .request = lpc32xx_gpio_request,
+ .to_irq = lpc32xx_gpio_to_irq_gpio_p3,
+ .base = LPC32XX_GPIO_P3_GRP,
+ .ngpio = LPC32XX_GPIO_P3_MAX,
+ .names = gpio_p3_names,
+ .can_sleep = false,
+ },
+ .gpio_grp = &gpio_grp_regs_p3,
+ },
+ {
+ .chip = {
+ .label = "gpi_p3",
+ .direction_input = lpc32xx_gpio_dir_in_always,
+ .get = lpc32xx_gpi_get_value,
+ .request = lpc32xx_gpio_request,
+ .to_irq = lpc32xx_gpio_to_irq_gpi_p3,
+ .base = LPC32XX_GPI_P3_GRP,
+ .ngpio = LPC32XX_GPI_P3_MAX,
+ .names = gpi_p3_names,
+ .can_sleep = false,
+ },
+ .gpio_grp = &gpio_grp_regs_p3,
+ },
+ {
+ .chip = {
+ .label = "gpo_p3",
+ .direction_output = lpc32xx_gpio_dir_out_always,
+ .set = lpc32xx_gpo_set_value,
+ .get = lpc32xx_gpo_get_value,
+ .request = lpc32xx_gpio_request,
+ .base = LPC32XX_GPO_P3_GRP,
+ .ngpio = LPC32XX_GPO_P3_MAX,
+ .names = gpo_p3_names,
+ .can_sleep = false,
+ },
+ .gpio_grp = &gpio_grp_regs_p3,
+ },
+};
+
+static int lpc32xx_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ /* Is this the correct bank? */
+ u32 bank = gpiospec->args[0];
+ if ((bank >= ARRAY_SIZE(lpc32xx_gpiochip) ||
+ (gc != &lpc32xx_gpiochip[bank].chip)))
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[2];
+ return gpiospec->args[1];
+}
+
+static int lpc32xx_gpio_probe(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++) {
+ 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;
+ }
+ gpiochip_add(&lpc32xx_gpiochip[i].chip);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpc32xx_gpio_of_match[] = {
+ { .compatible = "nxp,lpc3220-gpio", },
+ { },
+};
+#endif
+
+static struct platform_driver lpc32xx_gpio_driver = {
+ .driver = {
+ .name = "lpc32xx-gpio",
+ .of_match_table = of_match_ptr(lpc32xx_gpio_of_match),
+ },
+ .probe = lpc32xx_gpio_probe,
+};
+
+module_platform_driver(lpc32xx_gpio_driver);
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
new file mode 100644
index 000000000..127c755b3
--- /dev/null
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -0,0 +1,474 @@
+/*
+ * GPIO controller driver for Intel Lynxpoint PCH chipset>
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * Author: Mathias Nyman <mathias.nyman@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.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 registes.
+ *
+ * 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 = container_of(chip, struct lp_gpio, 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 = container_of(chip, struct lp_gpio, 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 = container_of(chip, struct lp_gpio, 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 = container_of(gc, struct lp_gpio, chip);
+ 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 = container_of(chip, struct lp_gpio, 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 = container_of(chip, struct lp_gpio, 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 = container_of(chip, struct lp_gpio, 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(unsigned hwirq, 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 = container_of(gc, struct lp_gpio, chip);
+ struct irq_chip *chip = irq_data_get_irq_chip(data);
+ u32 base, pin, mask;
+ unsigned long reg, ena, pending;
+
+ /* 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);
+
+ while ((pending = (inl(reg) & inl(ena)))) {
+ unsigned irq;
+
+ pin = __ffs(pending);
+ mask = BIT(pin);
+ /* Clear before handling so we don't lose an edge */
+ outl(mask, reg);
+ irq = irq_find_mapping(lg->chip.irqdomain, 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 = container_of(gc, struct lp_gpio, chip);
+ 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 = container_of(gc, struct lp_gpio, chip);
+ 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->dev = dev;
+
+ ret = gpiochip_add(gc);
+ 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");
+ gpiochip_remove(gc);
+ 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 platform_device *pdev = to_platform_device(dev);
+ struct lp_gpio *lg = platform_get_drvdata(pdev);
+ 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)
+{
+ struct lp_gpio *lg = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
+ gpiochip_remove(&lg->chip);
+ 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");
+MODULE_ALIAS("platform:lp_gpio");
diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c
new file mode 100644
index 000000000..0cc2c279a
--- /dev/null
+++ b/drivers/gpio/gpio-max7300.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix
+ *
+ * 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.
+ *
+ * Check max730x.c for further details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/spi/max7301.h>
+#include <linux/slab.h>
+
+static int max7300_i2c_write(struct device *dev, unsigned int reg,
+ unsigned int val)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int max7300_i2c_read(struct device *dev, unsigned int reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int max7300_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max7301 *ts;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ ts = devm_kzalloc(&client->dev, sizeof(struct max7301), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ts->read = max7300_i2c_read;
+ ts->write = max7300_i2c_write;
+ ts->dev = &client->dev;
+
+ return __max730x_probe(ts);
+}
+
+static int max7300_remove(struct i2c_client *client)
+{
+ return __max730x_remove(&client->dev);
+}
+
+static const struct i2c_device_id max7300_id[] = {
+ { "max7300", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max7300_id);
+
+static struct i2c_driver max7300_driver = {
+ .driver = {
+ .name = "max7300",
+ .owner = THIS_MODULE,
+ },
+ .probe = max7300_probe,
+ .remove = max7300_remove,
+ .id_table = max7300_id,
+};
+
+static int __init max7300_init(void)
+{
+ return i2c_add_driver(&max7300_driver);
+}
+subsys_initcall(max7300_init);
+
+static void __exit max7300_exit(void)
+{
+ i2c_del_driver(&max7300_driver);
+}
+module_exit(max7300_exit);
+
+MODULE_AUTHOR("Wolfram Sang");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MAX7300 GPIO-Expander");
diff --git a/drivers/gpio/gpio-max7301.c b/drivers/gpio/gpio-max7301.c
new file mode 100644
index 000000000..6e1c984a7
--- /dev/null
+++ b/drivers/gpio/gpio-max7301.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2006 Juergen Beisert, Pengutronix
+ * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix
+ *
+ * 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.
+ *
+ * Check max730x.c for further details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/max7301.h>
+
+/* A write to the MAX7301 means one message with one transfer */
+static int max7301_spi_write(struct device *dev, unsigned int reg,
+ unsigned int val)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u16 word = ((reg & 0x7F) << 8) | (val & 0xFF);
+
+ return spi_write(spi, (const u8 *)&word, sizeof(word));
+}
+
+/* A read from the MAX7301 means two transfers; here, one message each */
+
+static int max7301_spi_read(struct device *dev, unsigned int reg)
+{
+ int ret;
+ u16 word;
+ struct spi_device *spi = to_spi_device(dev);
+
+ word = 0x8000 | (reg << 8);
+ ret = spi_write(spi, (const u8 *)&word, sizeof(word));
+ if (ret)
+ return ret;
+ /*
+ * This relies on the fact, that a transfer with NULL tx_buf shifts out
+ * zero bytes (=NOOP for MAX7301)
+ */
+ ret = spi_read(spi, (u8 *)&word, sizeof(word));
+ if (ret)
+ return ret;
+ return word & 0xff;
+}
+
+static int max7301_probe(struct spi_device *spi)
+{
+ struct max7301 *ts;
+ int ret;
+
+ /* bits_per_word cannot be configured in platform data */
+ spi->bits_per_word = 16;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ ts = devm_kzalloc(&spi->dev, sizeof(struct max7301), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ts->read = max7301_spi_read;
+ ts->write = max7301_spi_write;
+ ts->dev = &spi->dev;
+
+ ret = __max730x_probe(ts);
+ return ret;
+}
+
+static int max7301_remove(struct spi_device *spi)
+{
+ return __max730x_remove(&spi->dev);
+}
+
+static const struct spi_device_id max7301_id[] = {
+ { "max7301", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, max7301_id);
+
+static struct spi_driver max7301_driver = {
+ .driver = {
+ .name = "max7301",
+ .owner = THIS_MODULE,
+ },
+ .probe = max7301_probe,
+ .remove = max7301_remove,
+ .id_table = max7301_id,
+};
+
+static int __init max7301_init(void)
+{
+ return spi_register_driver(&max7301_driver);
+}
+/* register after spi postcore initcall and before
+ * subsys initcalls that may rely on these GPIOs
+ */
+subsys_initcall(max7301_init);
+
+static void __exit max7301_exit(void)
+{
+ spi_unregister_driver(&max7301_driver);
+}
+module_exit(max7301_exit);
+
+MODULE_AUTHOR("Juergen Beisert, Wolfram Sang");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MAX7301 GPIO-Expander");
diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c
new file mode 100644
index 000000000..18ab89e20
--- /dev/null
+++ b/drivers/gpio/gpio-max730x.c
@@ -0,0 +1,246 @@
+/**
+ * Copyright (C) 2006 Juergen Beisert, Pengutronix
+ * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix
+ *
+ * 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.
+ *
+ * The Maxim MAX7300/1 device is an I2C/SPI driven GPIO expander. There are
+ * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more
+ * details
+ * Note:
+ * - DIN must be stable at the rising edge of clock.
+ * - when writing:
+ * - always clock in 16 clocks at once
+ * - at DIN: D15 first, D0 last
+ * - D0..D7 = databyte, D8..D14 = commandbyte
+ * - D15 = low -> write command
+ * - when reading
+ * - always clock in 16 clocks at once
+ * - at DIN: D15 first, D0 last
+ * - D0..D7 = dummy, D8..D14 = register address
+ * - D15 = high -> read command
+ * - raise CS and assert it again
+ * - always clock in 16 clocks at once
+ * - at DOUT: D15 first, D0 last
+ * - D0..D7 contains the data from the first cycle
+ *
+ * The driver exports a standard gpiochip interface
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/spi/max7301.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+/*
+ * Pin configurations, see MAX7301 datasheet page 6
+ */
+#define PIN_CONFIG_MASK 0x03
+#define PIN_CONFIG_IN_PULLUP 0x03
+#define PIN_CONFIG_IN_WO_PULLUP 0x02
+#define PIN_CONFIG_OUT 0x01
+
+#define PIN_NUMBER 28
+
+static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct max7301 *ts = container_of(chip, struct max7301, chip);
+ u8 *config;
+ u8 offset_bits, pin_config;
+ int ret;
+
+ /* First 4 pins are unused in the controller */
+ offset += 4;
+ offset_bits = (offset & 3) << 1;
+
+ config = &ts->port_config[offset >> 2];
+
+ if (ts->input_pullup_active & BIT(offset))
+ pin_config = PIN_CONFIG_IN_PULLUP;
+ else
+ pin_config = PIN_CONFIG_IN_WO_PULLUP;
+
+ mutex_lock(&ts->lock);
+
+ *config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
+ | (pin_config << offset_bits);
+
+ ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
+
+ mutex_unlock(&ts->lock);
+
+ return ret;
+}
+
+static int __max7301_set(struct max7301 *ts, unsigned offset, int value)
+{
+ if (value) {
+ ts->out_level |= 1 << offset;
+ return ts->write(ts->dev, 0x20 + offset, 0x01);
+ } else {
+ ts->out_level &= ~(1 << offset);
+ return ts->write(ts->dev, 0x20 + offset, 0x00);
+ }
+}
+
+static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct max7301 *ts = container_of(chip, struct max7301, chip);
+ u8 *config;
+ u8 offset_bits;
+ int ret;
+
+ /* First 4 pins are unused in the controller */
+ offset += 4;
+ offset_bits = (offset & 3) << 1;
+
+ config = &ts->port_config[offset >> 2];
+
+ mutex_lock(&ts->lock);
+
+ *config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
+ | (PIN_CONFIG_OUT << offset_bits);
+
+ ret = __max7301_set(ts, offset, value);
+
+ if (!ret)
+ ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
+
+ mutex_unlock(&ts->lock);
+
+ return ret;
+}
+
+static int max7301_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct max7301 *ts = container_of(chip, struct max7301, chip);
+ int config, level = -EINVAL;
+
+ /* First 4 pins are unused in the controller */
+ offset += 4;
+
+ mutex_lock(&ts->lock);
+
+ config = (ts->port_config[offset >> 2] >> ((offset & 3) << 1))
+ & PIN_CONFIG_MASK;
+
+ switch (config) {
+ case PIN_CONFIG_OUT:
+ /* Output: return cached level */
+ level = !!(ts->out_level & (1 << offset));
+ break;
+ case PIN_CONFIG_IN_WO_PULLUP:
+ case PIN_CONFIG_IN_PULLUP:
+ /* Input: read out */
+ level = ts->read(ts->dev, 0x20 + offset) & 0x01;
+ }
+ mutex_unlock(&ts->lock);
+
+ return level;
+}
+
+static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct max7301 *ts = container_of(chip, struct max7301, chip);
+
+ /* First 4 pins are unused in the controller */
+ offset += 4;
+
+ mutex_lock(&ts->lock);
+
+ __max7301_set(ts, offset, value);
+
+ mutex_unlock(&ts->lock);
+}
+
+int __max730x_probe(struct max7301 *ts)
+{
+ struct device *dev = ts->dev;
+ struct max7301_platform_data *pdata;
+ int i, ret;
+
+ pdata = dev_get_platdata(dev);
+
+ mutex_init(&ts->lock);
+ dev_set_drvdata(dev, ts);
+
+ /* Power up the chip and disable IRQ output */
+ ts->write(dev, 0x04, 0x01);
+
+ if (pdata) {
+ ts->input_pullup_active = pdata->input_pullup_active;
+ ts->chip.base = pdata->base;
+ } else {
+ ts->chip.base = -1;
+ }
+ ts->chip.label = dev->driver->name;
+
+ ts->chip.direction_input = max7301_direction_input;
+ ts->chip.get = max7301_get;
+ ts->chip.direction_output = max7301_direction_output;
+ ts->chip.set = max7301_set;
+
+ ts->chip.ngpio = PIN_NUMBER;
+ ts->chip.can_sleep = true;
+ ts->chip.dev = dev;
+ ts->chip.owner = THIS_MODULE;
+
+ /*
+ * initialize pullups according to platform data and cache the
+ * register values for later use.
+ */
+ for (i = 1; i < 8; i++) {
+ int j;
+ /*
+ * initialize port_config with "0xAA", which means
+ * input with internal pullup disabled. This is needed
+ * to avoid writing zeros (in the inner for loop),
+ * which is not allowed according to the datasheet.
+ */
+ ts->port_config[i] = 0xAA;
+ for (j = 0; j < 4; j++) {
+ int offset = (i - 1) * 4 + j;
+ ret = max7301_direction_input(&ts->chip, offset);
+ if (ret)
+ goto exit_destroy;
+ }
+ }
+
+ ret = gpiochip_add(&ts->chip);
+ if (ret)
+ goto exit_destroy;
+
+ return ret;
+
+exit_destroy:
+ mutex_destroy(&ts->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__max730x_probe);
+
+int __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);
+ kfree(ts);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__max730x_remove);
+
+MODULE_AUTHOR("Juergen Beisert, Wolfram Sang");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MAX730x GPIO-Expanders, generic parts");
diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
new file mode 100644
index 000000000..0fa4543c5
--- /dev/null
+++ b/drivers/gpio/gpio-max732x.c
@@ -0,0 +1,764 @@
+/*
+ * MAX732x I2C Port Expander with 8/16 I/O
+ *
+ * Copyright (C) 2007 Marvell International Ltd.
+ * Copyright (C) 2008 Jack Ren <jack.ren@marvell.com>
+ * Copyright (C) 2008 Eric Miao <eric.miao@marvell.com>
+ * Copyright (C) 2015 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Derived from drivers/gpio/pca953x.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c/max732x.h>
+#include <linux/of.h>
+
+
+/*
+ * Each port of MAX732x (including MAX7319) falls into one of the
+ * following three types:
+ *
+ * - Push Pull Output
+ * - Input
+ * - Open Drain I/O
+ *
+ * designated by 'O', 'I' and 'P' individually according to MAXIM's
+ * datasheets. 'I' and 'P' ports are interrupt capables, some with
+ * a dedicated interrupt mask.
+ *
+ * There are two groups of I/O ports, each group usually includes
+ * up to 8 I/O ports, and is accessed by a specific I2C address:
+ *
+ * - Group A : by I2C address 0b'110xxxx
+ * - Group B : by I2C address 0b'101xxxx
+ *
+ * where 'xxxx' is decided by the connections of pin AD2/AD0. The
+ * address used also affects the initial state of output signals.
+ *
+ * Within each group of ports, there are five known combinations of
+ * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for
+ * the detailed organization of these ports. Only Goup A is interrupt
+ * capable.
+ *
+ * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16',
+ * and GPIOs from GROUP_A are numbered before those from GROUP_B
+ * (if there are two groups).
+ *
+ * NOTE: MAX7328/MAX7329 are drop-in replacements for PCF8574/a, so
+ * they are not supported by this driver.
+ */
+
+#define PORT_NONE 0x0 /* '/' No Port */
+#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */
+#define PORT_INPUT 0x2 /* 'I' Input Only */
+#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */
+
+#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */
+#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */
+#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */
+#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */
+#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */
+
+#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */
+#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */
+
+#define INT_NONE 0x0 /* No interrupt capability */
+#define INT_NO_MASK 0x1 /* Has interrupts, no mask */
+#define INT_INDEP_MASK 0x2 /* Has interrupts, independent mask */
+#define INT_MERGED_MASK 0x3 /* Has interrupts, merged mask */
+
+#define INT_CAPS(x) (((uint64_t)(x)) << 32)
+
+enum {
+ MAX7319,
+ MAX7320,
+ MAX7321,
+ MAX7322,
+ MAX7323,
+ MAX7324,
+ MAX7325,
+ MAX7326,
+ MAX7327,
+};
+
+static uint64_t max732x_features[] = {
+ [MAX7319] = GROUP_A(IO_8I) | INT_CAPS(INT_MERGED_MASK),
+ [MAX7320] = GROUP_B(IO_8O),
+ [MAX7321] = GROUP_A(IO_8P) | INT_CAPS(INT_NO_MASK),
+ [MAX7322] = GROUP_A(IO_4I4O) | INT_CAPS(INT_MERGED_MASK),
+ [MAX7323] = GROUP_A(IO_4P4O) | INT_CAPS(INT_INDEP_MASK),
+ [MAX7324] = GROUP_A(IO_8I) | GROUP_B(IO_8O) | INT_CAPS(INT_MERGED_MASK),
+ [MAX7325] = GROUP_A(IO_8P) | GROUP_B(IO_8O) | INT_CAPS(INT_NO_MASK),
+ [MAX7326] = GROUP_A(IO_4I4O) | GROUP_B(IO_8O) | INT_CAPS(INT_MERGED_MASK),
+ [MAX7327] = GROUP_A(IO_4P4O) | GROUP_B(IO_8O) | INT_CAPS(INT_NO_MASK),
+};
+
+static const struct i2c_device_id max732x_id[] = {
+ { "max7319", MAX7319 },
+ { "max7320", MAX7320 },
+ { "max7321", MAX7321 },
+ { "max7322", MAX7322 },
+ { "max7323", MAX7323 },
+ { "max7324", MAX7324 },
+ { "max7325", MAX7325 },
+ { "max7326", MAX7326 },
+ { "max7327", MAX7327 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, max732x_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max732x_of_table[] = {
+ { .compatible = "maxim,max7319" },
+ { .compatible = "maxim,max7320" },
+ { .compatible = "maxim,max7321" },
+ { .compatible = "maxim,max7322" },
+ { .compatible = "maxim,max7323" },
+ { .compatible = "maxim,max7324" },
+ { .compatible = "maxim,max7325" },
+ { .compatible = "maxim,max7326" },
+ { .compatible = "maxim,max7327" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max732x_of_table);
+#endif
+
+struct max732x_chip {
+ struct gpio_chip gpio_chip;
+
+ struct i2c_client *client; /* "main" client */
+ struct i2c_client *client_dummy;
+ struct i2c_client *client_group_a;
+ struct i2c_client *client_group_b;
+
+ unsigned int mask_group_a;
+ unsigned int dir_input;
+ unsigned int dir_output;
+
+ struct mutex lock;
+ uint8_t reg_out[2];
+
+#ifdef CONFIG_GPIO_MAX732X_IRQ
+ struct mutex irq_lock;
+ uint8_t irq_mask;
+ uint8_t irq_mask_cur;
+ uint8_t irq_trig_raise;
+ uint8_t irq_trig_fall;
+ uint8_t irq_features;
+#endif
+};
+
+static inline struct max732x_chip *to_max732x(struct gpio_chip *gc)
+{
+ return container_of(gc, struct max732x_chip, gpio_chip);
+}
+
+static int max732x_writeb(struct max732x_chip *chip, int group_a, uint8_t val)
+{
+ struct i2c_client *client;
+ int ret;
+
+ client = group_a ? chip->client_group_a : chip->client_group_b;
+ ret = i2c_smbus_write_byte(client, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writing\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max732x_readb(struct max732x_chip *chip, int group_a, uint8_t *val)
+{
+ struct i2c_client *client;
+ int ret;
+
+ client = group_a ? chip->client_group_a : chip->client_group_b;
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading\n");
+ return ret;
+ }
+
+ *val = (uint8_t)ret;
+ return 0;
+}
+
+static inline int is_group_a(struct max732x_chip *chip, unsigned off)
+{
+ return (1u << off) & chip->mask_group_a;
+}
+
+static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off)
+{
+ struct max732x_chip *chip = to_max732x(gc);
+ uint8_t reg_val;
+ int ret;
+
+ ret = max732x_readb(chip, is_group_a(chip, off), &reg_val);
+ if (ret < 0)
+ return 0;
+
+ return reg_val & (1u << (off & 0x7));
+}
+
+static void max732x_gpio_set_mask(struct gpio_chip *gc, unsigned off, int mask,
+ int val)
+{
+ struct max732x_chip *chip = to_max732x(gc);
+ uint8_t reg_out;
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0];
+ reg_out = (reg_out & ~mask) | (val & mask);
+
+ ret = max732x_writeb(chip, is_group_a(chip, off), reg_out);
+ if (ret < 0)
+ goto out;
+
+ /* update the shadow register then */
+ if (off > 7)
+ chip->reg_out[1] = reg_out;
+ else
+ chip->reg_out[0] = reg_out;
+out:
+ mutex_unlock(&chip->lock);
+}
+
+static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
+{
+ unsigned base = off & ~0x7;
+ uint8_t mask = 1u << (off & 0x7);
+
+ max732x_gpio_set_mask(gc, base, mask, val << (off & 0x7));
+}
+
+static void max732x_gpio_set_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
+{
+ unsigned mask_lo = mask[0] & 0xff;
+ unsigned mask_hi = (mask[0] >> 8) & 0xff;
+
+ if (mask_lo)
+ max732x_gpio_set_mask(gc, 0, mask_lo, bits[0] & 0xff);
+ if (mask_hi)
+ max732x_gpio_set_mask(gc, 8, mask_hi, (bits[0] >> 8) & 0xff);
+}
+
+static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
+{
+ struct max732x_chip *chip = to_max732x(gc);
+ unsigned int mask = 1u << off;
+
+ if ((mask & chip->dir_input) == 0) {
+ dev_dbg(&chip->client->dev, "%s port %d is output only\n",
+ chip->client->name, off);
+ return -EACCES;
+ }
+
+ /*
+ * Open-drain pins must be set to high impedance (which is
+ * equivalent to output-high) to be turned into an input.
+ */
+ if ((mask & chip->dir_output))
+ max732x_gpio_set_value(gc, off, 1);
+
+ return 0;
+}
+
+static int max732x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned off, int val)
+{
+ struct max732x_chip *chip = to_max732x(gc);
+ unsigned int mask = 1u << off;
+
+ if ((mask & chip->dir_output) == 0) {
+ dev_dbg(&chip->client->dev, "%s port %d is input only\n",
+ chip->client->name, off);
+ return -EACCES;
+ }
+
+ max732x_gpio_set_value(gc, off, val);
+ return 0;
+}
+
+#ifdef CONFIG_GPIO_MAX732X_IRQ
+static int max732x_writew(struct max732x_chip *chip, uint16_t val)
+{
+ int ret;
+
+ val = cpu_to_le16(val);
+
+ ret = i2c_master_send(chip->client_group_a, (char *)&val, 2);
+ if (ret < 0) {
+ dev_err(&chip->client_group_a->dev, "failed writing\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max732x_readw(struct max732x_chip *chip, uint16_t *val)
+{
+ int ret;
+
+ ret = i2c_master_recv(chip->client_group_a, (char *)val, 2);
+ if (ret < 0) {
+ dev_err(&chip->client_group_a->dev, "failed reading\n");
+ return ret;
+ }
+
+ *val = le16_to_cpu(*val);
+ return 0;
+}
+
+static void max732x_irq_update_mask(struct max732x_chip *chip)
+{
+ uint16_t msg;
+
+ if (chip->irq_mask == chip->irq_mask_cur)
+ return;
+
+ chip->irq_mask = chip->irq_mask_cur;
+
+ if (chip->irq_features == INT_NO_MASK)
+ return;
+
+ mutex_lock(&chip->lock);
+
+ switch (chip->irq_features) {
+ case INT_INDEP_MASK:
+ msg = (chip->irq_mask << 8) | chip->reg_out[0];
+ max732x_writew(chip, msg);
+ break;
+
+ case INT_MERGED_MASK:
+ msg = chip->irq_mask | chip->reg_out[0];
+ max732x_writeb(chip, 1, (uint8_t)msg);
+ break;
+ }
+
+ mutex_unlock(&chip->lock);
+}
+
+static void max732x_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct max732x_chip *chip = to_max732x(gc);
+
+ chip->irq_mask_cur &= ~(1 << d->hwirq);
+}
+
+static void max732x_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct max732x_chip *chip = to_max732x(gc);
+
+ chip->irq_mask_cur |= 1 << d->hwirq;
+}
+
+static void max732x_irq_bus_lock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct max732x_chip *chip = to_max732x(gc);
+
+ mutex_lock(&chip->irq_lock);
+ chip->irq_mask_cur = chip->irq_mask;
+}
+
+static void max732x_irq_bus_sync_unlock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct max732x_chip *chip = to_max732x(gc);
+ uint16_t new_irqs;
+ uint16_t level;
+
+ max732x_irq_update_mask(chip);
+
+ new_irqs = chip->irq_trig_fall | chip->irq_trig_raise;
+ while (new_irqs) {
+ level = __ffs(new_irqs);
+ max732x_gpio_direction_input(&chip->gpio_chip, level);
+ new_irqs &= ~(1 << level);
+ }
+
+ mutex_unlock(&chip->irq_lock);
+}
+
+static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct max732x_chip *chip = to_max732x(gc);
+ uint16_t off = d->hwirq;
+ uint16_t mask = 1 << off;
+
+ if (!(mask & chip->dir_input)) {
+ dev_dbg(&chip->client->dev, "%s port %d is output only\n",
+ chip->client->name, off);
+ return -EACCES;
+ }
+
+ if (!(type & IRQ_TYPE_EDGE_BOTH)) {
+ dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
+ d->irq, type);
+ return -EINVAL;
+ }
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ chip->irq_trig_fall |= mask;
+ else
+ chip->irq_trig_fall &= ~mask;
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ chip->irq_trig_raise |= mask;
+ else
+ chip->irq_trig_raise &= ~mask;
+
+ return 0;
+}
+
+static struct irq_chip max732x_irq_chip = {
+ .name = "max732x",
+ .irq_mask = max732x_irq_mask,
+ .irq_unmask = max732x_irq_unmask,
+ .irq_bus_lock = max732x_irq_bus_lock,
+ .irq_bus_sync_unlock = max732x_irq_bus_sync_unlock,
+ .irq_set_type = max732x_irq_set_type,
+};
+
+static uint8_t max732x_irq_pending(struct max732x_chip *chip)
+{
+ uint8_t cur_stat;
+ uint8_t old_stat;
+ uint8_t trigger;
+ uint8_t pending;
+ uint16_t status;
+ int ret;
+
+ ret = max732x_readw(chip, &status);
+ if (ret)
+ return 0;
+
+ trigger = status >> 8;
+ trigger &= chip->irq_mask;
+
+ if (!trigger)
+ return 0;
+
+ cur_stat = status & 0xFF;
+ cur_stat &= chip->irq_mask;
+
+ old_stat = cur_stat ^ trigger;
+
+ pending = (old_stat & chip->irq_trig_fall) |
+ (cur_stat & chip->irq_trig_raise);
+ pending &= trigger;
+
+ return pending;
+}
+
+static irqreturn_t max732x_irq_handler(int irq, void *devid)
+{
+ struct max732x_chip *chip = devid;
+ uint8_t pending;
+ uint8_t level;
+
+ pending = max732x_irq_pending(chip);
+
+ if (!pending)
+ return IRQ_HANDLED;
+
+ do {
+ level = __ffs(pending);
+ handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain,
+ level));
+
+ pending &= ~(1 << level);
+ } while (pending);
+
+ return IRQ_HANDLED;
+}
+
+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;
+ chip->irq_features = has_irq;
+ mutex_init(&chip->irq_lock);
+
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL,
+ max732x_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&client->dev), chip);
+ if (ret) {
+ dev_err(&client->dev, "failed to request irq %d\n",
+ client->irq);
+ return ret;
+ }
+ ret = gpiochip_irqchip_add(&chip->gpio_chip,
+ &max732x_irq_chip,
+ irq_base,
+ handle_edge_irq,
+ IRQ_TYPE_NONE);
+ if (ret) {
+ dev_err(&client->dev,
+ "could not connect irqchip to gpiochip\n");
+ return ret;
+ }
+ gpiochip_set_chained_irqchip(&chip->gpio_chip,
+ &max732x_irq_chip,
+ client->irq,
+ NULL);
+ }
+
+ return 0;
+}
+
+#else /* CONFIG_GPIO_MAX732X_IRQ */
+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)
+ dev_warn(&client->dev, "interrupt support not compiled in\n");
+
+ return 0;
+}
+#endif
+
+static int max732x_setup_gpio(struct max732x_chip *chip,
+ const struct i2c_device_id *id,
+ unsigned gpio_start)
+{
+ struct gpio_chip *gc = &chip->gpio_chip;
+ uint32_t id_data = (uint32_t)max732x_features[id->driver_data];
+ int i, port = 0;
+
+ for (i = 0; i < 16; i++, id_data >>= 2) {
+ unsigned int mask = 1 << port;
+
+ switch (id_data & 0x3) {
+ case PORT_OUTPUT:
+ chip->dir_output |= mask;
+ break;
+ case PORT_INPUT:
+ chip->dir_input |= mask;
+ break;
+ case PORT_OPENDRAIN:
+ chip->dir_output |= mask;
+ chip->dir_input |= mask;
+ break;
+ default:
+ continue;
+ }
+
+ if (i < 8)
+ chip->mask_group_a |= mask;
+ port++;
+ }
+
+ if (chip->dir_input)
+ gc->direction_input = max732x_gpio_direction_input;
+ if (chip->dir_output) {
+ gc->direction_output = max732x_gpio_direction_output;
+ gc->set = max732x_gpio_set_value;
+ gc->set_multiple = max732x_gpio_set_multiple;
+ }
+ gc->get = max732x_gpio_get_value;
+ gc->can_sleep = true;
+
+ gc->base = gpio_start;
+ gc->ngpio = port;
+ gc->label = chip->client->name;
+ gc->owner = THIS_MODULE;
+
+ return port;
+}
+
+static struct max732x_platform_data *of_gpio_max732x(struct device *dev)
+{
+ struct max732x_platform_data *pdata;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->gpio_base = -1;
+
+ return pdata;
+}
+
+static int max732x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max732x_platform_data *pdata;
+ struct device_node *node;
+ struct max732x_chip *chip;
+ struct i2c_client *c;
+ uint16_t addr_a, addr_b;
+ int ret, nr_port;
+
+ pdata = dev_get_platdata(&client->dev);
+ node = client->dev.of_node;
+
+ if (!pdata && node)
+ pdata = of_gpio_max732x(&client->dev);
+
+ if (!pdata) {
+ dev_dbg(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+ chip->client = client;
+
+ nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base);
+ chip->gpio_chip.dev = &client->dev;
+
+ addr_a = (client->addr & 0x0f) | 0x60;
+ addr_b = (client->addr & 0x0f) | 0x50;
+
+ switch (client->addr & 0x70) {
+ case 0x60:
+ chip->client_group_a = client;
+ if (nr_port > 8) {
+ c = i2c_new_dummy(client->adapter, addr_b);
+ chip->client_group_b = chip->client_dummy = c;
+ }
+ break;
+ case 0x50:
+ chip->client_group_b = client;
+ if (nr_port > 8) {
+ c = i2c_new_dummy(client->adapter, addr_a);
+ chip->client_group_a = chip->client_dummy = c;
+ }
+ break;
+ default:
+ dev_err(&client->dev, "invalid I2C address specified %02x\n",
+ client->addr);
+ ret = -EINVAL;
+ goto out_failed;
+ }
+
+ if (nr_port > 8 && !chip->client_dummy) {
+ dev_err(&client->dev,
+ "Failed to allocate second group I2C device\n");
+ ret = -ENODEV;
+ goto out_failed;
+ }
+
+ mutex_init(&chip->lock);
+
+ max732x_readb(chip, is_group_a(chip, 0), &chip->reg_out[0]);
+ if (nr_port > 8)
+ max732x_readb(chip, is_group_a(chip, 8), &chip->reg_out[1]);
+
+ ret = gpiochip_add(&chip->gpio_chip);
+ if (ret)
+ goto out_failed;
+
+ ret = max732x_irq_setup(chip, id);
+ if (ret) {
+ gpiochip_remove(&chip->gpio_chip);
+ goto out_failed;
+ }
+
+ 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;
+
+out_failed:
+ if (chip->client_dummy)
+ i2c_unregister_device(chip->client_dummy);
+ return ret;
+}
+
+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;
+ }
+ }
+
+ gpiochip_remove(&chip->gpio_chip);
+
+ /* unregister any dummy i2c_client */
+ if (chip->client_dummy)
+ i2c_unregister_device(chip->client_dummy);
+
+ return 0;
+}
+
+static struct i2c_driver max732x_driver = {
+ .driver = {
+ .name = "max732x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max732x_of_table),
+ },
+ .probe = max732x_probe,
+ .remove = max732x_remove,
+ .id_table = max732x_id,
+};
+
+static int __init max732x_init(void)
+{
+ return i2c_add_driver(&max732x_driver);
+}
+/* register after i2c postcore initcall and before
+ * subsys initcalls that may rely on these GPIOs
+ */
+subsys_initcall(max732x_init);
+
+static void __exit max732x_exit(void)
+{
+ i2c_del_driver(&max732x_driver);
+}
+module_exit(max732x_exit);
+
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
+MODULE_DESCRIPTION("GPIO expander driver for MAX732X");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c
new file mode 100644
index 000000000..ee93c0ab0
--- /dev/null
+++ b/drivers/gpio/gpio-mb86s7x.c
@@ -0,0 +1,237 @@
+/*
+ * linux/drivers/gpio/gpio-mb86s7x.c
+ *
+ * Copyright (C) 2015 Fujitsu Semiconductor Limited
+ * Copyright (C) 2015 Linaro Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+/*
+ * Only first 8bits of a register correspond to each pin,
+ * so there are 4 registers for 32 pins.
+ */
+#define PDR(x) (0x0 + x / 8 * 4)
+#define DDR(x) (0x10 + x / 8 * 4)
+#define PFR(x) (0x20 + x / 8 * 4)
+
+#define OFFSET(x) BIT((x) % 8)
+
+struct mb86s70_gpio_chip {
+ struct gpio_chip gc;
+ void __iomem *base;
+ struct clk *clk;
+ spinlock_t lock;
+};
+
+static inline struct mb86s70_gpio_chip *chip_to_mb86s70(struct gpio_chip *gc)
+{
+ return container_of(gc, struct mb86s70_gpio_chip, gc);
+}
+
+static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned gpio)
+{
+ struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&gchip->lock, flags);
+
+ val = readl(gchip->base + PFR(gpio));
+ if (!(val & OFFSET(gpio))) {
+ spin_unlock_irqrestore(&gchip->lock, flags);
+ return -EINVAL;
+ }
+
+ val &= ~OFFSET(gpio);
+ writel(val, gchip->base + PFR(gpio));
+
+ spin_unlock_irqrestore(&gchip->lock, flags);
+
+ return 0;
+}
+
+static void mb86s70_gpio_free(struct gpio_chip *gc, unsigned gpio)
+{
+ struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&gchip->lock, flags);
+
+ val = readl(gchip->base + PFR(gpio));
+ val |= OFFSET(gpio);
+ writel(val, gchip->base + PFR(gpio));
+
+ spin_unlock_irqrestore(&gchip->lock, flags);
+}
+
+static int mb86s70_gpio_direction_input(struct gpio_chip *gc, unsigned gpio)
+{
+ struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
+ unsigned long flags;
+ unsigned char val;
+
+ spin_lock_irqsave(&gchip->lock, flags);
+
+ val = readl(gchip->base + DDR(gpio));
+ val &= ~OFFSET(gpio);
+ writel(val, gchip->base + DDR(gpio));
+
+ spin_unlock_irqrestore(&gchip->lock, flags);
+
+ return 0;
+}
+
+static int mb86s70_gpio_direction_output(struct gpio_chip *gc,
+ unsigned gpio, int value)
+{
+ struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
+ unsigned long flags;
+ unsigned char val;
+
+ spin_lock_irqsave(&gchip->lock, flags);
+
+ val = readl(gchip->base + PDR(gpio));
+ if (value)
+ val |= OFFSET(gpio);
+ else
+ val &= ~OFFSET(gpio);
+ writel(val, gchip->base + PDR(gpio));
+
+ val = readl(gchip->base + DDR(gpio));
+ val |= OFFSET(gpio);
+ writel(val, gchip->base + DDR(gpio));
+
+ spin_unlock_irqrestore(&gchip->lock, flags);
+
+ return 0;
+}
+
+static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+ struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
+
+ return !!(readl(gchip->base + PDR(gpio)) & OFFSET(gpio));
+}
+
+static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value)
+{
+ struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
+ unsigned long flags;
+ unsigned char val;
+
+ spin_lock_irqsave(&gchip->lock, flags);
+
+ val = readl(gchip->base + PDR(gpio));
+ if (value)
+ val |= OFFSET(gpio);
+ else
+ val &= ~OFFSET(gpio);
+ writel(val, gchip->base + PDR(gpio));
+
+ spin_unlock_irqrestore(&gchip->lock, flags);
+}
+
+static int mb86s70_gpio_probe(struct platform_device *pdev)
+{
+ struct mb86s70_gpio_chip *gchip;
+ struct resource *res;
+ int ret;
+
+ gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
+ if (gchip == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, gchip);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gchip->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gchip->base))
+ return PTR_ERR(gchip->base);
+
+ gchip->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(gchip->clk))
+ return PTR_ERR(gchip->clk);
+
+ clk_prepare_enable(gchip->clk);
+
+ spin_lock_init(&gchip->lock);
+
+ gchip->gc.direction_output = mb86s70_gpio_direction_output;
+ gchip->gc.direction_input = mb86s70_gpio_direction_input;
+ gchip->gc.request = mb86s70_gpio_request;
+ gchip->gc.free = mb86s70_gpio_free;
+ gchip->gc.get = mb86s70_gpio_get;
+ gchip->gc.set = mb86s70_gpio_set;
+ gchip->gc.label = dev_name(&pdev->dev);
+ gchip->gc.ngpio = 32;
+ gchip->gc.owner = THIS_MODULE;
+ gchip->gc.dev = &pdev->dev;
+ gchip->gc.base = -1;
+
+ platform_set_drvdata(pdev, gchip);
+
+ ret = gpiochip_add(&gchip->gc);
+ if (ret) {
+ dev_err(&pdev->dev, "couldn't register gpio driver\n");
+ clk_disable_unprepare(gchip->clk);
+ }
+
+ return ret;
+}
+
+static int mb86s70_gpio_remove(struct platform_device *pdev)
+{
+ struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gchip->gc);
+ clk_disable_unprepare(gchip->clk);
+
+ return 0;
+}
+
+static const struct of_device_id mb86s70_gpio_dt_ids[] = {
+ { .compatible = "fujitsu,mb86s70-gpio" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);
+
+static struct platform_driver mb86s70_gpio_driver = {
+ .driver = {
+ .name = "mb86s70-gpio",
+ .of_match_table = mb86s70_gpio_dt_ids,
+ },
+ .probe = mb86s70_gpio_probe,
+ .remove = mb86s70_gpio_remove,
+};
+
+static int __init mb86s70_gpio_init(void)
+{
+ return platform_driver_register(&mb86s70_gpio_driver);
+}
+module_init(mb86s70_gpio_init);
+
+MODULE_DESCRIPTION("MB86S7x GPIO Driver");
+MODULE_ALIAS("platform:mb86s70-gpio");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c
new file mode 100644
index 000000000..a431604c9
--- /dev/null
+++ b/drivers/gpio/gpio-mc33880.c
@@ -0,0 +1,189 @@
+/*
+ * MC33880 high-side/low-side switch GPIO driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Freescale MC33880 high-side/low-side switch
+ */
+
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mc33880.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define DRIVER_NAME "mc33880"
+
+/*
+ * Pin configurations, see MAX7301 datasheet page 6
+ */
+#define PIN_CONFIG_MASK 0x03
+#define PIN_CONFIG_IN_PULLUP 0x03
+#define PIN_CONFIG_IN_WO_PULLUP 0x02
+#define PIN_CONFIG_OUT 0x01
+
+#define PIN_NUMBER 8
+
+
+/*
+ * Some registers must be read back to modify.
+ * To save time we cache them here in memory
+ */
+struct mc33880 {
+ struct mutex lock; /* protect from simultaneous accesses */
+ u8 port_config;
+ struct gpio_chip chip;
+ struct spi_device *spi;
+};
+
+static int mc33880_write_config(struct mc33880 *mc)
+{
+ return spi_write(mc->spi, &mc->port_config, sizeof(mc->port_config));
+}
+
+
+static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value)
+{
+ if (value)
+ mc->port_config |= 1 << offset;
+ else
+ mc->port_config &= ~(1 << offset);
+
+ return mc33880_write_config(mc);
+}
+
+
+static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct mc33880 *mc = container_of(chip, struct mc33880, chip);
+
+ mutex_lock(&mc->lock);
+
+ __mc33880_set(mc, offset, value);
+
+ mutex_unlock(&mc->lock);
+}
+
+static int mc33880_probe(struct spi_device *spi)
+{
+ struct mc33880 *mc;
+ struct mc33880_platform_data *pdata;
+ int ret;
+
+ pdata = dev_get_platdata(&spi->dev);
+ if (!pdata || !pdata->base) {
+ dev_dbg(&spi->dev, "incorrect or missing platform data\n");
+ return -EINVAL;
+ }
+
+ /*
+ * bits_per_word cannot be configured in platform data
+ */
+ spi->bits_per_word = 8;
+
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ mc = devm_kzalloc(&spi->dev, sizeof(struct mc33880), GFP_KERNEL);
+ if (!mc)
+ return -ENOMEM;
+
+ mutex_init(&mc->lock);
+
+ spi_set_drvdata(spi, mc);
+
+ mc->spi = spi;
+
+ mc->chip.label = DRIVER_NAME,
+ mc->chip.set = mc33880_set;
+ mc->chip.base = pdata->base;
+ mc->chip.ngpio = PIN_NUMBER;
+ mc->chip.can_sleep = true;
+ mc->chip.dev = &spi->dev;
+ mc->chip.owner = THIS_MODULE;
+
+ mc->port_config = 0x00;
+ /* write twice, because during initialisation the first setting
+ * is just for testing SPI communication, and the second is the
+ * "real" configuration
+ */
+ ret = mc33880_write_config(mc);
+ mc->port_config = 0x00;
+ if (!ret)
+ ret = mc33880_write_config(mc);
+
+ if (ret) {
+ dev_err(&spi->dev, "Failed writing to " DRIVER_NAME ": %d\n",
+ ret);
+ goto exit_destroy;
+ }
+
+ ret = gpiochip_add(&mc->chip);
+ if (ret)
+ goto exit_destroy;
+
+ return ret;
+
+exit_destroy:
+ mutex_destroy(&mc->lock);
+ return ret;
+}
+
+static int mc33880_remove(struct spi_device *spi)
+{
+ struct mc33880 *mc;
+
+ mc = spi_get_drvdata(spi);
+ if (!mc)
+ return -ENODEV;
+
+ gpiochip_remove(&mc->chip);
+ mutex_destroy(&mc->lock);
+
+ return 0;
+}
+
+static struct spi_driver mc33880_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = mc33880_probe,
+ .remove = mc33880_remove,
+};
+
+static int __init mc33880_init(void)
+{
+ return spi_register_driver(&mc33880_driver);
+}
+/* register after spi postcore initcall and before
+ * subsys initcalls that may rely on these GPIOs
+ */
+subsys_initcall(mc33880_init);
+
+static void __exit mc33880_exit(void)
+{
+ spi_unregister_driver(&mc33880_driver);
+}
+module_exit(mc33880_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
new file mode 100644
index 000000000..d62b4f818
--- /dev/null
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2009-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Author: Wu Guoxing <b39297@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.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 inline struct mc9s08dz60 *to_mc9s08dz60(struct gpio_chip *gc)
+{
+ return container_of(gc, struct mc9s08dz60, 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 = to_mc9s08dz60(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 = to_mc9s08dz60(gc);
+
+ mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(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.dev = &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 gpiochip_add(&mc9s->chip);
+}
+
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+ struct mc9s08dz60 *mc9s;
+
+ mc9s = i2c_get_clientdata(client);
+
+ gpiochip_remove(&mc9s->chip);
+ return 0;
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ {"mc9s08dz60", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+module_i2c_driver(mc9s08dz60_i2c_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc. "
+ "Wu Guoxing <b39297@freescale.com>");
+MODULE_DESCRIPTION("mc9s08dz60 gpio function on mx35 3ds board");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
new file mode 100644
index 000000000..2fc7ff852
--- /dev/null
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -0,0 +1,1083 @@
+/*
+ * MCP23S08 SPI/I2C GPIO gpio expander driver
+ *
+ * The inputs and outputs of the mcp23s08, mcp23s17, mcp23008 and mcp23017 are
+ * supported.
+ * For the I2C versions of the chips (mcp23008 and mcp23017) generation of
+ * interrupts is also supported.
+ * The hardware of the SPI versions of the chips (mcp23s08 and mcp23s17) is
+ * also capable of generating interrupts, but the linux driver does not
+ * support that yet.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mcp23s08.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+
+/**
+ * MCP types supported by driver
+ */
+#define MCP_TYPE_S08 0
+#define MCP_TYPE_S17 1
+#define MCP_TYPE_008 2
+#define MCP_TYPE_017 3
+
+/* Registers are all 8 bits wide.
+ *
+ * The mcp23s17 has twice as many bits, and can be configured to work
+ * with either 16 bit registers or with two adjacent 8 bit banks.
+ */
+#define MCP_IODIR 0x00 /* init/reset: all ones */
+#define MCP_IPOL 0x01
+#define MCP_GPINTEN 0x02
+#define MCP_DEFVAL 0x03
+#define MCP_INTCON 0x04
+#define MCP_IOCON 0x05
+# define IOCON_MIRROR (1 << 6)
+# define IOCON_SEQOP (1 << 5)
+# define IOCON_HAEN (1 << 3)
+# define IOCON_ODR (1 << 2)
+# define IOCON_INTPOL (1 << 1)
+#define MCP_GPPU 0x06
+#define MCP_INTF 0x07
+#define MCP_INTCAP 0x08
+#define MCP_GPIO 0x09
+#define MCP_OLAT 0x0a
+
+struct mcp23s08;
+
+struct mcp23s08_ops {
+ int (*read)(struct mcp23s08 *mcp, unsigned reg);
+ int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val);
+ int (*read_regs)(struct mcp23s08 *mcp, unsigned reg,
+ u16 *vals, unsigned n);
+};
+
+struct mcp23s08 {
+ u8 addr;
+ bool irq_active_high;
+
+ u16 cache[11];
+ u16 irq_rise;
+ u16 irq_fall;
+ int irq;
+ bool irq_controller;
+ /* lock protects the cached values */
+ struct mutex lock;
+ struct mutex irq_lock;
+ struct irq_domain *irq_domain;
+
+ struct gpio_chip chip;
+
+ const struct mcp23s08_ops *ops;
+ void *data; /* ops specific data */
+};
+
+/* A given spi_device can represent up to eight mcp23sxx chips
+ * sharing the same chipselect but using different addresses
+ * (e.g. chips #0 and #3 might be populated, but not #1 or $2).
+ * Driver data holds all the per-chip data.
+ */
+struct mcp23s08_driver_data {
+ unsigned ngpio;
+ struct mcp23s08 *mcp[8];
+ struct mcp23s08 chip[];
+};
+
+/* This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
+/*----------------------------------------------------------------------*/
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int mcp23008_read(struct mcp23s08 *mcp, unsigned reg)
+{
+ return i2c_smbus_read_byte_data(mcp->data, reg);
+}
+
+static int mcp23008_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
+{
+ return i2c_smbus_write_byte_data(mcp->data, reg, val);
+}
+
+static int
+mcp23008_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
+{
+ while (n--) {
+ int ret = mcp23008_read(mcp, reg++);
+ if (ret < 0)
+ return ret;
+ *vals++ = ret;
+ }
+
+ return 0;
+}
+
+static int mcp23017_read(struct mcp23s08 *mcp, unsigned reg)
+{
+ return i2c_smbus_read_word_data(mcp->data, reg << 1);
+}
+
+static int mcp23017_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
+{
+ return i2c_smbus_write_word_data(mcp->data, reg << 1, val);
+}
+
+static int
+mcp23017_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
+{
+ while (n--) {
+ int ret = mcp23017_read(mcp, reg++);
+ if (ret < 0)
+ return ret;
+ *vals++ = ret;
+ }
+
+ return 0;
+}
+
+static const struct mcp23s08_ops mcp23008_ops = {
+ .read = mcp23008_read,
+ .write = mcp23008_write,
+ .read_regs = mcp23008_read_regs,
+};
+
+static const struct mcp23s08_ops mcp23017_ops = {
+ .read = mcp23017_read,
+ .write = mcp23017_write,
+ .read_regs = mcp23017_read_regs,
+};
+
+#endif /* CONFIG_I2C */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_SPI_MASTER
+
+static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
+{
+ u8 tx[2], rx[1];
+ int status;
+
+ tx[0] = mcp->addr | 0x01;
+ tx[1] = reg;
+ status = spi_write_then_read(mcp->data, tx, sizeof(tx), rx, sizeof(rx));
+ return (status < 0) ? status : rx[0];
+}
+
+static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
+{
+ u8 tx[3];
+
+ tx[0] = mcp->addr;
+ tx[1] = reg;
+ tx[2] = val;
+ return spi_write_then_read(mcp->data, tx, sizeof(tx), NULL, 0);
+}
+
+static int
+mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
+{
+ u8 tx[2], *tmp;
+ int status;
+
+ if ((n + reg) > sizeof(mcp->cache))
+ return -EINVAL;
+ tx[0] = mcp->addr | 0x01;
+ tx[1] = reg;
+
+ tmp = (u8 *)vals;
+ status = spi_write_then_read(mcp->data, tx, sizeof(tx), tmp, n);
+ if (status >= 0) {
+ while (n--)
+ vals[n] = tmp[n]; /* expand to 16bit */
+ }
+ return status;
+}
+
+static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg)
+{
+ u8 tx[2], rx[2];
+ int status;
+
+ tx[0] = mcp->addr | 0x01;
+ tx[1] = reg << 1;
+ status = spi_write_then_read(mcp->data, tx, sizeof(tx), rx, sizeof(rx));
+ return (status < 0) ? status : (rx[0] | (rx[1] << 8));
+}
+
+static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
+{
+ u8 tx[4];
+
+ tx[0] = mcp->addr;
+ tx[1] = reg << 1;
+ tx[2] = val;
+ tx[3] = val >> 8;
+ return spi_write_then_read(mcp->data, tx, sizeof(tx), NULL, 0);
+}
+
+static int
+mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
+{
+ u8 tx[2];
+ int status;
+
+ if ((n + reg) > sizeof(mcp->cache))
+ return -EINVAL;
+ tx[0] = mcp->addr | 0x01;
+ tx[1] = reg << 1;
+
+ status = spi_write_then_read(mcp->data, tx, sizeof(tx),
+ (u8 *)vals, n * 2);
+ if (status >= 0) {
+ while (n--)
+ vals[n] = __le16_to_cpu((__le16)vals[n]);
+ }
+
+ return status;
+}
+
+static const struct mcp23s08_ops mcp23s08_ops = {
+ .read = mcp23s08_read,
+ .write = mcp23s08_write,
+ .read_regs = mcp23s08_read_regs,
+};
+
+static const struct mcp23s08_ops mcp23s17_ops = {
+ .read = mcp23s17_read,
+ .write = mcp23s17_write,
+ .read_regs = mcp23s17_read_regs,
+};
+
+#endif /* CONFIG_SPI_MASTER */
+
+/*----------------------------------------------------------------------*/
+
+static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+ int status;
+
+ mutex_lock(&mcp->lock);
+ mcp->cache[MCP_IODIR] |= (1 << offset);
+ status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
+ mutex_unlock(&mcp->lock);
+ return status;
+}
+
+static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+ int status;
+
+ mutex_lock(&mcp->lock);
+
+ /* REVISIT reading this clears any IRQ ... */
+ status = mcp->ops->read(mcp, MCP_GPIO);
+ if (status < 0)
+ status = 0;
+ else {
+ mcp->cache[MCP_GPIO] = status;
+ status = !!(status & (1 << offset));
+ }
+ mutex_unlock(&mcp->lock);
+ return status;
+}
+
+static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value)
+{
+ unsigned olat = mcp->cache[MCP_OLAT];
+
+ if (value)
+ olat |= mask;
+ else
+ olat &= ~mask;
+ mcp->cache[MCP_OLAT] = olat;
+ return mcp->ops->write(mcp, MCP_OLAT, olat);
+}
+
+static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+ unsigned mask = 1 << offset;
+
+ mutex_lock(&mcp->lock);
+ __mcp23s08_set(mcp, mask, value);
+ mutex_unlock(&mcp->lock);
+}
+
+static int
+mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+ unsigned mask = 1 << offset;
+ int status;
+
+ mutex_lock(&mcp->lock);
+ status = __mcp23s08_set(mcp, mask, value);
+ if (status == 0) {
+ mcp->cache[MCP_IODIR] &= ~mask;
+ status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
+ }
+ mutex_unlock(&mcp->lock);
+ return status;
+}
+
+/*----------------------------------------------------------------------*/
+static irqreturn_t mcp23s08_irq(int irq, void *data)
+{
+ struct mcp23s08 *mcp = data;
+ int intcap, intf, i;
+ unsigned int child_irq;
+
+ mutex_lock(&mcp->lock);
+ intf = mcp->ops->read(mcp, MCP_INTF);
+ if (intf < 0) {
+ mutex_unlock(&mcp->lock);
+ return IRQ_HANDLED;
+ }
+
+ mcp->cache[MCP_INTF] = intf;
+
+ intcap = mcp->ops->read(mcp, MCP_INTCAP);
+ if (intcap < 0) {
+ mutex_unlock(&mcp->lock);
+ return IRQ_HANDLED;
+ }
+
+ mcp->cache[MCP_INTCAP] = intcap;
+ mutex_unlock(&mcp->lock);
+
+
+ for (i = 0; i < mcp->chip.ngpio; i++) {
+ if ((BIT(i) & mcp->cache[MCP_INTF]) &&
+ ((BIT(i) & intcap & mcp->irq_rise) ||
+ (mcp->irq_fall & ~intcap & BIT(i)))) {
+ child_irq = irq_find_mapping(mcp->irq_domain, i);
+ handle_nested_irq(child_irq);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mcp23s08_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+
+ return irq_find_mapping(mcp->irq_domain, offset);
+}
+
+static void mcp23s08_irq_mask(struct irq_data *data)
+{
+ struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
+ unsigned int pos = data->hwirq;
+
+ mcp->cache[MCP_GPINTEN] &= ~BIT(pos);
+}
+
+static void mcp23s08_irq_unmask(struct irq_data *data)
+{
+ struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
+ unsigned int pos = data->hwirq;
+
+ mcp->cache[MCP_GPINTEN] |= BIT(pos);
+}
+
+static int mcp23s08_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
+ unsigned int pos = data->hwirq;
+ int status = 0;
+
+ if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
+ mcp->cache[MCP_INTCON] &= ~BIT(pos);
+ mcp->irq_rise |= BIT(pos);
+ mcp->irq_fall |= BIT(pos);
+ } else if (type & IRQ_TYPE_EDGE_RISING) {
+ mcp->cache[MCP_INTCON] &= ~BIT(pos);
+ mcp->irq_rise |= BIT(pos);
+ mcp->irq_fall &= ~BIT(pos);
+ } else if (type & IRQ_TYPE_EDGE_FALLING) {
+ mcp->cache[MCP_INTCON] &= ~BIT(pos);
+ mcp->irq_rise &= ~BIT(pos);
+ mcp->irq_fall |= BIT(pos);
+ } else
+ return -EINVAL;
+
+ return status;
+}
+
+static void mcp23s08_irq_bus_lock(struct irq_data *data)
+{
+ struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&mcp->irq_lock);
+}
+
+static void mcp23s08_irq_bus_unlock(struct irq_data *data)
+{
+ struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&mcp->lock);
+ mcp->ops->write(mcp, MCP_GPINTEN, mcp->cache[MCP_GPINTEN]);
+ mcp->ops->write(mcp, MCP_DEFVAL, mcp->cache[MCP_DEFVAL]);
+ mcp->ops->write(mcp, MCP_INTCON, mcp->cache[MCP_INTCON]);
+ mutex_unlock(&mcp->lock);
+ mutex_unlock(&mcp->irq_lock);
+}
+
+static int mcp23s08_irq_reqres(struct irq_data *data)
+{
+ struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
+
+ if (gpiochip_lock_as_irq(&mcp->chip, data->hwirq)) {
+ dev_err(mcp->chip.dev,
+ "unable to lock HW IRQ %lu for IRQ usage\n",
+ data->hwirq);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void mcp23s08_irq_relres(struct irq_data *data)
+{
+ struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
+
+ gpiochip_unlock_as_irq(&mcp->chip, data->hwirq);
+}
+
+static struct irq_chip mcp23s08_irq_chip = {
+ .name = "gpio-mcp23xxx",
+ .irq_mask = mcp23s08_irq_mask,
+ .irq_unmask = mcp23s08_irq_unmask,
+ .irq_set_type = mcp23s08_irq_set_type,
+ .irq_bus_lock = mcp23s08_irq_bus_lock,
+ .irq_bus_sync_unlock = mcp23s08_irq_bus_unlock,
+ .irq_request_resources = mcp23s08_irq_reqres,
+ .irq_release_resources = mcp23s08_irq_relres,
+};
+
+static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
+{
+ struct gpio_chip *chip = &mcp->chip;
+ int err, irq, j;
+ unsigned long irqflags = IRQF_ONESHOT | IRQF_SHARED;
+
+ mutex_init(&mcp->irq_lock);
+
+ mcp->irq_domain = irq_domain_add_linear(chip->dev->of_node, chip->ngpio,
+ &irq_domain_simple_ops, mcp);
+ if (!mcp->irq_domain)
+ return -ENODEV;
+
+ if (mcp->irq_active_high)
+ irqflags |= IRQF_TRIGGER_HIGH;
+ else
+ irqflags |= IRQF_TRIGGER_LOW;
+
+ err = devm_request_threaded_irq(chip->dev, mcp->irq, NULL, mcp23s08_irq,
+ irqflags, dev_name(chip->dev), mcp);
+ if (err != 0) {
+ dev_err(chip->dev, "unable to request IRQ#%d: %d\n",
+ mcp->irq, err);
+ return err;
+ }
+
+ chip->to_irq = mcp23s08_gpio_to_irq;
+
+ for (j = 0; j < mcp->chip.ngpio; j++) {
+ irq = irq_create_mapping(mcp->irq_domain, j);
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_data(irq, mcp);
+ irq_set_chip(irq, &mcp23s08_irq_chip);
+ irq_set_nested_thread(irq, true);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ }
+ return 0;
+}
+
+static void mcp23s08_irq_teardown(struct mcp23s08 *mcp)
+{
+ unsigned int irq, i;
+
+ for (i = 0; i < mcp->chip.ngpio; i++) {
+ irq = irq_find_mapping(mcp->irq_domain, i);
+ if (irq > 0)
+ irq_dispose_mapping(irq);
+ }
+
+ irq_domain_remove(mcp->irq_domain);
+}
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/seq_file.h>
+
+/*
+ * This shows more info than the generic gpio dump code:
+ * pullups, deglitching, open drain drive.
+ */
+static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct mcp23s08 *mcp;
+ char bank;
+ int t;
+ unsigned mask;
+
+ mcp = container_of(chip, struct mcp23s08, chip);
+
+ /* NOTE: we only handle one bank for now ... */
+ bank = '0' + ((mcp->addr >> 1) & 0x7);
+
+ mutex_lock(&mcp->lock);
+ t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
+ if (t < 0) {
+ seq_printf(s, " I/O ERROR %d\n", t);
+ goto done;
+ }
+
+ for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) {
+ const char *label;
+
+ label = gpiochip_is_requested(chip, t);
+ if (!label)
+ continue;
+
+ seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s",
+ chip->base + t, bank, t, label,
+ (mcp->cache[MCP_IODIR] & mask) ? "in " : "out",
+ (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo",
+ (mcp->cache[MCP_GPPU] & mask) ? "up" : " ");
+ /* NOTE: ignoring the irq-related registers */
+ seq_puts(s, "\n");
+ }
+done:
+ mutex_unlock(&mcp->lock);
+}
+
+#else
+#define mcp23s08_dbg_show NULL
+#endif
+
+/*----------------------------------------------------------------------*/
+
+static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
+ void *data, unsigned addr, unsigned type,
+ struct mcp23s08_platform_data *pdata, int cs)
+{
+ int status;
+ bool mirror = false;
+
+ mutex_init(&mcp->lock);
+
+ mcp->data = data;
+ mcp->addr = addr;
+ mcp->irq_active_high = false;
+
+ mcp->chip.direction_input = mcp23s08_direction_input;
+ mcp->chip.get = mcp23s08_get;
+ mcp->chip.direction_output = mcp23s08_direction_output;
+ mcp->chip.set = mcp23s08_set;
+ mcp->chip.dbg_show = mcp23s08_dbg_show;
+#ifdef CONFIG_OF
+ mcp->chip.of_gpio_n_cells = 2;
+ mcp->chip.of_node = dev->of_node;
+#endif
+
+ switch (type) {
+#ifdef CONFIG_SPI_MASTER
+ case MCP_TYPE_S08:
+ mcp->ops = &mcp23s08_ops;
+ mcp->chip.ngpio = 8;
+ mcp->chip.label = "mcp23s08";
+ break;
+
+ case MCP_TYPE_S17:
+ mcp->ops = &mcp23s17_ops;
+ mcp->chip.ngpio = 16;
+ mcp->chip.label = "mcp23s17";
+ break;
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+ case MCP_TYPE_008:
+ mcp->ops = &mcp23008_ops;
+ mcp->chip.ngpio = 8;
+ mcp->chip.label = "mcp23008";
+ break;
+
+ case MCP_TYPE_017:
+ mcp->ops = &mcp23017_ops;
+ mcp->chip.ngpio = 16;
+ mcp->chip.label = "mcp23017";
+ break;
+#endif /* CONFIG_I2C */
+
+ default:
+ dev_err(dev, "invalid device type (%d)\n", type);
+ return -EINVAL;
+ }
+
+ mcp->chip.base = pdata->base;
+ mcp->chip.can_sleep = true;
+ mcp->chip.dev = dev;
+ mcp->chip.owner = THIS_MODULE;
+
+ /* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
+ * and MCP_IOCON.HAEN = 1, so we work with all chips.
+ */
+
+ status = mcp->ops->read(mcp, MCP_IOCON);
+ if (status < 0)
+ goto fail;
+
+ mcp->irq_controller = pdata->irq_controller;
+ if (mcp->irq && mcp->irq_controller) {
+ mcp->irq_active_high =
+ of_property_read_bool(mcp->chip.dev->of_node,
+ "microchip,irq-active-high");
+
+ if (type == MCP_TYPE_017)
+ mirror = pdata->mirror;
+ }
+
+ if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror ||
+ mcp->irq_active_high) {
+ /* mcp23s17 has IOCON twice, make sure they are in sync */
+ status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8));
+ status |= IOCON_HAEN | (IOCON_HAEN << 8);
+ if (mcp->irq_active_high)
+ status |= IOCON_INTPOL | (IOCON_INTPOL << 8);
+ else
+ status &= ~(IOCON_INTPOL | (IOCON_INTPOL << 8));
+
+ if (mirror)
+ status |= IOCON_MIRROR | (IOCON_MIRROR << 8);
+
+ status = mcp->ops->write(mcp, MCP_IOCON, status);
+ if (status < 0)
+ goto fail;
+ }
+
+ /* configure ~100K pullups */
+ status = mcp->ops->write(mcp, MCP_GPPU, pdata->chip[cs].pullups);
+ if (status < 0)
+ goto fail;
+
+ status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
+ if (status < 0)
+ goto fail;
+
+ /* disable inverter on input */
+ if (mcp->cache[MCP_IPOL] != 0) {
+ mcp->cache[MCP_IPOL] = 0;
+ status = mcp->ops->write(mcp, MCP_IPOL, 0);
+ if (status < 0)
+ goto fail;
+ }
+
+ /* disable irqs */
+ if (mcp->cache[MCP_GPINTEN] != 0) {
+ mcp->cache[MCP_GPINTEN] = 0;
+ status = mcp->ops->write(mcp, MCP_GPINTEN, 0);
+ if (status < 0)
+ goto fail;
+ }
+
+ status = gpiochip_add(&mcp->chip);
+ if (status < 0)
+ goto fail;
+
+ if (mcp->irq && mcp->irq_controller) {
+ status = mcp23s08_irq_setup(mcp);
+ if (status) {
+ mcp23s08_irq_teardown(mcp);
+ goto fail;
+ }
+ }
+fail:
+ if (status < 0)
+ dev_dbg(dev, "can't setup chip %d, --> %d\n",
+ addr, status);
+ return status;
+}
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_OF
+#ifdef CONFIG_SPI_MASTER
+static const struct of_device_id mcp23s08_spi_of_match[] = {
+ {
+ .compatible = "microchip,mcp23s08",
+ .data = (void *) MCP_TYPE_S08,
+ },
+ {
+ .compatible = "microchip,mcp23s17",
+ .data = (void *) MCP_TYPE_S17,
+ },
+/* NOTE: The use of the mcp prefix is deprecated and will be removed. */
+ {
+ .compatible = "mcp,mcp23s08",
+ .data = (void *) MCP_TYPE_S08,
+ },
+ {
+ .compatible = "mcp,mcp23s17",
+ .data = (void *) MCP_TYPE_S17,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match);
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+static const struct of_device_id mcp23s08_i2c_of_match[] = {
+ {
+ .compatible = "microchip,mcp23008",
+ .data = (void *) MCP_TYPE_008,
+ },
+ {
+ .compatible = "microchip,mcp23017",
+ .data = (void *) MCP_TYPE_017,
+ },
+/* NOTE: The use of the mcp prefix is deprecated and will be removed. */
+ {
+ .compatible = "mcp,mcp23008",
+ .data = (void *) MCP_TYPE_008,
+ },
+ {
+ .compatible = "mcp,mcp23017",
+ .data = (void *) MCP_TYPE_017,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match);
+#endif
+#endif /* CONFIG_OF */
+
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int mcp230xx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mcp23s08_platform_data *pdata, local_pdata;
+ struct mcp23s08 *mcp;
+ int status;
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match),
+ &client->dev);
+ if (match) {
+ pdata = &local_pdata;
+ pdata->base = -1;
+ pdata->chip[0].pullups = 0;
+ pdata->irq_controller = of_property_read_bool(
+ client->dev.of_node,
+ "interrupt-controller");
+ pdata->mirror = of_property_read_bool(client->dev.of_node,
+ "microchip,irq-mirror");
+ client->irq = irq_of_parse_and_map(client->dev.of_node, 0);
+ } else {
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct mcp23s08_platform_data),
+ GFP_KERNEL);
+ pdata->base = -1;
+ }
+ }
+
+ mcp = kzalloc(sizeof(*mcp), GFP_KERNEL);
+ if (!mcp)
+ return -ENOMEM;
+
+ mcp->irq = client->irq;
+ status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
+ id->driver_data, pdata, 0);
+ if (status)
+ goto fail;
+
+ i2c_set_clientdata(client, mcp);
+
+ return 0;
+
+fail:
+ kfree(mcp);
+
+ return status;
+}
+
+static int mcp230xx_remove(struct i2c_client *client)
+{
+ struct mcp23s08 *mcp = i2c_get_clientdata(client);
+
+ if (client->irq && mcp->irq_controller)
+ mcp23s08_irq_teardown(mcp);
+
+ gpiochip_remove(&mcp->chip);
+ kfree(mcp);
+
+ return 0;
+}
+
+static const struct i2c_device_id mcp230xx_id[] = {
+ { "mcp23008", MCP_TYPE_008 },
+ { "mcp23017", MCP_TYPE_017 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, mcp230xx_id);
+
+static struct i2c_driver mcp230xx_driver = {
+ .driver = {
+ .name = "mcp230xx",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mcp23s08_i2c_of_match),
+ },
+ .probe = mcp230xx_probe,
+ .remove = mcp230xx_remove,
+ .id_table = mcp230xx_id,
+};
+
+static int __init mcp23s08_i2c_init(void)
+{
+ return i2c_add_driver(&mcp230xx_driver);
+}
+
+static void mcp23s08_i2c_exit(void)
+{
+ i2c_del_driver(&mcp230xx_driver);
+}
+
+#else
+
+static int __init mcp23s08_i2c_init(void) { return 0; }
+static void mcp23s08_i2c_exit(void) { }
+
+#endif /* CONFIG_I2C */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_SPI_MASTER
+
+static int mcp23s08_probe(struct spi_device *spi)
+{
+ struct mcp23s08_platform_data *pdata, local_pdata;
+ unsigned addr;
+ int chips = 0;
+ struct mcp23s08_driver_data *data;
+ int status, type;
+ unsigned ngpio = 0;
+ const struct of_device_id *match;
+ u32 spi_present_mask = 0;
+
+ match = of_match_device(of_match_ptr(mcp23s08_spi_of_match), &spi->dev);
+ if (match) {
+ type = (int)(uintptr_t)match->data;
+ status = of_property_read_u32(spi->dev.of_node,
+ "microchip,spi-present-mask", &spi_present_mask);
+ if (status) {
+ status = of_property_read_u32(spi->dev.of_node,
+ "mcp,spi-present-mask", &spi_present_mask);
+ if (status) {
+ dev_err(&spi->dev,
+ "DT has no spi-present-mask\n");
+ return -ENODEV;
+ }
+ }
+ if ((spi_present_mask <= 0) || (spi_present_mask >= 256)) {
+ dev_err(&spi->dev, "invalid spi-present-mask\n");
+ return -ENODEV;
+ }
+
+ pdata = &local_pdata;
+ pdata->base = -1;
+ for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
+ pdata->chip[addr].pullups = 0;
+ if (spi_present_mask & (1 << addr))
+ chips++;
+ }
+ pdata->irq_controller = of_property_read_bool(
+ spi->dev.of_node,
+ "interrupt-controller");
+ pdata->mirror = of_property_read_bool(spi->dev.of_node,
+ "microchip,irq-mirror");
+ } else {
+ type = spi_get_device_id(spi)->driver_data;
+ pdata = dev_get_platdata(&spi->dev);
+ if (!pdata) {
+ pdata = devm_kzalloc(&spi->dev,
+ sizeof(struct mcp23s08_platform_data),
+ GFP_KERNEL);
+ pdata->base = -1;
+ }
+
+ for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
+ if (!pdata->chip[addr].is_present)
+ continue;
+ chips++;
+ if ((type == MCP_TYPE_S08) && (addr > 3)) {
+ dev_err(&spi->dev,
+ "mcp23s08 only supports address 0..3\n");
+ return -EINVAL;
+ }
+ spi_present_mask |= 1 << addr;
+ }
+ }
+
+ if (!chips)
+ return -ENODEV;
+
+ data = devm_kzalloc(&spi->dev,
+ sizeof(*data) + chips * sizeof(struct mcp23s08),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, data);
+
+ spi->irq = irq_of_parse_and_map(spi->dev.of_node, 0);
+
+ for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
+ if (!(spi_present_mask & (1 << addr)))
+ continue;
+ chips--;
+ data->mcp[addr] = &data->chip[chips];
+ data->mcp[addr]->irq = spi->irq;
+ status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
+ 0x40 | (addr << 1), type, pdata,
+ addr);
+ if (status < 0)
+ goto fail;
+
+ if (pdata->base != -1)
+ pdata->base += (type == MCP_TYPE_S17) ? 16 : 8;
+ ngpio += (type == MCP_TYPE_S17) ? 16 : 8;
+ }
+ data->ngpio = ngpio;
+
+ /* NOTE: these chips have a relatively sane IRQ framework, with
+ * per-signal masking and level/edge triggering. It's not yet
+ * handled here...
+ */
+
+ return 0;
+
+fail:
+ for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
+
+ if (!data->mcp[addr])
+ continue;
+ gpiochip_remove(&data->mcp[addr]->chip);
+ }
+ return status;
+}
+
+static int mcp23s08_remove(struct spi_device *spi)
+{
+ struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
+ unsigned addr;
+
+ for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
+
+ if (!data->mcp[addr])
+ continue;
+
+ if (spi->irq && data->mcp[addr]->irq_controller)
+ mcp23s08_irq_teardown(data->mcp[addr]);
+ gpiochip_remove(&data->mcp[addr]->chip);
+ }
+
+ return 0;
+}
+
+static const struct spi_device_id mcp23s08_ids[] = {
+ { "mcp23s08", MCP_TYPE_S08 },
+ { "mcp23s17", MCP_TYPE_S17 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
+
+static struct spi_driver mcp23s08_driver = {
+ .probe = mcp23s08_probe,
+ .remove = mcp23s08_remove,
+ .id_table = mcp23s08_ids,
+ .driver = {
+ .name = "mcp23s08",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mcp23s08_spi_of_match),
+ },
+};
+
+static int __init mcp23s08_spi_init(void)
+{
+ return spi_register_driver(&mcp23s08_driver);
+}
+
+static void mcp23s08_spi_exit(void)
+{
+ spi_unregister_driver(&mcp23s08_driver);
+}
+
+#else
+
+static int __init mcp23s08_spi_init(void) { return 0; }
+static void mcp23s08_spi_exit(void) { }
+
+#endif /* CONFIG_SPI_MASTER */
+
+/*----------------------------------------------------------------------*/
+
+static int __init mcp23s08_init(void)
+{
+ int ret;
+
+ ret = mcp23s08_spi_init();
+ if (ret)
+ goto spi_fail;
+
+ ret = mcp23s08_i2c_init();
+ if (ret)
+ goto i2c_fail;
+
+ return 0;
+
+ i2c_fail:
+ mcp23s08_spi_exit();
+ spi_fail:
+ return ret;
+}
+/* register after spi/i2c postcore initcall and before
+ * subsys initcalls that may rely on these GPIOs
+ */
+subsys_initcall(mcp23s08_init);
+
+static void __exit mcp23s08_exit(void)
+{
+ mcp23s08_spi_exit();
+ mcp23s08_i2c_exit();
+}
+module_exit(mcp23s08_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c
new file mode 100644
index 000000000..5536108aa
--- /dev/null
+++ b/drivers/gpio/gpio-ml-ioh.c
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#define IOH_EDGE_FALLING 0
+#define IOH_EDGE_RISING BIT(0)
+#define IOH_LEVEL_L BIT(1)
+#define IOH_LEVEL_H (BIT(0) | BIT(1))
+#define IOH_EDGE_BOTH BIT(2)
+#define IOH_IM_MASK (BIT(0) | BIT(1) | BIT(2))
+
+#define IOH_IRQ_BASE 0
+
+#define PCI_VENDOR_ID_ROHM 0x10DB
+
+struct ioh_reg_comn {
+ u32 ien;
+ u32 istatus;
+ u32 idisp;
+ u32 iclr;
+ u32 imask;
+ u32 imaskclr;
+ u32 po;
+ u32 pi;
+ u32 pm;
+ u32 im_0;
+ u32 im_1;
+ u32 reserved;
+};
+
+struct ioh_regs {
+ struct ioh_reg_comn regs[8];
+ u32 reserve1[16];
+ u32 ioh_sel_reg[4];
+ u32 reserve2[11];
+ u32 srst;
+};
+
+/**
+ * struct ioh_gpio_reg_data - The register store data.
+ * @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.
+ * @im0_reg: To store contents of interrupt mode regist0
+ * @im1_reg: To store contents of interrupt mode regist1
+ * @use_sel_reg: To store contents of GPIO_USE_SEL0~3
+ */
+struct ioh_gpio_reg_data {
+ u32 ien_reg;
+ u32 imask_reg;
+ u32 po_reg;
+ u32 pm_reg;
+ u32 im0_reg;
+ u32 im1_reg;
+ u32 use_sel_reg;
+};
+
+/**
+ * struct ioh_gpio - GPIO private data structure.
+ * @base: PCI base address of Memory mapped I/O register.
+ * @reg: Memory mapped IOH GPIO register list.
+ * @dev: Pointer to device structure.
+ * @gpio: Data for GPIO infrastructure.
+ * @ioh_gpio_reg: Memory mapped Register data is saved here
+ * when suspend.
+ * @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM
+ * @ch: Indicate GPIO channel
+ * @irq_base: Save base of IRQ number for interrupt
+ * @spinlock: Used for register access protection
+ */
+struct ioh_gpio {
+ void __iomem *base;
+ struct ioh_regs __iomem *reg;
+ struct device *dev;
+ struct gpio_chip gpio;
+ struct ioh_gpio_reg_data ioh_gpio_reg;
+ u32 gpio_use_sel;
+ int ch;
+ int irq_base;
+ spinlock_t spinlock;
+};
+
+static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12};
+
+static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
+{
+ u32 reg_val;
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ reg_val = ioread32(&chip->reg->regs[chip->ch].po);
+ if (val)
+ reg_val |= (1 << nr);
+ else
+ reg_val &= ~(1 << nr);
+
+ iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+
+ return ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr);
+}
+
+static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+ int val)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ u32 pm;
+ u32 reg_val;
+ 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);
+ iowrite32(pm, &chip->reg->regs[chip->ch].pm);
+
+ reg_val = ioread32(&chip->reg->regs[chip->ch].po);
+ if (val)
+ reg_val |= (1 << nr);
+ else
+ reg_val &= ~(1 << nr);
+ iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
+
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ u32 pm;
+ 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);
+ 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)
+{
+ int i;
+
+ for (i = 0; i < 8; i ++, chip++) {
+ chip->ioh_gpio_reg.po_reg =
+ ioread32(&chip->reg->regs[chip->ch].po);
+ chip->ioh_gpio_reg.pm_reg =
+ ioread32(&chip->reg->regs[chip->ch].pm);
+ chip->ioh_gpio_reg.ien_reg =
+ ioread32(&chip->reg->regs[chip->ch].ien);
+ chip->ioh_gpio_reg.imask_reg =
+ ioread32(&chip->reg->regs[chip->ch].imask);
+ chip->ioh_gpio_reg.im0_reg =
+ ioread32(&chip->reg->regs[chip->ch].im_0);
+ chip->ioh_gpio_reg.im1_reg =
+ ioread32(&chip->reg->regs[chip->ch].im_1);
+ if (i < 4)
+ chip->ioh_gpio_reg.use_sel_reg =
+ ioread32(&chip->reg->ioh_sel_reg[i]);
+ }
+}
+
+/*
+ * This function restores the register configuration of the GPIO device.
+ */
+static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
+{
+ int i;
+
+ for (i = 0; i < 8; i ++, chip++) {
+ iowrite32(chip->ioh_gpio_reg.po_reg,
+ &chip->reg->regs[chip->ch].po);
+ iowrite32(chip->ioh_gpio_reg.pm_reg,
+ &chip->reg->regs[chip->ch].pm);
+ iowrite32(chip->ioh_gpio_reg.ien_reg,
+ &chip->reg->regs[chip->ch].ien);
+ iowrite32(chip->ioh_gpio_reg.imask_reg,
+ &chip->reg->regs[chip->ch].imask);
+ iowrite32(chip->ioh_gpio_reg.im0_reg,
+ &chip->reg->regs[chip->ch].im_0);
+ iowrite32(chip->ioh_gpio_reg.im1_reg,
+ &chip->reg->regs[chip->ch].im_1);
+ if (i < 4)
+ iowrite32(chip->ioh_gpio_reg.use_sel_reg,
+ &chip->reg->ioh_sel_reg[i]);
+ }
+}
+#endif
+
+static int ioh_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ return chip->irq_base + offset;
+}
+
+static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port)
+{
+ struct gpio_chip *gpio = &chip->gpio;
+
+ gpio->label = dev_name(chip->dev);
+ gpio->owner = THIS_MODULE;
+ gpio->direction_input = ioh_gpio_direction_input;
+ gpio->get = ioh_gpio_get;
+ gpio->direction_output = ioh_gpio_direction_output;
+ gpio->set = ioh_gpio_set;
+ gpio->dbg_show = NULL;
+ gpio->base = -1;
+ gpio->ngpio = num_port;
+ gpio->can_sleep = false;
+ gpio->to_irq = ioh_gpio_to_irq;
+}
+
+static int ioh_irq_type(struct irq_data *d, unsigned int type)
+{
+ u32 im;
+ void __iomem *im_reg;
+ u32 ien;
+ u32 im_pos;
+ int ch;
+ unsigned long flags;
+ u32 val;
+ int irq = d->irq;
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+
+ ch = irq - chip->irq_base;
+ if (irq <= chip->irq_base + 7) {
+ im_reg = &chip->reg->regs[chip->ch].im_0;
+ im_pos = ch;
+ } else {
+ im_reg = &chip->reg->regs[chip->ch].im_1;
+ im_pos = ch - 8;
+ }
+ dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n",
+ __func__, irq, type, ch, im_pos, type);
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ val = IOH_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ val = IOH_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = IOH_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val = IOH_LEVEL_H;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val = IOH_LEVEL_L;
+ break;
+ case IRQ_TYPE_PROBE:
+ goto end;
+ default:
+ dev_warn(chip->dev, "%s: unknown type(%dd)",
+ __func__, type);
+ goto end;
+ }
+
+ /* Set interrupt mode */
+ im = ioread32(im_reg) & ~(IOH_IM_MASK << (im_pos * 4));
+ iowrite32(im | (val << (im_pos * 4)), im_reg);
+
+ /* iclr */
+ iowrite32(BIT(ch), &chip->reg->regs[chip->ch].iclr);
+
+ /* IMASKCLR */
+ iowrite32(BIT(ch), &chip->reg->regs[chip->ch].imaskclr);
+
+ /* Enable interrupt */
+ ien = ioread32(&chip->reg->regs[chip->ch].ien);
+ iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien);
+end:
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+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),
+ &chip->reg->regs[chip->ch].imaskclr);
+}
+
+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),
+ &chip->reg->regs[chip->ch].imask);
+}
+
+static void ioh_irq_disable(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+ unsigned long flags;
+ u32 ien;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ ien = ioread32(&chip->reg->regs[chip->ch].ien);
+ ien &= ~(1 << (d->irq - chip->irq_base));
+ iowrite32(ien, &chip->reg->regs[chip->ch].ien);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static void ioh_irq_enable(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+ unsigned long flags;
+ u32 ien;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ ien = ioread32(&chip->reg->regs[chip->ch].ien);
+ ien |= 1 << (d->irq - chip->irq_base);
+ iowrite32(ien, &chip->reg->regs[chip->ch].ien);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static irqreturn_t ioh_gpio_handler(int irq, void *dev_id)
+{
+ struct ioh_gpio *chip = dev_id;
+ u32 reg_val;
+ int i, j;
+ int ret = IRQ_NONE;
+
+ for (i = 0; i < 8; i++, chip++) {
+ reg_val = ioread32(&chip->reg->regs[i].istatus);
+ for (j = 0; j < num_ports[i]; j++) {
+ if (reg_val & BIT(j)) {
+ dev_dbg(chip->dev,
+ "%s:[%d]:irq=%d status=0x%x\n",
+ __func__, j, irq, reg_val);
+ iowrite32(BIT(j),
+ &chip->reg->regs[chip->ch].iclr);
+ generic_handle_irq(chip->irq_base + j);
+ ret = IRQ_HANDLED;
+ }
+ }
+ }
+ return ret;
+}
+
+static void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
+ unsigned int irq_start, unsigned int num)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base,
+ handle_simple_irq);
+ gc->private = chip;
+ ct = gc->chip_types;
+
+ ct->chip.irq_mask = ioh_irq_mask;
+ ct->chip.irq_unmask = ioh_irq_unmask;
+ ct->chip.irq_set_type = ioh_irq_type;
+ ct->chip.irq_disable = ioh_irq_disable;
+ ct->chip.irq_enable = ioh_irq_enable;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST | IRQ_NOPROBE, 0);
+}
+
+static int ioh_gpio_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int ret;
+ int i, j;
+ struct ioh_gpio *chip;
+ void __iomem *base;
+ void *chip_save;
+ int irq_base;
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "%s : pci_enable_device failed", __func__);
+ goto err_pci_enable;
+ }
+
+ ret = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (ret) {
+ dev_err(&pdev->dev, "pci_request_regions failed-%d", ret);
+ goto err_request_regions;
+ }
+
+ base = pci_iomap(pdev, 1, 0);
+ if (!base) {
+ dev_err(&pdev->dev, "%s : pci_iomap failed", __func__);
+ ret = -ENOMEM;
+ goto err_iomap;
+ }
+
+ chip_save = kzalloc(sizeof(*chip) * 8, GFP_KERNEL);
+ if (chip_save == NULL) {
+ dev_err(&pdev->dev, "%s : kzalloc failed", __func__);
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ chip = chip_save;
+ for (i = 0; i < 8; i++, chip++) {
+ chip->dev = &pdev->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(&chip->gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "IOH gpio: Failed to register GPIO\n");
+ goto err_gpiochip_add;
+ }
+ }
+
+ chip = chip_save;
+ for (j = 0; j < 8; j++, chip++) {
+ irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j],
+ NUMA_NO_NODE);
+ if (irq_base < 0) {
+ dev_warn(&pdev->dev,
+ "ml_ioh_gpio: Failed to get IRQ base num\n");
+ chip->irq_base = -1;
+ ret = irq_base;
+ goto err_irq_alloc_descs;
+ }
+ chip->irq_base = irq_base;
+ ioh_gpio_alloc_generic_chip(chip, irq_base, num_ports[j]);
+ }
+
+ chip = chip_save;
+ ret = request_irq(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_request_irq;
+ }
+
+ pci_set_drvdata(pdev, chip);
+
+ return 0;
+
+err_request_irq:
+ chip = chip_save;
+err_irq_alloc_descs:
+ while (--j >= 0) {
+ chip--;
+ irq_free_descs(chip->irq_base, num_ports[j]);
+ }
+
+ chip = chip_save;
+err_gpiochip_add:
+ while (--i >= 0) {
+ chip--;
+ gpiochip_remove(&chip->gpio);
+ }
+ 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)
+{
+ int i;
+ struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ void *chip_save;
+
+ chip_save = chip;
+
+ free_irq(pdev->irq, chip);
+
+ for (i = 0; i < 8; i++, chip++) {
+ irq_free_descs(chip->irq_base, num_ports[i]);
+ 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);
+ 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)
+{
+ s32 ret;
+ struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ 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);
+ ioh_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+#else
+#define ioh_gpio_suspend NULL
+#define ioh_gpio_resume NULL
+#endif
+
+static const struct pci_device_id ioh_gpio_pcidev_id[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, ioh_gpio_pcidev_id);
+
+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
+};
+
+module_pci_driver(ioh_gpio_driver);
+
+MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML-IOH series GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c
new file mode 100644
index 000000000..f67ef2283
--- /dev/null
+++ b/drivers/gpio/gpio-mm-lantiq.c
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <lantiq_soc.h>
+
+/*
+ * By attaching hardware latches to the EBU it is possible to create output
+ * only gpios. This driver configures a special memory address, which when
+ * written to outputs 16 bit to the latches.
+ */
+
+#define LTQ_EBU_BUSCON 0x1e7ff /* 16 bit access, slowest timing */
+#define LTQ_EBU_WP 0x80000000 /* write protect bit */
+
+struct ltq_mm {
+ struct of_mm_gpio_chip mmchip;
+ u16 shadow; /* shadow the latches state */
+};
+
+/**
+ * ltq_mm_apply() - write the shadow value to the ebu address.
+ * @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.
+ */
+static void ltq_mm_apply(struct ltq_mm *chip)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ebu_lock, flags);
+ ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1);
+ __raw_writew(chip->shadow, chip->mmchip.regs);
+ ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1);
+ spin_unlock_irqrestore(&ebu_lock, flags);
+}
+
+/**
+ * ltq_mm_set() - gpio_chip->set - set gpios.
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ * @val: Value to be written to specified signal.
+ *
+ * Set the shadow value and call ltq_mm_apply.
+ */
+static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct ltq_mm *chip =
+ container_of(mm_gc, struct ltq_mm, mmchip);
+
+ if (value)
+ chip->shadow |= (1 << offset);
+ else
+ chip->shadow &= ~(1 << offset);
+ ltq_mm_apply(chip);
+}
+
+/**
+ * ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction.
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ * @val: Value to be written to specified signal.
+ *
+ * Same as ltq_mm_set, always returns 0.
+ */
+static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value)
+{
+ ltq_mm_set(gc, offset, value);
+
+ return 0;
+}
+
+/**
+ * ltq_mm_save_regs() - Set initial values of GPIO pins
+ * @mm_gc: pointer to memory mapped GPIO chip structure
+ */
+static void ltq_mm_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+ struct ltq_mm *chip =
+ container_of(mm_gc, struct ltq_mm, mmchip);
+
+ /* tell the ebu controller which memory address we will be using */
+ ltq_ebu_w32(CPHYSADDR(chip->mmchip.regs) | 0x1, LTQ_EBU_ADDRSEL1);
+
+ ltq_mm_apply(chip);
+}
+
+static int ltq_mm_probe(struct platform_device *pdev)
+{
+ struct ltq_mm *chip;
+ u32 shadow;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, chip);
+
+ chip->mmchip.gc.ngpio = 16;
+ chip->mmchip.gc.direction_output = ltq_mm_dir_out;
+ chip->mmchip.gc.set = ltq_mm_set;
+ chip->mmchip.save_regs = ltq_mm_save_regs;
+
+ /* store the shadow value if one was passed by the devicetree */
+ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow))
+ chip->shadow = shadow;
+
+ return of_mm_gpiochip_add(pdev->dev.of_node, &chip->mmchip);
+}
+
+static int ltq_mm_remove(struct platform_device *pdev)
+{
+ struct ltq_mm *chip = platform_get_drvdata(pdev);
+
+ of_mm_gpiochip_remove(&chip->mmchip);
+
+ return 0;
+}
+
+static const struct of_device_id ltq_mm_match[] = {
+ { .compatible = "lantiq,gpio-mm" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltq_mm_match);
+
+static struct platform_driver ltq_mm_driver = {
+ .probe = ltq_mm_probe,
+ .remove = ltq_mm_remove,
+ .driver = {
+ .name = "gpio-mm-ltq",
+ .of_match_table = ltq_mm_match,
+ },
+};
+
+static int __init ltq_mm_init(void)
+{
+ return platform_driver_register(&ltq_mm_driver);
+}
+
+subsys_initcall(ltq_mm_init);
+
+static void __exit ltq_mm_exit(void)
+{
+ platform_driver_unregister(&ltq_mm_driver);
+}
+module_exit(ltq_mm_exit);
diff --git a/drivers/gpio/gpio-moxart.c b/drivers/gpio/gpio-moxart.c
new file mode 100644
index 000000000..c3ab46e59
--- /dev/null
+++ b/drivers/gpio/gpio-moxart.c
@@ -0,0 +1,114 @@
+/*
+ * MOXA ART SoCs GPIO driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.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/err.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define GPIO_DATA_OUT 0x00
+#define GPIO_DATA_IN 0x04
+#define GPIO_PIN_DIRECTION 0x08
+
+static int moxart_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(offset);
+}
+
+static void moxart_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(offset);
+}
+
+static int moxart_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(chip);
+ u32 ret = bgc->read_reg(bgc->reg_dir);
+
+ if (ret & BIT(offset))
+ return !!(bgc->read_reg(bgc->reg_set) & BIT(offset));
+ else
+ return !!(bgc->read_reg(bgc->reg_dat) & BIT(offset));
+}
+
+static int moxart_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct bgpio_chip *bgc;
+ void __iomem *base;
+ int ret;
+
+ bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL);
+ if (!bgc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ ret = bgpio_init(bgc, dev, 4, base + GPIO_DATA_IN,
+ base + GPIO_DATA_OUT, NULL,
+ base + GPIO_PIN_DIRECTION, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "bgpio_init failed\n");
+ return ret;
+ }
+
+ bgc->gc.label = "moxart-gpio";
+ bgc->gc.request = moxart_gpio_request;
+ bgc->gc.free = moxart_gpio_free;
+ bgc->gc.get = moxart_gpio_get;
+ bgc->data = bgc->read_reg(bgc->reg_set);
+ bgc->gc.base = 0;
+ bgc->gc.ngpio = 32;
+ bgc->gc.dev = dev;
+ bgc->gc.owner = THIS_MODULE;
+
+ ret = gpiochip_add(&bgc->gc);
+ if (ret) {
+ dev_err(dev, "%s: gpiochip_add failed\n",
+ dev->of_node->full_name);
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct of_device_id moxart_gpio_match[] = {
+ { .compatible = "moxa,moxart-gpio" },
+ { }
+};
+
+static struct platform_driver moxart_gpio_driver = {
+ .driver = {
+ .name = "moxart-gpio",
+ .of_match_table = moxart_gpio_match,
+ },
+ .probe = moxart_gpio_probe,
+};
+module_platform_driver(moxart_gpio_driver);
+
+MODULE_DESCRIPTION("MOXART GPIO chip driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c
new file mode 100644
index 000000000..4c542153e
--- /dev/null
+++ b/drivers/gpio/gpio-mpc5200.c
@@ -0,0 +1,388 @@
+/*
+ * MPC52xx gpio driver
+ *
+ * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+
+#include <asm/gpio.h>
+#include <asm/mpc52xx.h>
+#include <sysdev/fsl_soc.h>
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+struct mpc52xx_gpiochip {
+ struct of_mm_gpio_chip mmchip;
+ unsigned int shadow_dvo;
+ unsigned int shadow_gpioe;
+ unsigned int shadow_ddr;
+};
+
+/*
+ * GPIO LIB API implementation for wakeup GPIOs.
+ *
+ * There's a maximum of 8 wakeup GPIOs. Which of these are available
+ * for use depends on your board setup.
+ *
+ * 0 -> GPIO_WKUP_7
+ * 1 -> GPIO_WKUP_6
+ * 2 -> PSC6_1
+ * 3 -> PSC6_0
+ * 4 -> ETH_17
+ * 5 -> PSC3_9
+ * 6 -> PSC2_4
+ * 7 -> PSC1_4
+ *
+ */
+static int mpc52xx_wkup_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+ unsigned int ret;
+
+ ret = (in_8(&regs->wkup_ival) >> (7 - gpio)) & 1;
+
+ pr_debug("%s: gpio: %d ret: %d\n", __func__, gpio, ret);
+
+ return ret;
+}
+
+static inline void
+__mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+
+ if (val)
+ chip->shadow_dvo |= 1 << (7 - gpio);
+ else
+ chip->shadow_dvo &= ~(1 << (7 - gpio));
+
+ out_8(&regs->wkup_dvo, chip->shadow_dvo);
+}
+
+static void
+mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ __mpc52xx_wkup_gpio_set(gc, gpio, val);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+}
+
+static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* set the direction */
+ chip->shadow_ddr &= ~(1 << (7 - gpio));
+ out_8(&regs->wkup_ddr, chip->shadow_ddr);
+
+ /* and enable the pin */
+ chip->shadow_gpioe |= 1 << (7 - gpio);
+ out_8(&regs->wkup_gpioe, chip->shadow_gpioe);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static int
+mpc52xx_wkup_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ __mpc52xx_wkup_gpio_set(gc, gpio, val);
+
+ /* Then set direction */
+ chip->shadow_ddr |= 1 << (7 - gpio);
+ out_8(&regs->wkup_ddr, chip->shadow_ddr);
+
+ /* Finally enable the pin */
+ chip->shadow_gpioe |= 1 << (7 - gpio);
+ out_8(&regs->wkup_gpioe, chip->shadow_gpioe);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+
+ return 0;
+}
+
+static int mpc52xx_wkup_gpiochip_probe(struct platform_device *ofdev)
+{
+ struct mpc52xx_gpiochip *chip;
+ struct mpc52xx_gpio_wkup __iomem *regs;
+ struct gpio_chip *gc;
+ int ret;
+
+ chip = devm_kzalloc(&ofdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ platform_set_drvdata(ofdev, chip);
+
+ gc = &chip->mmchip.gc;
+
+ gc->ngpio = 8;
+ gc->direction_input = mpc52xx_wkup_gpio_dir_in;
+ gc->direction_output = mpc52xx_wkup_gpio_dir_out;
+ gc->get = mpc52xx_wkup_gpio_get;
+ gc->set = mpc52xx_wkup_gpio_set;
+
+ ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
+ if (ret)
+ return ret;
+
+ regs = chip->mmchip.regs;
+ chip->shadow_gpioe = in_8(&regs->wkup_gpioe);
+ chip->shadow_ddr = in_8(&regs->wkup_ddr);
+ chip->shadow_dvo = in_8(&regs->wkup_dvo);
+
+ return 0;
+}
+
+static int mpc52xx_gpiochip_remove(struct platform_device *ofdev)
+{
+ struct mpc52xx_gpiochip *chip = platform_get_drvdata(ofdev);
+
+ of_mm_gpiochip_remove(&chip->mmchip);
+
+ return 0;
+}
+
+static const struct of_device_id mpc52xx_wkup_gpiochip_match[] = {
+ { .compatible = "fsl,mpc5200-gpio-wkup", },
+ {}
+};
+
+static struct platform_driver mpc52xx_wkup_gpiochip_driver = {
+ .driver = {
+ .name = "mpc5200-gpio-wkup",
+ .of_match_table = mpc52xx_wkup_gpiochip_match,
+ },
+ .probe = mpc52xx_wkup_gpiochip_probe,
+ .remove = mpc52xx_gpiochip_remove,
+};
+
+/*
+ * GPIO LIB API implementation for simple GPIOs
+ *
+ * There's a maximum of 32 simple GPIOs. Which of these are available
+ * for use depends on your board setup.
+ * The numbering reflects the bit numbering in the port registers:
+ *
+ * 0..1 > reserved
+ * 2..3 > IRDA
+ * 4..7 > ETHR
+ * 8..11 > reserved
+ * 12..15 > USB
+ * 16..17 > reserved
+ * 18..23 > PSC3
+ * 24..27 > PSC2
+ * 28..31 > PSC1
+ */
+static int mpc52xx_simple_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+ unsigned int ret;
+
+ ret = (in_be32(&regs->simple_ival) >> (31 - gpio)) & 1;
+
+ return ret;
+}
+
+static inline void
+__mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+
+ if (val)
+ chip->shadow_dvo |= 1 << (31 - gpio);
+ else
+ chip->shadow_dvo &= ~(1 << (31 - gpio));
+ out_be32(&regs->simple_dvo, chip->shadow_dvo);
+}
+
+static void
+mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ __mpc52xx_simple_gpio_set(gc, gpio, val);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+}
+
+static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* set the direction */
+ chip->shadow_ddr &= ~(1 << (31 - gpio));
+ out_be32(&regs->simple_ddr, chip->shadow_ddr);
+
+ /* and enable the pin */
+ chip->shadow_gpioe |= 1 << (31 - gpio);
+ out_be32(&regs->simple_gpioe, chip->shadow_gpioe);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static int
+mpc52xx_simple_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+ struct mpc52xx_gpiochip, mmchip);
+ struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* First set initial value */
+ __mpc52xx_simple_gpio_set(gc, gpio, val);
+
+ /* Then set direction */
+ chip->shadow_ddr |= 1 << (31 - gpio);
+ out_be32(&regs->simple_ddr, chip->shadow_ddr);
+
+ /* Finally enable the pin */
+ chip->shadow_gpioe |= 1 << (31 - gpio);
+ out_be32(&regs->simple_gpioe, chip->shadow_gpioe);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+
+ return 0;
+}
+
+static int mpc52xx_simple_gpiochip_probe(struct platform_device *ofdev)
+{
+ struct mpc52xx_gpiochip *chip;
+ struct gpio_chip *gc;
+ struct mpc52xx_gpio __iomem *regs;
+ int ret;
+
+ chip = devm_kzalloc(&ofdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ platform_set_drvdata(ofdev, chip);
+
+ gc = &chip->mmchip.gc;
+
+ gc->ngpio = 32;
+ gc->direction_input = mpc52xx_simple_gpio_dir_in;
+ gc->direction_output = mpc52xx_simple_gpio_dir_out;
+ gc->get = mpc52xx_simple_gpio_get;
+ gc->set = mpc52xx_simple_gpio_set;
+
+ ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
+ if (ret)
+ return ret;
+
+ regs = chip->mmchip.regs;
+ chip->shadow_gpioe = in_be32(&regs->simple_gpioe);
+ chip->shadow_ddr = in_be32(&regs->simple_ddr);
+ chip->shadow_dvo = in_be32(&regs->simple_dvo);
+
+ return 0;
+}
+
+static const struct of_device_id mpc52xx_simple_gpiochip_match[] = {
+ { .compatible = "fsl,mpc5200-gpio", },
+ {}
+};
+
+static struct platform_driver mpc52xx_simple_gpiochip_driver = {
+ .driver = {
+ .name = "mpc5200-gpio",
+ .of_match_table = mpc52xx_simple_gpiochip_match,
+ },
+ .probe = mpc52xx_simple_gpiochip_probe,
+ .remove = mpc52xx_gpiochip_remove,
+};
+
+static int __init mpc52xx_gpio_init(void)
+{
+ if (platform_driver_register(&mpc52xx_wkup_gpiochip_driver))
+ printk(KERN_ERR "Unable to register wakeup GPIO driver\n");
+
+ if (platform_driver_register(&mpc52xx_simple_gpiochip_driver))
+ printk(KERN_ERR "Unable to register simple GPIO driver\n");
+
+ return 0;
+}
+
+/* Make sure we get initialised before anyone else tries to use us */
+subsys_initcall(mpc52xx_gpio_init);
+
+static void __exit mpc52xx_gpio_exit(void)
+{
+ platform_driver_unregister(&mpc52xx_wkup_gpiochip_driver);
+
+ platform_driver_unregister(&mpc52xx_simple_gpiochip_driver);
+}
+module_exit(mpc52xx_gpio_exit);
+
+MODULE_DESCRIPTION("Freescale MPC52xx gpio driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
new file mode 100644
index 000000000..a65b75161
--- /dev/null
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -0,0 +1,434 @@
+/*
+ * GPIOs on MPC512x/8349/8572/8610 and compatible
+ *
+ * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+
+#define MPC8XXX_GPIO_PINS 32
+
+#define GPIO_DIR 0x00
+#define GPIO_ODR 0x04
+#define GPIO_DAT 0x08
+#define GPIO_IER 0x0c
+#define GPIO_IMR 0x10
+#define GPIO_ICR 0x14
+#define GPIO_ICR2 0x18
+
+struct mpc8xxx_gpio_chip {
+ struct of_mm_gpio_chip mm_gc;
+ spinlock_t lock;
+
+ /*
+ * shadowed data register to be able to clear/set output pins in
+ * open drain mode safely
+ */
+ u32 data;
+ struct irq_domain *irq;
+ unsigned int irqn;
+ const void *of_dev_id_data;
+};
+
+static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
+{
+ return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio);
+}
+
+static inline struct mpc8xxx_gpio_chip *
+to_mpc8xxx_gpio_chip(struct of_mm_gpio_chip *mm)
+{
+ return container_of(mm, struct mpc8xxx_gpio_chip, mm_gc);
+}
+
+static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+ mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT);
+}
+
+/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs
+ * defined as output cannot be determined by reading GPDAT register,
+ * so we use shadow data register instead. The status of input pins
+ * is determined by reading GPDAT register.
+ */
+static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ u32 val;
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+ u32 out_mask, out_shadow;
+
+ out_mask = in_be32(mm->regs + GPIO_DIR);
+
+ val = in_be32(mm->regs + GPIO_DAT) & ~out_mask;
+ out_shadow = mpc8xxx_gc->data & out_mask;
+
+ return (val | out_shadow) & mpc8xxx_gpio2mask(gpio);
+}
+
+static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+
+ return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio);
+}
+
+static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ if (val)
+ mpc8xxx_gc->data |= mpc8xxx_gpio2mask(gpio);
+ else
+ mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio);
+
+ out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
+static void mpc8xxx_gpio_set_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ for (i = 0; i < gc->ngpio; i++) {
+ if (*mask == 0)
+ break;
+ if (__test_and_clear_bit(i, mask)) {
+ if (test_bit(i, bits))
+ mpc8xxx_gc->data |= mpc8xxx_gpio2mask(i);
+ else
+ mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(i);
+ }
+ }
+
+ out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
+static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+
+ return 0;
+}
+
+static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+ unsigned long flags;
+
+ mpc8xxx_gpio_set(gc, gpio, val);
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+
+ return 0;
+}
+
+static int mpc5121_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ /* GPIO 28..31 are input only on MPC5121 */
+ if (gpio >= 28)
+ return -EINVAL;
+
+ return mpc8xxx_gpio_dir_out(gc, gpio, val);
+}
+
+static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+ if (mpc8xxx_gc->irq && offset < MPC8XXX_GPIO_PINS)
+ return irq_create_mapping(mpc8xxx_gc->irq, offset);
+ else
+ return -ENXIO;
+}
+
+static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned int mask;
+
+ mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->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);
+}
+
+static void mpc8xxx_irq_unmask(struct irq_data *d)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
+static void mpc8xxx_irq_mask(struct irq_data *d)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+ clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
+static void mpc8xxx_irq_ack(struct irq_data *d)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+
+ out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+}
+
+static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long flags;
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_FALLING:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ setbits32(mm->regs + GPIO_ICR,
+ mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrbits32(mm->regs + GPIO_ICR,
+ mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long gpio = irqd_to_hwirq(d);
+ void __iomem *reg;
+ unsigned int shift;
+ unsigned long flags;
+
+ if (gpio < 16) {
+ reg = mm->regs + GPIO_ICR;
+ shift = (15 - gpio) * 2;
+ } else {
+ reg = mm->regs + GPIO_ICR2;
+ shift = (15 - (gpio % 16)) * 2;
+ }
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_LEVEL_LOW:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrsetbits_be32(reg, 3 << shift, 2 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrsetbits_be32(reg, 3 << shift, 1 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrbits32(reg, 3 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct irq_chip mpc8xxx_irq_chip = {
+ .name = "mpc8xxx-gpio",
+ .irq_unmask = mpc8xxx_irq_unmask,
+ .irq_mask = mpc8xxx_irq_mask,
+ .irq_ack = mpc8xxx_irq_ack,
+ .irq_set_type = mpc8xxx_irq_set_type,
+};
+
+static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = h->host_data;
+
+ if (mpc8xxx_gc->of_dev_id_data)
+ mpc8xxx_irq_chip.irq_set_type = mpc8xxx_gc->of_dev_id_data;
+
+ irq_set_chip_data(irq, h->host_data);
+ irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static struct irq_domain_ops mpc8xxx_gpio_irq_ops = {
+ .map = mpc8xxx_gpio_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static struct of_device_id mpc8xxx_gpio_ids[] = {
+ { .compatible = "fsl,mpc8349-gpio", },
+ { .compatible = "fsl,mpc8572-gpio", },
+ { .compatible = "fsl,mpc8610-gpio", },
+ { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, },
+ { .compatible = "fsl,pq3-gpio", },
+ { .compatible = "fsl,qoriq-gpio", },
+ {}
+};
+
+static int mpc8xxx_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct gpio_chip *gc;
+ const struct of_device_id *id;
+ int ret;
+
+ mpc8xxx_gc = devm_kzalloc(&pdev->dev, sizeof(*mpc8xxx_gc), GFP_KERNEL);
+ if (!mpc8xxx_gc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mpc8xxx_gc);
+
+ spin_lock_init(&mpc8xxx_gc->lock);
+
+ mm_gc = &mpc8xxx_gc->mm_gc;
+ gc = &mm_gc->gc;
+
+ mm_gc->save_regs = mpc8xxx_gpio_save_regs;
+ gc->ngpio = MPC8XXX_GPIO_PINS;
+ gc->direction_input = mpc8xxx_gpio_dir_in;
+ gc->direction_output = of_device_is_compatible(np, "fsl,mpc5121-gpio") ?
+ mpc5121_gpio_dir_out : mpc8xxx_gpio_dir_out;
+ gc->get = of_device_is_compatible(np, "fsl,mpc8572-gpio") ?
+ mpc8572_gpio_get : mpc8xxx_gpio_get;
+ gc->set = mpc8xxx_gpio_set;
+ gc->set_multiple = mpc8xxx_gpio_set_multiple;
+ gc->to_irq = mpc8xxx_gpio_to_irq;
+
+ ret = of_mm_gpiochip_add(np, mm_gc);
+ if (ret)
+ return ret;
+
+ mpc8xxx_gc->irqn = irq_of_parse_and_map(np, 0);
+ if (mpc8xxx_gc->irqn == NO_IRQ)
+ return 0;
+
+ mpc8xxx_gc->irq = irq_domain_add_linear(np, MPC8XXX_GPIO_PINS,
+ &mpc8xxx_gpio_irq_ops, mpc8xxx_gc);
+ if (!mpc8xxx_gc->irq)
+ return 0;
+
+ id = of_match_node(mpc8xxx_gpio_ids, np);
+ if (id)
+ mpc8xxx_gc->of_dev_id_data = id->data;
+
+ /* ack and mask all irqs */
+ out_be32(mm_gc->regs + GPIO_IER, 0xffffffff);
+ out_be32(mm_gc->regs + GPIO_IMR, 0);
+
+ irq_set_handler_data(mpc8xxx_gc->irqn, mpc8xxx_gc);
+ irq_set_chained_handler(mpc8xxx_gc->irqn, mpc8xxx_gpio_irq_cascade);
+
+ return 0;
+}
+
+static int mpc8xxx_remove(struct platform_device *pdev)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = platform_get_drvdata(pdev);
+
+ if (mpc8xxx_gc->irq) {
+ irq_set_handler_data(mpc8xxx_gc->irqn, NULL);
+ irq_set_chained_handler(mpc8xxx_gc->irqn, NULL);
+ irq_domain_remove(mpc8xxx_gc->irq);
+ }
+
+ of_mm_gpiochip_remove(&mpc8xxx_gc->mm_gc);
+
+ return 0;
+}
+
+static struct platform_driver mpc8xxx_plat_driver = {
+ .probe = mpc8xxx_probe,
+ .remove = mpc8xxx_remove,
+ .driver = {
+ .name = "gpio-mpc8xxx",
+ .of_match_table = mpc8xxx_gpio_ids,
+ },
+};
+
+static int __init mpc8xxx_init(void)
+{
+ return platform_driver_register(&mpc8xxx_plat_driver);
+}
+
+arch_initcall(mpc8xxx_init);
diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c
new file mode 100644
index 000000000..01acf0a8c
--- /dev/null
+++ b/drivers/gpio/gpio-msic.c
@@ -0,0 +1,337 @@
+/*
+ * Intel Medfield MSIC GPIO driver>
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * Author: Mathias Nyman <mathias.nyman@linux.intel.com>
+ * Based on intel_pmic_gpio.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/intel_msic.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 = container_of(chip, struct msic_gpio, 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(unsigned irq, 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);
+ int i;
+ int bitnr;
+ u8 pin;
+ unsigned long pending = 0;
+
+ for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) {
+ intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin);
+ pending = pin;
+
+ if (pending) {
+ 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\n");
+ return -EINVAL;
+ }
+
+ 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.dev = dev;
+
+ mutex_init(&mg->buslock);
+
+ retval = gpiochip_add(&mg->chip);
+ 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(mg->irq, msic_gpio_irq_handler);
+ irq_set_handler_data(mg->irq, 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);
+
+MODULE_AUTHOR("Mathias Nyman <mathias.nyman@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Medfield MSIC GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
new file mode 100644
index 000000000..52ff18229
--- /dev/null
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -0,0 +1,462 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+#define MAX_NR_GPIO 300
+
+/* Bits of interest in the GPIO_IN_OUT register.
+ */
+enum {
+ GPIO_IN = 0,
+ GPIO_OUT = 1
+};
+
+/* Bits of interest in the GPIO_INTR_STATUS register.
+ */
+enum {
+ INTR_STATUS = 0,
+};
+
+/* Bits of interest in the GPIO_CFG register.
+ */
+enum {
+ GPIO_OE = 9,
+};
+
+/* Bits of interest in the GPIO_INTR_CFG register.
+ * When a GPIO triggers, two separate decisions are made, controlled
+ * by two separate flags.
+ *
+ * - First, INTR_RAW_STATUS_EN controls whether or not the GPIO_INTR_STATUS
+ * register for that GPIO will be updated to reflect the triggering of that
+ * gpio. If this bit is 0, this register will not be updated.
+ * - Second, INTR_ENABLE controls whether an interrupt is triggered.
+ *
+ * If INTR_ENABLE is set and INTR_RAW_STATUS_EN is NOT set, an interrupt
+ * can be triggered but the status register will not reflect it.
+ */
+enum {
+ INTR_ENABLE = 0,
+ INTR_POL_CTL = 1,
+ INTR_DECT_CTL = 2,
+ INTR_RAW_STATUS_EN = 3,
+};
+
+/* Codes of interest in GPIO_INTR_CFG_SU.
+ */
+enum {
+ TARGET_PROC_SCORPION = 4,
+ TARGET_PROC_NONE = 7,
+};
+
+/**
+ * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
+ *
+ * @enabled_irqs: a bitmap used to optimize the summary-irq handler. By
+ * keeping track of which gpios are unmasked as irq sources, we avoid
+ * having to do readl calls on hundreds of iomapped registers each time
+ * the summary interrupt fires in order to locate the active interrupts.
+ *
+ * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
+ * as wakeup sources. When the device is suspended, interrupts which are
+ * not wakeup sources are disabled.
+ *
+ * @dual_edge_irqs: a bitmap used to track which irqs are configured
+ * as dual-edge, as this is not supported by the hardware and requires
+ * some special handling in the driver.
+ */
+struct msm_gpio_dev {
+ struct gpio_chip gpio_chip;
+ DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
+ DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO);
+ DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
+ struct irq_domain *domain;
+ int summary_irq;
+ void __iomem *msm_tlmm_base;
+};
+
+static struct msm_gpio_dev msm_gpio;
+
+#define GPIO_INTR_CFG_SU(gpio) (msm_gpio.msm_tlmm_base + 0x0400 + \
+ (0x04 * (gpio)))
+#define GPIO_CONFIG(gpio) (msm_gpio.msm_tlmm_base + 0x1000 + \
+ (0x10 * (gpio)))
+#define GPIO_IN_OUT(gpio) (msm_gpio.msm_tlmm_base + 0x1004 + \
+ (0x10 * (gpio)))
+#define GPIO_INTR_CFG(gpio) (msm_gpio.msm_tlmm_base + 0x1008 + \
+ (0x10 * (gpio)))
+#define GPIO_INTR_STATUS(gpio) (msm_gpio.msm_tlmm_base + 0x100c + \
+ (0x10 * (gpio)))
+
+static DEFINE_SPINLOCK(tlmm_lock);
+
+static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
+{
+ return container_of(chip, struct msm_gpio_dev, gpio_chip);
+}
+
+static inline void set_gpio_bits(unsigned n, void __iomem *reg)
+{
+ writel(readl(reg) | n, reg);
+}
+
+static inline void clear_gpio_bits(unsigned n, void __iomem *reg)
+{
+ writel(readl(reg) & ~n, reg);
+}
+
+static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN);
+}
+
+static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ writel(val ? BIT(GPIO_OUT) : 0, GPIO_IN_OUT(offset));
+}
+
+static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+ clear_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+ return 0;
+}
+
+static int msm_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset,
+ int val)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+ msm_gpio_set(chip, offset, val);
+ set_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+ return 0;
+}
+
+static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return 0;
+}
+
+static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ return;
+}
+
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
+ struct irq_domain *domain = g_dev->domain;
+
+ return irq_create_mapping(domain, offset);
+}
+
+static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
+{
+ struct irq_data *irq_data = irq_get_irq_data(irq);
+
+ return irq_data->hwirq;
+}
+
+
+/* For dual-edge interrupts in software, since the hardware has no
+ * such support:
+ *
+ * At appropriate moments, this function may be called to flip the polarity
+ * settings of both-edge irq lines to try and catch the next edge.
+ *
+ * The attempt is considered successful if:
+ * - the status bit goes high, indicating that an edge was caught, or
+ * - the input value of the gpio doesn't change during the attempt.
+ * If the value changes twice during the process, that would cause the first
+ * test to fail but would force the second, as two opposite
+ * transitions would cause a detection no matter the polarity setting.
+ *
+ * The do-loop tries to sledge-hammer closed the timing hole between
+ * the initial value-read and the polarity-write - if the line value changes
+ * during that window, an interrupt is lost, the new polarity setting is
+ * incorrect, and the first success test will fail, causing a retry.
+ *
+ * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
+ */
+static void msm_gpio_update_dual_edge_pos(unsigned gpio)
+{
+ int loop_limit = 100;
+ unsigned val, val2, intstat;
+
+ do {
+ val = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
+ if (val)
+ clear_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
+ else
+ set_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
+ val2 = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
+ intstat = readl(GPIO_INTR_STATUS(gpio)) & BIT(INTR_STATUS);
+ if (intstat || val == val2)
+ return;
+ } while (loop_limit-- > 0);
+ pr_err("%s: dual-edge irq failed to stabilize, "
+ "interrupts dropped. %#08x != %#08x\n",
+ __func__, val, val2);
+}
+
+static void msm_gpio_irq_ack(struct irq_data *d)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+
+ writel(BIT(INTR_STATUS), GPIO_INTR_STATUS(gpio));
+ if (test_bit(gpio, msm_gpio.dual_edge_irqs))
+ msm_gpio_update_dual_edge_pos(gpio);
+}
+
+static void msm_gpio_irq_mask(struct irq_data *d)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+ writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
+ clear_gpio_bits(BIT(INTR_RAW_STATUS_EN) | BIT(INTR_ENABLE), GPIO_INTR_CFG(gpio));
+ __clear_bit(gpio, msm_gpio.enabled_irqs);
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+}
+
+static void msm_gpio_irq_unmask(struct irq_data *d)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+ __set_bit(gpio, msm_gpio.enabled_irqs);
+ set_gpio_bits(BIT(INTR_RAW_STATUS_EN) | BIT(INTR_ENABLE), GPIO_INTR_CFG(gpio));
+ writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+}
+
+static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+ unsigned long irq_flags;
+ uint32_t bits;
+
+ spin_lock_irqsave(&tlmm_lock, irq_flags);
+
+ bits = readl(GPIO_INTR_CFG(gpio));
+
+ if (flow_type & IRQ_TYPE_EDGE_BOTH) {
+ bits |= BIT(INTR_DECT_CTL);
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
+ __set_bit(gpio, msm_gpio.dual_edge_irqs);
+ else
+ __clear_bit(gpio, msm_gpio.dual_edge_irqs);
+ } else {
+ bits &= ~BIT(INTR_DECT_CTL);
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ __clear_bit(gpio, msm_gpio.dual_edge_irqs);
+ }
+
+ if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
+ bits |= BIT(INTR_POL_CTL);
+ else
+ bits &= ~BIT(INTR_POL_CTL);
+
+ writel(bits, GPIO_INTR_CFG(gpio));
+
+ if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
+ msm_gpio_update_dual_edge_pos(gpio);
+
+ spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+
+ return 0;
+}
+
+/*
+ * When the summary IRQ is raised, any number of GPIO lines may be high.
+ * It is the job of the summary handler to find all those GPIO lines
+ * which have been set as summary IRQ lines and which are triggered,
+ * and to call their interrupt handlers.
+ */
+static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned long i;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+
+ for_each_set_bit(i, msm_gpio.enabled_irqs, MAX_NR_GPIO) {
+ if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
+ generic_handle_irq(irq_find_mapping(msm_gpio.domain,
+ i));
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
+
+ if (on) {
+ if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO))
+ irq_set_irq_wake(msm_gpio.summary_irq, 1);
+ set_bit(gpio, msm_gpio.wake_irqs);
+ } else {
+ clear_bit(gpio, msm_gpio.wake_irqs);
+ if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO))
+ irq_set_irq_wake(msm_gpio.summary_irq, 0);
+ }
+
+ return 0;
+}
+
+static struct irq_chip msm_gpio_irq_chip = {
+ .name = "msmgpio",
+ .irq_mask = msm_gpio_irq_mask,
+ .irq_unmask = msm_gpio_irq_unmask,
+ .irq_ack = msm_gpio_irq_ack,
+ .irq_set_type = msm_gpio_irq_set_type,
+ .irq_set_wake = msm_gpio_irq_set_wake,
+};
+
+static struct lock_class_key msm_gpio_lock_class;
+
+static int msm_gpio_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_lockdep_class(irq, &msm_gpio_lock_class);
+ irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
+ handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static const struct irq_domain_ops msm_gpio_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .map = msm_gpio_irq_domain_map,
+};
+
+static int msm_gpio_probe(struct platform_device *pdev)
+{
+ int ret, ngpio;
+ struct resource *res;
+
+ if (of_property_read_u32(pdev->dev.of_node, "ngpio", &ngpio)) {
+ dev_err(&pdev->dev, "%s: ngpio property missing\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ngpio > MAX_NR_GPIO)
+ WARN(1, "ngpio exceeds the MAX_NR_GPIO. Increase MAX_NR_GPIO\n");
+
+ bitmap_zero(msm_gpio.enabled_irqs, MAX_NR_GPIO);
+ bitmap_zero(msm_gpio.wake_irqs, MAX_NR_GPIO);
+ bitmap_zero(msm_gpio.dual_edge_irqs, MAX_NR_GPIO);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ msm_gpio.msm_tlmm_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(msm_gpio.msm_tlmm_base))
+ return PTR_ERR(msm_gpio.msm_tlmm_base);
+
+ msm_gpio.gpio_chip.ngpio = ngpio;
+ msm_gpio.gpio_chip.label = pdev->name;
+ msm_gpio.gpio_chip.dev = &pdev->dev;
+ msm_gpio.gpio_chip.base = 0;
+ msm_gpio.gpio_chip.direction_input = msm_gpio_direction_input;
+ msm_gpio.gpio_chip.direction_output = msm_gpio_direction_output;
+ msm_gpio.gpio_chip.get = msm_gpio_get;
+ msm_gpio.gpio_chip.set = msm_gpio_set;
+ msm_gpio.gpio_chip.to_irq = msm_gpio_to_irq;
+ msm_gpio.gpio_chip.request = msm_gpio_request;
+ msm_gpio.gpio_chip.free = msm_gpio_free;
+
+ ret = gpiochip_add(&msm_gpio.gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpiochip_add failed with error %d\n", ret);
+ return ret;
+ }
+
+ msm_gpio.summary_irq = platform_get_irq(pdev, 0);
+ if (msm_gpio.summary_irq < 0) {
+ dev_err(&pdev->dev, "No Summary irq defined for msmgpio\n");
+ return msm_gpio.summary_irq;
+ }
+
+ msm_gpio.domain = irq_domain_add_linear(pdev->dev.of_node, ngpio,
+ &msm_gpio_irq_domain_ops,
+ &msm_gpio);
+ if (!msm_gpio.domain)
+ return -ENODEV;
+
+ irq_set_chained_handler(msm_gpio.summary_irq, msm_summary_irq_handler);
+
+ return 0;
+}
+
+static const struct of_device_id msm_gpio_of_match[] = {
+ { .compatible = "qcom,msm-gpio", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, msm_gpio_of_match);
+
+static int msm_gpio_remove(struct platform_device *dev)
+{
+ gpiochip_remove(&msm_gpio.gpio_chip);
+
+ irq_set_handler(msm_gpio.summary_irq, NULL);
+
+ return 0;
+}
+
+static struct platform_driver msm_gpio_driver = {
+ .probe = msm_gpio_probe,
+ .remove = msm_gpio_remove,
+ .driver = {
+ .name = "msmgpio",
+ .of_match_table = msm_gpio_of_match,
+ },
+};
+
+module_platform_driver(msm_gpio_driver)
+
+MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msmgpio");
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
new file mode 100644
index 000000000..1a5420586
--- /dev/null
+++ b/drivers/gpio/gpio-mvebu.c
@@ -0,0 +1,863 @@
+/*
+ * GPIO driver for Marvell SoCs
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.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.
+ *
+ * This driver is a fairly straightforward GPIO driver for the
+ * complete family of Marvell EBU SoC platforms (Orion, Dove,
+ * Kirkwood, Discovery, Armada 370/XP). The only complexity of this
+ * driver is the different register layout that exists between the
+ * non-SMP platforms (Orion, Dove, Kirkwood, Armada 370) and the SMP
+ * platforms (MV78200 from the Discovery family and the Armada
+ * XP). Therefore, this driver handles three variants of the GPIO
+ * block:
+ * - the basic variant, called "orion-gpio", with the simplest
+ * register set. Used on Orion, Dove, Kirkwoord, Armada 370 and
+ * non-SMP Discovery systems
+ * - the mv78200 variant for MV78200 Discovery systems. This variant
+ * turns the edge mask and level mask registers into CPU0 edge
+ * mask/level mask registers, and adds CPU1 edge mask/level mask
+ * registers.
+ * - the armadaxp variant for Armada XP systems. This variant keeps
+ * the normal cause/edge mask/level mask registers when the global
+ * interrupts are used, but adds per-CPU cause/edge mask/level mask
+ * registers n a separate memory area for the per-CPU GPIO
+ * interrupts.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/irqchip/chained_irq.h>
+
+/*
+ * GPIO unit register offsets.
+ */
+#define GPIO_OUT_OFF 0x0000
+#define GPIO_IO_CONF_OFF 0x0004
+#define GPIO_BLINK_EN_OFF 0x0008
+#define GPIO_IN_POL_OFF 0x000c
+#define GPIO_DATA_IN_OFF 0x0010
+#define GPIO_EDGE_CAUSE_OFF 0x0014
+#define GPIO_EDGE_MASK_OFF 0x0018
+#define GPIO_LEVEL_MASK_OFF 0x001c
+
+/* The MV78200 has per-CPU registers for edge mask and level mask */
+#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
+#define GPIO_LEVEL_MASK_MV78200_OFF(cpu) ((cpu) ? 0x34 : 0x1C)
+
+/* The Armada XP has per-CPU registers for interrupt cause, interrupt
+ * mask and interrupt level mask. Those are relative to the
+ * percpu_membase. */
+#define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4)
+#define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4)
+#define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4)
+
+#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
+#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
+#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
+
+#define MVEBU_MAX_GPIO_PER_BANK 32
+
+struct mvebu_gpio_chip {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ void __iomem *membase;
+ void __iomem *percpu_membase;
+ int irqbase;
+ struct irq_domain *domain;
+ int soc_variant;
+
+ /* Used to preserve GPIO registers across suspend/resume */
+ u32 out_reg;
+ u32 io_conf_reg;
+ u32 blink_en_reg;
+ u32 in_pol_reg;
+ u32 edge_mask_regs[4];
+ u32 level_mask_regs[4];
+};
+
+/*
+ * Functions returning addresses of individual registers for a given
+ * GPIO controller.
+ */
+static inline void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_OUT_OFF;
+}
+
+static inline void __iomem *mvebu_gpioreg_blink(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_BLINK_EN_OFF;
+}
+
+static inline void __iomem *
+mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_IO_CONF_OFF;
+}
+
+static inline void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_IN_POL_OFF;
+}
+
+static inline void __iomem *
+mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_DATA_IN_OFF;
+}
+
+static inline void __iomem *
+mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
+{
+ int cpu;
+
+ switch (mvchip->soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ return mvchip->membase + GPIO_EDGE_CAUSE_OFF;
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ cpu = smp_processor_id();
+ return mvchip->percpu_membase +
+ GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
+ default:
+ BUG();
+ }
+}
+
+static inline void __iomem *
+mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
+{
+ int cpu;
+
+ switch (mvchip->soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ return mvchip->membase + GPIO_EDGE_MASK_OFF;
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ cpu = smp_processor_id();
+ return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu);
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ cpu = smp_processor_id();
+ return mvchip->percpu_membase +
+ GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
+ default:
+ BUG();
+ }
+}
+
+static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip)
+{
+ int cpu;
+
+ switch (mvchip->soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ return mvchip->membase + GPIO_LEVEL_MASK_OFF;
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ cpu = smp_processor_id();
+ return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu);
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ cpu = smp_processor_id();
+ return mvchip->percpu_membase +
+ GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
+ default:
+ BUG();
+ }
+}
+
+/*
+ * Functions implementing the gpio_chip methods
+ */
+
+static int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin)
+{
+ return pinctrl_request_gpio(chip->base + pin);
+}
+
+static void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin)
+{
+ pinctrl_free_gpio(chip->base + pin);
+}
+
+static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ unsigned long flags;
+ u32 u;
+
+ spin_lock_irqsave(&mvchip->lock, flags);
+ u = readl_relaxed(mvebu_gpioreg_out(mvchip));
+ if (value)
+ u |= 1 << pin;
+ else
+ u &= ~(1 << pin);
+ writel_relaxed(u, mvebu_gpioreg_out(mvchip));
+ spin_unlock_irqrestore(&mvchip->lock, flags);
+}
+
+static int mvebu_gpio_get(struct gpio_chip *chip, unsigned pin)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ u32 u;
+
+ if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin)) {
+ u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^
+ readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ } else {
+ u = readl_relaxed(mvebu_gpioreg_out(mvchip));
+ }
+
+ return (u >> pin) & 1;
+}
+
+static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ unsigned long flags;
+ u32 u;
+
+ spin_lock_irqsave(&mvchip->lock, flags);
+ u = readl_relaxed(mvebu_gpioreg_blink(mvchip));
+ if (value)
+ u |= 1 << pin;
+ else
+ u &= ~(1 << pin);
+ writel_relaxed(u, mvebu_gpioreg_blink(mvchip));
+ spin_unlock_irqrestore(&mvchip->lock, flags);
+}
+
+static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ unsigned long flags;
+ int ret;
+ u32 u;
+
+ /* Check with the pinctrl driver whether this pin is usable as
+ * an input GPIO */
+ ret = pinctrl_gpio_direction_input(chip->base + pin);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&mvchip->lock, flags);
+ u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
+ u |= 1 << pin;
+ writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
+ spin_unlock_irqrestore(&mvchip->lock, flags);
+
+ return 0;
+}
+
+static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ unsigned long flags;
+ int ret;
+ u32 u;
+
+ /* Check with the pinctrl driver whether this pin is usable as
+ * an output GPIO */
+ ret = pinctrl_gpio_direction_output(chip->base + pin);
+ if (ret)
+ return ret;
+
+ mvebu_gpio_blink(chip, pin, 0);
+ mvebu_gpio_set(chip, pin, value);
+
+ spin_lock_irqsave(&mvchip->lock, flags);
+ u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
+ u &= ~(1 << pin);
+ writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
+ spin_unlock_irqrestore(&mvchip->lock, flags);
+
+ return 0;
+}
+
+static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ return irq_create_mapping(mvchip->domain, pin);
+}
+
+/*
+ * Functions implementing the irq_chip methods
+ */
+static void mvebu_gpio_irq_ack(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ u32 mask = ~(1 << (d->irq - gc->irq_base));
+
+ irq_gc_lock(gc);
+ writel_relaxed(mask, mvebu_gpioreg_edge_cause(mvchip));
+ irq_gc_unlock(gc);
+}
+
+static void mvebu_gpio_edge_irq_mask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+ u32 mask = 1 << (d->irq - gc->irq_base);
+
+ irq_gc_lock(gc);
+ ct->mask_cache_priv &= ~mask;
+
+ writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip));
+ irq_gc_unlock(gc);
+}
+
+static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+
+ u32 mask = 1 << (d->irq - gc->irq_base);
+
+ irq_gc_lock(gc);
+ ct->mask_cache_priv |= mask;
+ writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip));
+ irq_gc_unlock(gc);
+}
+
+static void mvebu_gpio_level_irq_mask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+
+ u32 mask = 1 << (d->irq - gc->irq_base);
+
+ irq_gc_lock(gc);
+ ct->mask_cache_priv &= ~mask;
+ writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip));
+ irq_gc_unlock(gc);
+}
+
+static void mvebu_gpio_level_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+
+ u32 mask = 1 << (d->irq - gc->irq_base);
+
+ irq_gc_lock(gc);
+ ct->mask_cache_priv |= mask;
+ writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip));
+ irq_gc_unlock(gc);
+}
+
+/*****************************************************************************
+ * MVEBU GPIO IRQ
+ *
+ * GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same
+ * value of the line or the opposite value.
+ *
+ * Level IRQ handlers: DATA_IN is used directly as cause register.
+ * Interrupt are masked by LEVEL_MASK registers.
+ * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE.
+ * Interrupt are masked by EDGE_MASK registers.
+ * Both-edge handlers: Similar to regular Edge handlers, but also swaps
+ * the polarity to catch the next line transaction.
+ * This is a race condition that might not perfectly
+ * work on some use cases.
+ *
+ * Every eight GPIO lines are grouped (OR'ed) before going up to main
+ * cause register.
+ *
+ * EDGE cause mask
+ * data-in /--------| |-----| |----\
+ * -----| |----- ---- to main cause reg
+ * X \----------------| |----/
+ * polarity LEVEL mask
+ *
+ ****************************************************************************/
+
+static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ int pin;
+ u32 u;
+
+ pin = d->hwirq;
+
+ u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin);
+ if (!u)
+ return -EINVAL;
+
+ type &= IRQ_TYPE_SENSE_MASK;
+ if (type == IRQ_TYPE_NONE)
+ return -EINVAL;
+
+ /* Check if we need to change chip and handler */
+ if (!(ct->type & type))
+ if (irq_setup_alt_chip(d, type))
+ return -EINVAL;
+
+ /*
+ * Configure interrupt polarity.
+ */
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ u &= ~(1 << pin);
+ writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_LEVEL_LOW:
+ u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ u |= 1 << pin;
+ writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
+ break;
+ case IRQ_TYPE_EDGE_BOTH: {
+ u32 v;
+
+ v = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)) ^
+ readl_relaxed(mvebu_gpioreg_data_in(mvchip));
+
+ /*
+ * set initial polarity based on current input level
+ */
+ u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ if (v & (1 << pin))
+ u |= 1 << pin; /* falling */
+ else
+ u &= ~(1 << pin); /* rising */
+ writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
+ break;
+ }
+ }
+ return 0;
+}
+
+static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct mvebu_gpio_chip *mvchip = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 cause, type;
+ int i;
+
+ if (mvchip == NULL)
+ return;
+
+ chained_irq_enter(chip, desc);
+
+ cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) &
+ readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
+ cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) &
+ readl_relaxed(mvebu_gpioreg_edge_mask(mvchip));
+
+ for (i = 0; i < mvchip->chip.ngpio; i++) {
+ int irq;
+
+ irq = mvchip->irqbase + i;
+
+ if (!(cause & (1 << i)))
+ continue;
+
+ type = irq_get_trigger_type(irq);
+ if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
+ /* Swap polarity (race with GPIO line) */
+ u32 polarity;
+
+ polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ polarity ^= 1 << i;
+ writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip));
+ }
+
+ generic_handle_irq(irq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk;
+ int i;
+
+ out = readl_relaxed(mvebu_gpioreg_out(mvchip));
+ io_conf = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
+ blink = readl_relaxed(mvebu_gpioreg_blink(mvchip));
+ in_pol = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ data_in = readl_relaxed(mvebu_gpioreg_data_in(mvchip));
+ cause = readl_relaxed(mvebu_gpioreg_edge_cause(mvchip));
+ edg_msk = readl_relaxed(mvebu_gpioreg_edge_mask(mvchip));
+ lvl_msk = readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
+
+ for (i = 0; i < chip->ngpio; i++) {
+ const char *label;
+ u32 msk;
+ bool is_out;
+
+ label = gpiochip_is_requested(chip, i);
+ if (!label)
+ continue;
+
+ msk = 1 << i;
+ is_out = !(io_conf & msk);
+
+ seq_printf(s, " gpio-%-3d (%-20.20s)", chip->base + i, label);
+
+ if (is_out) {
+ seq_printf(s, " out %s %s\n",
+ out & msk ? "hi" : "lo",
+ blink & msk ? "(blink )" : "");
+ continue;
+ }
+
+ seq_printf(s, " in %s (act %s) - IRQ",
+ (data_in ^ in_pol) & msk ? "hi" : "lo",
+ in_pol & msk ? "lo" : "hi");
+ if (!((edg_msk | lvl_msk) & msk)) {
+ seq_puts(s, " disabled\n");
+ continue;
+ }
+ if (edg_msk & msk)
+ seq_puts(s, " edge ");
+ if (lvl_msk & msk)
+ seq_puts(s, " level");
+ seq_printf(s, " (%s)\n", cause & msk ? "pending" : "clear ");
+ }
+}
+#else
+#define mvebu_gpio_dbg_show NULL
+#endif
+
+static const struct of_device_id mvebu_gpio_of_match[] = {
+ {
+ .compatible = "marvell,orion-gpio",
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
+ },
+ {
+ .compatible = "marvell,mv78200-gpio",
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_MV78200,
+ },
+ {
+ .compatible = "marvell,armadaxp-gpio",
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
+ },
+ {
+ /* sentinel */
+ },
+};
+MODULE_DEVICE_TABLE(of, mvebu_gpio_of_match);
+
+static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
+ int i;
+
+ mvchip->out_reg = readl(mvebu_gpioreg_out(mvchip));
+ mvchip->io_conf_reg = readl(mvebu_gpioreg_io_conf(mvchip));
+ mvchip->blink_en_reg = readl(mvebu_gpioreg_blink(mvchip));
+ mvchip->in_pol_reg = readl(mvebu_gpioreg_in_pol(mvchip));
+
+ switch (mvchip->soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ mvchip->edge_mask_regs[0] =
+ readl(mvchip->membase + GPIO_EDGE_MASK_OFF);
+ mvchip->level_mask_regs[0] =
+ readl(mvchip->membase + GPIO_LEVEL_MASK_OFF);
+ break;
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ for (i = 0; i < 2; i++) {
+ mvchip->edge_mask_regs[i] =
+ readl(mvchip->membase +
+ GPIO_EDGE_MASK_MV78200_OFF(i));
+ mvchip->level_mask_regs[i] =
+ readl(mvchip->membase +
+ GPIO_LEVEL_MASK_MV78200_OFF(i));
+ }
+ break;
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ for (i = 0; i < 4; i++) {
+ mvchip->edge_mask_regs[i] =
+ readl(mvchip->membase +
+ GPIO_EDGE_MASK_ARMADAXP_OFF(i));
+ mvchip->level_mask_regs[i] =
+ readl(mvchip->membase +
+ GPIO_LEVEL_MASK_ARMADAXP_OFF(i));
+ }
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+static int mvebu_gpio_resume(struct platform_device *pdev)
+{
+ struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
+ int i;
+
+ writel(mvchip->out_reg, mvebu_gpioreg_out(mvchip));
+ writel(mvchip->io_conf_reg, mvebu_gpioreg_io_conf(mvchip));
+ writel(mvchip->blink_en_reg, mvebu_gpioreg_blink(mvchip));
+ writel(mvchip->in_pol_reg, mvebu_gpioreg_in_pol(mvchip));
+
+ switch (mvchip->soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ writel(mvchip->edge_mask_regs[0],
+ mvchip->membase + GPIO_EDGE_MASK_OFF);
+ writel(mvchip->level_mask_regs[0],
+ mvchip->membase + GPIO_LEVEL_MASK_OFF);
+ break;
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ for (i = 0; i < 2; i++) {
+ writel(mvchip->edge_mask_regs[i],
+ mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(i));
+ writel(mvchip->level_mask_regs[i],
+ mvchip->membase +
+ GPIO_LEVEL_MASK_MV78200_OFF(i));
+ }
+ break;
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ for (i = 0; i < 4; i++) {
+ writel(mvchip->edge_mask_regs[i],
+ mvchip->membase +
+ GPIO_EDGE_MASK_ARMADAXP_OFF(i));
+ writel(mvchip->level_mask_regs[i],
+ mvchip->membase +
+ GPIO_LEVEL_MASK_ARMADAXP_OFF(i));
+ }
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+static int mvebu_gpio_probe(struct platform_device *pdev)
+{
+ struct mvebu_gpio_chip *mvchip;
+ const struct of_device_id *match;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+ struct clk *clk;
+ unsigned int ngpios;
+ int soc_variant;
+ int i, cpu, id;
+ int err;
+
+ match = of_match_device(mvebu_gpio_of_match, &pdev->dev);
+ if (match)
+ soc_variant = (int) match->data;
+ else
+ soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
+
+ mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip),
+ GFP_KERNEL);
+ if (!mvchip)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mvchip);
+
+ if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) {
+ dev_err(&pdev->dev, "Missing ngpios OF property\n");
+ return -ENODEV;
+ }
+
+ id = of_alias_get_id(pdev->dev.of_node, "gpio");
+ if (id < 0) {
+ dev_err(&pdev->dev, "Couldn't get OF id\n");
+ return id;
+ }
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ /* Not all SoCs require a clock.*/
+ if (!IS_ERR(clk))
+ clk_prepare_enable(clk);
+
+ mvchip->soc_variant = soc_variant;
+ mvchip->chip.label = dev_name(&pdev->dev);
+ mvchip->chip.dev = &pdev->dev;
+ mvchip->chip.request = mvebu_gpio_request;
+ mvchip->chip.free = mvebu_gpio_free;
+ mvchip->chip.direction_input = mvebu_gpio_direction_input;
+ mvchip->chip.get = mvebu_gpio_get;
+ mvchip->chip.direction_output = mvebu_gpio_direction_output;
+ mvchip->chip.set = mvebu_gpio_set;
+ mvchip->chip.to_irq = mvebu_gpio_to_irq;
+ 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;
+
+ spin_lock_init(&mvchip->lock);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mvchip->membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mvchip->membase))
+ return PTR_ERR(mvchip->membase);
+
+ /* The Armada XP has a second range of registers for the
+ * per-CPU registers */
+ if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ mvchip->percpu_membase = devm_ioremap_resource(&pdev->dev,
+ res);
+ if (IS_ERR(mvchip->percpu_membase))
+ return PTR_ERR(mvchip->percpu_membase);
+ }
+
+ /*
+ * Mask and clear GPIO interrupts.
+ */
+ switch (soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
+ writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
+ break;
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
+ for (cpu = 0; cpu < 2; cpu++) {
+ writel_relaxed(0, mvchip->membase +
+ GPIO_EDGE_MASK_MV78200_OFF(cpu));
+ writel_relaxed(0, mvchip->membase +
+ GPIO_LEVEL_MASK_MV78200_OFF(cpu));
+ }
+ break;
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
+ writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
+ for (cpu = 0; cpu < 4; cpu++) {
+ writel_relaxed(0, mvchip->percpu_membase +
+ GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu));
+ writel_relaxed(0, mvchip->percpu_membase +
+ GPIO_EDGE_MASK_ARMADAXP_OFF(cpu));
+ writel_relaxed(0, mvchip->percpu_membase +
+ GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu));
+ }
+ break;
+ default:
+ BUG();
+ }
+
+ gpiochip_add(&mvchip->chip);
+
+ /* Some gpio controllers do not provide irq support */
+ if (!of_irq_count(np))
+ return 0;
+
+ /* Setup the interrupt handlers. Each chip can have up to 4
+ * interrupt handlers, with each handler dealing with 8 GPIO
+ * pins. */
+ for (i = 0; i < 4; i++) {
+ int irq = platform_get_irq(pdev, i);
+
+ if (irq < 0)
+ continue;
+ irq_set_handler_data(irq, mvchip);
+ irq_set_chained_handler(irq, mvebu_gpio_irq_handler);
+ }
+
+ mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
+ if (mvchip->irqbase < 0) {
+ dev_err(&pdev->dev, "no irqs\n");
+ err = mvchip->irqbase;
+ goto err_gpiochip_add;
+ }
+
+ gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
+ mvchip->membase, handle_level_irq);
+ if (!gc) {
+ dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
+ err = -ENOMEM;
+ goto err_gpiochip_add;
+ }
+
+ gc->private = mvchip;
+ ct = &gc->chip_types[0];
+ ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
+ ct->chip.irq_mask = mvebu_gpio_level_irq_mask;
+ ct->chip.irq_unmask = mvebu_gpio_level_irq_unmask;
+ ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
+ ct->chip.name = mvchip->chip.label;
+
+ ct = &gc->chip_types[1];
+ ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ ct->chip.irq_ack = mvebu_gpio_irq_ack;
+ ct->chip.irq_mask = mvebu_gpio_edge_irq_mask;
+ ct->chip.irq_unmask = mvebu_gpio_edge_irq_unmask;
+ ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
+ ct->handler = handle_edge_irq;
+ ct->chip.name = mvchip->chip.label;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(ngpios), 0,
+ IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
+
+ /* Setup irq domain on top of the generic chip. */
+ mvchip->domain = irq_domain_add_simple(np, mvchip->chip.ngpio,
+ mvchip->irqbase,
+ &irq_domain_simple_ops,
+ mvchip);
+ if (!mvchip->domain) {
+ dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
+ mvchip->chip.label);
+ err = -ENODEV;
+ goto err_generic_chip;
+ }
+
+ return 0;
+
+err_generic_chip:
+ irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
+ IRQ_LEVEL | IRQ_NOPROBE);
+ kfree(gc);
+
+err_gpiochip_add:
+ gpiochip_remove(&mvchip->chip);
+
+ return err;
+}
+
+static struct platform_driver mvebu_gpio_driver = {
+ .driver = {
+ .name = "mvebu-gpio",
+ .of_match_table = mvebu_gpio_of_match,
+ },
+ .probe = mvebu_gpio_probe,
+ .suspend = mvebu_gpio_suspend,
+ .resume = mvebu_gpio_resume,
+};
+module_platform_driver(mvebu_gpio_driver);
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
new file mode 100644
index 000000000..9f7446a7a
--- /dev/null
+++ b/drivers/gpio/gpio-mxc.c
@@ -0,0 +1,515 @@
+/*
+ * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ *
+ * Based on code from Freescale,
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <asm-generic/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;
+ unsigned gdir_reg;
+ unsigned psr_reg;
+ unsigned icr1_reg;
+ unsigned icr2_reg;
+ unsigned imr_reg;
+ unsigned isr_reg;
+ int edge_sel_reg;
+ unsigned low_level;
+ unsigned high_level;
+ unsigned rise_edge;
+ unsigned fall_edge;
+};
+
+struct mxc_gpio_port {
+ struct list_head node;
+ void __iomem *base;
+ int irq;
+ int irq_high;
+ struct irq_domain *domain;
+ struct bgpio_chip bgc;
+ u32 both_edges;
+};
+
+static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
+ .dr_reg = 0x1c,
+ .gdir_reg = 0x00,
+ .psr_reg = 0x24,
+ .icr1_reg = 0x28,
+ .icr2_reg = 0x2c,
+ .imr_reg = 0x30,
+ .isr_reg = 0x34,
+ .edge_sel_reg = -EINVAL,
+ .low_level = 0x03,
+ .high_level = 0x02,
+ .rise_edge = 0x00,
+ .fall_edge = 0x01,
+};
+
+static struct mxc_gpio_hwdata imx31_gpio_hwdata = {
+ .dr_reg = 0x00,
+ .gdir_reg = 0x04,
+ .psr_reg = 0x08,
+ .icr1_reg = 0x0c,
+ .icr2_reg = 0x10,
+ .imr_reg = 0x14,
+ .isr_reg = 0x18,
+ .edge_sel_reg = -EINVAL,
+ .low_level = 0x00,
+ .high_level = 0x01,
+ .rise_edge = 0x02,
+ .fall_edge = 0x03,
+};
+
+static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
+ .dr_reg = 0x00,
+ .gdir_reg = 0x04,
+ .psr_reg = 0x08,
+ .icr1_reg = 0x0c,
+ .icr2_reg = 0x10,
+ .imr_reg = 0x14,
+ .isr_reg = 0x18,
+ .edge_sel_reg = 0x1c,
+ .low_level = 0x00,
+ .high_level = 0x01,
+ .rise_edge = 0x02,
+ .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_INT_BOTH_EDGES 0x4
+
+static 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], },
+ { /* sentinel */ }
+};
+
+/*
+ * MX2 has one interrupt *for all* gpio ports. The list is used
+ * to save the references to all ports, so that mx2_gpio_irq_handler
+ * can walk through all interrupt status registers.
+ */
+static LIST_HEAD(mxc_gpio_ports);
+
+/* Note: This driver assumes 32 GPIOs are handled in one register */
+
+static int gpio_set_irq_type(struct irq_data *d, u32 type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxc_gpio_port *port = gc->private;
+ u32 bit, val;
+ u32 gpio_idx = d->hwirq;
+ u32 gpio = port->bgc.gc.base + gpio_idx;
+ int edge;
+ void __iomem *reg = port->base;
+
+ port->both_edges &= ~(1 << gpio_idx);
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ edge = GPIO_INT_RISE_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ edge = GPIO_INT_FALL_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ if (GPIO_EDGE_SEL >= 0) {
+ edge = GPIO_INT_BOTH_EDGES;
+ } else {
+ val = gpio_get_value(gpio);
+ if (val) {
+ edge = GPIO_INT_LOW_LEV;
+ pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
+ } else {
+ edge = GPIO_INT_HIGH_LEV;
+ pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
+ }
+ port->both_edges |= 1 << gpio_idx;
+ }
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ edge = GPIO_INT_LOW_LEV;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ edge = GPIO_INT_HIGH_LEV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (GPIO_EDGE_SEL >= 0) {
+ val = readl(port->base + GPIO_EDGE_SEL);
+ if (edge == GPIO_INT_BOTH_EDGES)
+ writel(val | (1 << gpio_idx),
+ port->base + GPIO_EDGE_SEL);
+ else
+ writel(val & ~(1 << gpio_idx),
+ port->base + GPIO_EDGE_SEL);
+ }
+
+ if (edge != GPIO_INT_BOTH_EDGES) {
+ reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
+ bit = gpio_idx & 0xf;
+ val = readl(reg) & ~(0x3 << (bit << 1));
+ writel(val | (edge << (bit << 1)), reg);
+ }
+
+ writel(1 << gpio_idx, port->base + GPIO_ISR);
+
+ return 0;
+}
+
+static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
+{
+ void __iomem *reg = port->base;
+ u32 bit, val;
+ int edge;
+
+ reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
+ bit = gpio & 0xf;
+ val = readl(reg);
+ edge = (val >> (bit << 1)) & 3;
+ val &= ~(0x3 << (bit << 1));
+ if (edge == GPIO_INT_HIGH_LEV) {
+ edge = GPIO_INT_LOW_LEV;
+ pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
+ } else if (edge == GPIO_INT_LOW_LEV) {
+ edge = GPIO_INT_HIGH_LEV;
+ pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
+ } else {
+ pr_err("mxc: invalid configuration for GPIO %d: %x\n",
+ gpio, edge);
+ return;
+ }
+ writel(val | (edge << (bit << 1)), reg);
+}
+
+/* handle 32 interrupts in one status register */
+static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
+{
+ while (irq_stat != 0) {
+ int irqoffset = fls(irq_stat) - 1;
+
+ if (port->both_edges & (1 << irqoffset))
+ mxc_flip_edge(port, irqoffset);
+
+ generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
+
+ irq_stat &= ~(1 << irqoffset);
+ }
+}
+
+/* MX1 and MX3 has one interrupt *per* gpio port */
+static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 irq_stat;
+ struct mxc_gpio_port *port = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
+
+ mxc_gpio_irq_handler(port, irq_stat);
+
+ chained_irq_exit(chip, desc);
+}
+
+/* MX2 has one interrupt *for all* gpio ports */
+static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 irq_msk, irq_stat;
+ struct mxc_gpio_port *port;
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ /* walk through all interrupt status registers */
+ list_for_each_entry(port, &mxc_gpio_ports, node) {
+ irq_msk = readl(port->base + GPIO_IMR);
+ if (!irq_msk)
+ continue;
+
+ irq_stat = readl(port->base + GPIO_ISR) & irq_msk;
+ if (irq_stat)
+ mxc_gpio_irq_handler(port, irq_stat);
+ }
+ chained_irq_exit(chip, desc);
+}
+
+/*
+ * Set interrupt number "irq" in the GPIO as a wake-up source.
+ * While system is running, all registered GPIO interrupts need to have
+ * wake-up enabled. When system is suspended, only selected GPIO interrupts
+ * need to have wake-up enabled.
+ * @param irq interrupt source number
+ * @param enable enable as wake-up if equal to non-zero
+ * @return This function returns 0 on success.
+ */
+static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxc_gpio_port *port = gc->private;
+ u32 gpio_idx = d->hwirq;
+
+ if (enable) {
+ if (port->irq_high && (gpio_idx >= 16))
+ enable_irq_wake(port->irq_high);
+ else
+ enable_irq_wake(port->irq);
+ } else {
+ if (port->irq_high && (gpio_idx >= 16))
+ disable_irq_wake(port->irq_high);
+ else
+ disable_irq_wake(port->irq);
+ }
+
+ return 0;
+}
+
+static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("gpio-mxc", 1, irq_base,
+ port->base, handle_level_irq);
+ gc->private = port;
+
+ ct = gc->chip_types;
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
+ ct->chip.irq_mask = irq_gc_mask_clr_bit;
+ 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->regs.ack = GPIO_ISR;
+ ct->regs.mask = GPIO_IMR;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK,
+ IRQ_NOREQUEST, 0);
+}
+
+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 bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct mxc_gpio_port *port =
+ container_of(bgc, struct mxc_gpio_port, bgc);
+
+ return irq_find_mapping(port->domain, offset);
+}
+
+static int mxc_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct mxc_gpio_port *port;
+ struct resource *iores;
+ int irq_base;
+ int err;
+
+ mxc_gpio_get_hw(pdev);
+
+ port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ port->base = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(port->base))
+ return PTR_ERR(port->base);
+
+ port->irq_high = platform_get_irq(pdev, 1);
+ port->irq = platform_get_irq(pdev, 0);
+ if (port->irq < 0)
+ return port->irq;
+
+ /* disable the interrupt and clear the status */
+ writel(0, port->base + GPIO_IMR);
+ writel(~0, port->base + GPIO_ISR);
+
+ if (mxc_gpio_hwtype == IMX21_GPIO) {
+ /*
+ * Setup one handler for all GPIO interrupts. Actually setting
+ * the handler is needed only once, but doing it for every port
+ * is more robust and easier.
+ */
+ irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
+ } else {
+ /* setup one handler for each entry */
+ irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
+ irq_set_handler_data(port->irq, port);
+ if (port->irq_high > 0) {
+ /* setup handler for GPIO 16 to 31 */
+ irq_set_chained_handler(port->irq_high,
+ mx3_gpio_irq_handler);
+ irq_set_handler_data(port->irq_high, port);
+ }
+ }
+
+ err = bgpio_init(&port->bgc, &pdev->dev, 4,
+ port->base + GPIO_PSR,
+ port->base + GPIO_DR, NULL,
+ port->base + GPIO_GDIR, NULL, 0);
+ if (err)
+ goto out_bgio;
+
+ port->bgc.gc.to_irq = mxc_gpio_to_irq;
+ port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
+ pdev->id * 32;
+
+ err = gpiochip_add(&port->bgc.gc);
+ if (err)
+ goto out_bgpio_remove;
+
+ irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
+ if (irq_base < 0) {
+ err = irq_base;
+ goto out_gpiochip_remove;
+ }
+
+ port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+ if (!port->domain) {
+ err = -ENODEV;
+ goto out_irqdesc_free;
+ }
+
+ /* gpio-mxc can be a generic irq chip */
+ mxc_gpio_init_gc(port, irq_base);
+
+ list_add_tail(&port->node, &mxc_gpio_ports);
+
+ return 0;
+
+out_irqdesc_free:
+ irq_free_descs(irq_base, 32);
+out_gpiochip_remove:
+ gpiochip_remove(&port->bgc.gc);
+out_bgpio_remove:
+ bgpio_remove(&port->bgc);
+out_bgio:
+ dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
+ return err;
+}
+
+static struct platform_driver mxc_gpio_driver = {
+ .driver = {
+ .name = "gpio-mxc",
+ .of_match_table = mxc_gpio_dt_ids,
+ },
+ .probe = mxc_gpio_probe,
+ .id_table = mxc_gpio_devtype,
+};
+
+static int __init gpio_mxc_init(void)
+{
+ return platform_driver_register(&mxc_gpio_driver);
+}
+postcore_initcall(gpio_mxc_init);
+
+MODULE_AUTHOR("Freescale Semiconductor, "
+ "Daniel Mack <danielncaiaq.de>, "
+ "Juergen Beisert <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("Freescale MXC GPIO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
new file mode 100644
index 000000000..84cbda6ac
--- /dev/null
+++ b/drivers/gpio/gpio-mxs.c
@@ -0,0 +1,370 @@
+/*
+ * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ *
+ * Based on code from Freescale,
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/module.h>
+
+#define MXS_SET 0x4
+#define MXS_CLR 0x8
+
+#define PINCTRL_DOUT(p) ((is_imx23_gpio(p) ? 0x0500 : 0x0700) + (p->id) * 0x10)
+#define PINCTRL_DIN(p) ((is_imx23_gpio(p) ? 0x0600 : 0x0900) + (p->id) * 0x10)
+#define PINCTRL_DOE(p) ((is_imx23_gpio(p) ? 0x0700 : 0x0b00) + (p->id) * 0x10)
+#define PINCTRL_PIN2IRQ(p) ((is_imx23_gpio(p) ? 0x0800 : 0x1000) + (p->id) * 0x10)
+#define PINCTRL_IRQEN(p) ((is_imx23_gpio(p) ? 0x0900 : 0x1100) + (p->id) * 0x10)
+#define PINCTRL_IRQLEV(p) ((is_imx23_gpio(p) ? 0x0a00 : 0x1200) + (p->id) * 0x10)
+#define PINCTRL_IRQPOL(p) ((is_imx23_gpio(p) ? 0x0b00 : 0x1300) + (p->id) * 0x10)
+#define PINCTRL_IRQSTAT(p) ((is_imx23_gpio(p) ? 0x0c00 : 0x1400) + (p->id) * 0x10)
+
+#define GPIO_INT_FALL_EDGE 0x0
+#define GPIO_INT_LOW_LEV 0x1
+#define GPIO_INT_RISE_EDGE 0x2
+#define GPIO_INT_HIGH_LEV 0x3
+#define GPIO_INT_LEV_MASK (1 << 0)
+#define GPIO_INT_POL_MASK (1 << 1)
+
+enum mxs_gpio_id {
+ IMX23_GPIO,
+ IMX28_GPIO,
+};
+
+struct mxs_gpio_port {
+ void __iomem *base;
+ int id;
+ int irq;
+ struct irq_domain *domain;
+ struct bgpio_chip bgc;
+ enum mxs_gpio_id devid;
+ u32 both_edges;
+};
+
+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)
+{
+ u32 val;
+ u32 pin_mask = 1 << d->hwirq;
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxs_gpio_port *port = gc->private;
+ void __iomem *pin_addr;
+ int edge;
+
+ port->both_edges &= ~pin_mask;
+ switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ val = gpio_get_value(port->bgc.gc.base + d->hwirq);
+ if (val)
+ edge = GPIO_INT_FALL_EDGE;
+ else
+ edge = GPIO_INT_RISE_EDGE;
+ port->both_edges |= pin_mask;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ edge = GPIO_INT_RISE_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ edge = GPIO_INT_FALL_EDGE;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ edge = GPIO_INT_LOW_LEV;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ edge = GPIO_INT_HIGH_LEV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set level or edge */
+ pin_addr = port->base + PINCTRL_IRQLEV(port);
+ if (edge & GPIO_INT_LEV_MASK)
+ writel(pin_mask, pin_addr + MXS_SET);
+ else
+ writel(pin_mask, pin_addr + MXS_CLR);
+
+ /* set polarity */
+ pin_addr = port->base + PINCTRL_IRQPOL(port);
+ if (edge & GPIO_INT_POL_MASK)
+ writel(pin_mask, pin_addr + MXS_SET);
+ else
+ writel(pin_mask, pin_addr + MXS_CLR);
+
+ writel(pin_mask,
+ port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
+
+ return 0;
+}
+
+static void mxs_flip_edge(struct mxs_gpio_port *port, u32 gpio)
+{
+ u32 bit, val, edge;
+ void __iomem *pin_addr;
+
+ bit = 1 << gpio;
+
+ pin_addr = port->base + PINCTRL_IRQPOL(port);
+ val = readl(pin_addr);
+ edge = val & bit;
+
+ if (edge)
+ writel(bit, pin_addr + MXS_CLR);
+ else
+ writel(bit, pin_addr + MXS_SET);
+}
+
+/* MXS has one interrupt *per* gpio port */
+static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 irq_stat;
+ struct mxs_gpio_port *port = irq_get_handler_data(irq);
+
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+
+ irq_stat = readl(port->base + PINCTRL_IRQSTAT(port)) &
+ readl(port->base + PINCTRL_IRQEN(port));
+
+ while (irq_stat != 0) {
+ int irqoffset = fls(irq_stat) - 1;
+ if (port->both_edges & (1 << irqoffset))
+ mxs_flip_edge(port, irqoffset);
+
+ generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
+ irq_stat &= ~(1 << irqoffset);
+ }
+}
+
+/*
+ * Set interrupt number "irq" in the GPIO as a wake-up source.
+ * While system is running, all registered GPIO interrupts need to have
+ * wake-up enabled. When system is suspended, only selected GPIO interrupts
+ * need to have wake-up enabled.
+ * @param irq interrupt source number
+ * @param enable enable as wake-up if equal to non-zero
+ * @return This function returns 0 on success.
+ */
+static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxs_gpio_port *port = gc->private;
+
+ if (enable)
+ enable_irq_wake(port->irq);
+ else
+ disable_irq_wake(port->irq);
+
+ return 0;
+}
+
+static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("gpio-mxs", 1, irq_base,
+ port->base, handle_level_irq);
+ gc->private = port;
+
+ ct = gc->chip_types;
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
+ ct->chip.irq_mask = irq_gc_mask_clr_bit;
+ ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->chip.irq_set_type = mxs_gpio_set_irq_type;
+ ct->chip.irq_set_wake = mxs_gpio_set_wake_irq;
+ ct->regs.ack = PINCTRL_IRQSTAT(port) + MXS_CLR;
+ ct->regs.mask = PINCTRL_IRQEN(port);
+
+ irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK,
+ IRQ_NOREQUEST, 0);
+}
+
+static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct mxs_gpio_port *port =
+ container_of(bgc, struct mxs_gpio_port, bgc);
+
+ return irq_find_mapping(port->domain, offset);
+}
+
+static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct mxs_gpio_port *port =
+ container_of(bgc, struct mxs_gpio_port, bgc);
+ u32 mask = 1 << offset;
+ u32 dir;
+
+ dir = readl(port->base + PINCTRL_DOE(port));
+ return !(dir & mask);
+}
+
+static 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);
+
+static const struct of_device_id mxs_gpio_dt_ids[] = {
+ { .compatible = "fsl,imx23-gpio", .data = (void *) IMX23_GPIO, },
+ { .compatible = "fsl,imx28-gpio", .data = (void *) IMX28_GPIO, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_gpio_dt_ids);
+
+static int mxs_gpio_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(mxs_gpio_dt_ids, &pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *parent;
+ static void __iomem *base;
+ struct mxs_gpio_port *port;
+ int irq_base;
+ int err;
+
+ port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->id = of_alias_get_id(np, "gpio");
+ if (port->id < 0)
+ return port->id;
+ port->devid = (enum mxs_gpio_id) of_id->data;
+ port->irq = platform_get_irq(pdev, 0);
+ if (port->irq < 0)
+ return port->irq;
+
+ /*
+ * map memory region only once, as all the gpio ports
+ * share the same one
+ */
+ if (!base) {
+ parent = of_get_parent(np);
+ base = of_iomap(parent, 0);
+ of_node_put(parent);
+ if (!base)
+ return -EADDRNOTAVAIL;
+ }
+ port->base = base;
+
+ /*
+ * select the pin interrupt functionality but initially
+ * disable the interrupts
+ */
+ writel(~0U, port->base + PINCTRL_PIN2IRQ(port));
+ writel(0, port->base + PINCTRL_IRQEN(port));
+
+ /* clear address has to be used to clear IRQSTAT bits */
+ writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
+
+ irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
+ if (irq_base < 0)
+ return irq_base;
+
+ port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+ if (!port->domain) {
+ err = -ENODEV;
+ goto out_irqdesc_free;
+ }
+
+ /* gpio-mxs can be a generic irq chip */
+ mxs_gpio_init_gc(port, irq_base);
+
+ /* setup one handler for each entry */
+ irq_set_chained_handler(port->irq, mxs_gpio_irq_handler);
+ irq_set_handler_data(port->irq, port);
+
+ err = bgpio_init(&port->bgc, &pdev->dev, 4,
+ port->base + PINCTRL_DIN(port),
+ port->base + PINCTRL_DOUT(port) + MXS_SET,
+ port->base + PINCTRL_DOUT(port) + MXS_CLR,
+ port->base + PINCTRL_DOE(port), NULL, 0);
+ if (err)
+ goto out_irqdesc_free;
+
+ port->bgc.gc.to_irq = mxs_gpio_to_irq;
+ port->bgc.gc.get_direction = mxs_gpio_get_direction;
+ port->bgc.gc.base = port->id * 32;
+
+ err = gpiochip_add(&port->bgc.gc);
+ if (err)
+ goto out_bgpio_remove;
+
+ return 0;
+
+out_bgpio_remove:
+ bgpio_remove(&port->bgc);
+out_irqdesc_free:
+ irq_free_descs(irq_base, 32);
+ return err;
+}
+
+static struct platform_driver mxs_gpio_driver = {
+ .driver = {
+ .name = "gpio-mxs",
+ .of_match_table = mxs_gpio_dt_ids,
+ },
+ .probe = mxs_gpio_probe,
+ .id_table = mxs_gpio_ids,
+};
+
+static int __init mxs_gpio_init(void)
+{
+ return platform_driver_register(&mxs_gpio_driver);
+}
+postcore_initcall(mxs_gpio_init);
+
+MODULE_AUTHOR("Freescale Semiconductor, "
+ "Daniel Mack <danielncaiaq.de>, "
+ "Juergen Beisert <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("Freescale MXS GPIO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-octeon.c b/drivers/gpio/gpio-octeon.c
new file mode 100644
index 000000000..62ae251d4
--- /dev/null
+++ b/drivers/gpio/gpio-octeon.c
@@ -0,0 +1,157 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2011, 2012 Cavium Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <asm/octeon/octeon.h>
+#include <asm/octeon/cvmx-gpio-defs.h>
+
+#define RX_DAT 0x80
+#define TX_SET 0x88
+#define TX_CLEAR 0x90
+/*
+ * The address offset of the GPIO configuration register for a given
+ * line.
+ */
+static unsigned int bit_cfg_reg(unsigned int offset)
+{
+ /*
+ * The register stride is 8, with a discontinuity after the
+ * first 16.
+ */
+ if (offset < 16)
+ return 8 * offset;
+ else
+ return 8 * (offset - 16) + 0x100;
+}
+
+struct octeon_gpio {
+ struct gpio_chip chip;
+ u64 register_base;
+};
+
+static int octeon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct octeon_gpio *gpio = container_of(chip, struct octeon_gpio, chip);
+
+ cvmx_write_csr(gpio->register_base + bit_cfg_reg(offset), 0);
+ return 0;
+}
+
+static void octeon_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct octeon_gpio *gpio = container_of(chip, struct octeon_gpio, chip);
+ u64 mask = 1ull << offset;
+ u64 reg = gpio->register_base + (value ? TX_SET : TX_CLEAR);
+ cvmx_write_csr(reg, mask);
+}
+
+static int octeon_gpio_dir_out(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct octeon_gpio *gpio = container_of(chip, struct octeon_gpio, chip);
+ union cvmx_gpio_bit_cfgx cfgx;
+
+ octeon_gpio_set(chip, offset, value);
+
+ cfgx.u64 = 0;
+ cfgx.s.tx_oe = 1;
+
+ cvmx_write_csr(gpio->register_base + bit_cfg_reg(offset), cfgx.u64);
+ return 0;
+}
+
+static int octeon_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct octeon_gpio *gpio = container_of(chip, struct octeon_gpio, chip);
+ u64 read_bits = cvmx_read_csr(gpio->register_base + RX_DAT);
+
+ return ((1ull << offset) & read_bits) != 0;
+}
+
+static int octeon_gpio_probe(struct platform_device *pdev)
+{
+ struct octeon_gpio *gpio;
+ struct gpio_chip *chip;
+ struct resource *res_mem;
+ int err = 0;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+ chip = &gpio->chip;
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res_mem == NULL) {
+ dev_err(&pdev->dev, "found no memory resource\n");
+ err = -ENXIO;
+ goto out;
+ }
+ if (!devm_request_mem_region(&pdev->dev, res_mem->start,
+ resource_size(res_mem),
+ res_mem->name)) {
+ dev_err(&pdev->dev, "request_mem_region failed\n");
+ err = -ENXIO;
+ goto out;
+ }
+ gpio->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start,
+ resource_size(res_mem));
+
+ pdev->dev.platform_data = chip;
+ chip->label = "octeon-gpio";
+ chip->dev = &pdev->dev;
+ chip->owner = THIS_MODULE;
+ chip->base = 0;
+ chip->can_sleep = false;
+ chip->ngpio = 20;
+ chip->direction_input = octeon_gpio_dir_in;
+ chip->get = octeon_gpio_get;
+ chip->direction_output = octeon_gpio_dir_out;
+ chip->set = octeon_gpio_set;
+ err = gpiochip_add(chip);
+ if (err)
+ goto out;
+
+ dev_info(&pdev->dev, "OCTEON GPIO driver probed.\n");
+out:
+ return err;
+}
+
+static int octeon_gpio_remove(struct platform_device *pdev)
+{
+ struct gpio_chip *chip = pdev->dev.platform_data;
+ gpiochip_remove(chip);
+ return 0;
+}
+
+static struct of_device_id octeon_gpio_match[] = {
+ {
+ .compatible = "cavium,octeon-3860-gpio",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, octeon_gpio_match);
+
+static struct platform_driver octeon_gpio_driver = {
+ .driver = {
+ .name = "octeon_gpio",
+ .of_match_table = octeon_gpio_match,
+ },
+ .probe = octeon_gpio_probe,
+ .remove = octeon_gpio_remove,
+};
+
+module_platform_driver(octeon_gpio_driver);
+
+MODULE_DESCRIPTION("Cavium Inc. OCTEON GPIO Driver");
+MODULE_AUTHOR("David Daney");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
new file mode 100644
index 000000000..b232397ad
--- /dev/null
+++ b/drivers/gpio/gpio-omap.c
@@ -0,0 +1,1617 @@
+/*
+ * Support functions for OMAP GPIO
+ *
+ * Copyright (C) 2003-2005 Nokia Corporation
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * Copyright (C) 2009 Texas Instruments
+ * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/syscore_ops.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/gpio.h>
+#include <linux/bitops.h>
+#include <linux/platform_data/gpio-omap.h>
+
+#define OFF_MODE 1
+
+static LIST_HEAD(omap_gpio_list);
+
+struct gpio_regs {
+ u32 irqenable1;
+ u32 irqenable2;
+ u32 wake_en;
+ u32 ctrl;
+ u32 oe;
+ u32 leveldetect0;
+ u32 leveldetect1;
+ u32 risingdetect;
+ u32 fallingdetect;
+ u32 dataout;
+ u32 debounce;
+ u32 debounce_en;
+};
+
+struct gpio_bank {
+ struct list_head node;
+ void __iomem *base;
+ u16 irq;
+ u32 non_wakeup_gpios;
+ u32 enabled_non_wakeup_gpios;
+ struct gpio_regs context;
+ u32 saved_datain;
+ u32 level_mask;
+ u32 toggle_mask;
+ spinlock_t lock;
+ struct gpio_chip chip;
+ struct clk *dbck;
+ u32 mod_usage;
+ u32 irq_usage;
+ u32 dbck_enable_mask;
+ bool dbck_enabled;
+ struct device *dev;
+ bool is_mpuio;
+ bool dbck_flag;
+ bool loses_context;
+ bool context_valid;
+ int stride;
+ u32 width;
+ int context_loss_count;
+ int power_mode;
+ bool workaround_enabled;
+
+ void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
+ int (*get_context_loss_count)(struct device *dev);
+
+ struct omap_gpio_reg_offs *regs;
+};
+
+#define GPIO_MOD_CTRL_BIT BIT(0)
+
+#define BANK_USED(bank) (bank->mod_usage || bank->irq_usage)
+#define LINE_USED(line, offset) (line & (BIT(offset)))
+
+static void omap_gpio_unmask_irq(struct irq_data *d);
+
+static inline struct gpio_bank *omap_irq_data_get_bank(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ return container_of(chip, struct gpio_bank, chip);
+}
+
+static void omap_set_gpio_direction(struct gpio_bank *bank, int gpio,
+ int is_input)
+{
+ void __iomem *reg = bank->base;
+ u32 l;
+
+ reg += bank->regs->direction;
+ l = readl_relaxed(reg);
+ if (is_input)
+ l |= BIT(gpio);
+ else
+ l &= ~(BIT(gpio));
+ writel_relaxed(l, reg);
+ bank->context.oe = l;
+}
+
+
+/* set data out value using dedicate set/clear register */
+static void omap_set_gpio_dataout_reg(struct gpio_bank *bank, unsigned offset,
+ int enable)
+{
+ void __iomem *reg = bank->base;
+ u32 l = BIT(offset);
+
+ if (enable) {
+ reg += bank->regs->set_dataout;
+ bank->context.dataout |= l;
+ } else {
+ reg += bank->regs->clr_dataout;
+ bank->context.dataout &= ~l;
+ }
+
+ writel_relaxed(l, reg);
+}
+
+/* set data out value using mask register */
+static void omap_set_gpio_dataout_mask(struct gpio_bank *bank, unsigned offset,
+ int enable)
+{
+ void __iomem *reg = bank->base + bank->regs->dataout;
+ u32 gpio_bit = BIT(offset);
+ u32 l;
+
+ l = readl_relaxed(reg);
+ if (enable)
+ l |= gpio_bit;
+ else
+ l &= ~gpio_bit;
+ writel_relaxed(l, reg);
+ bank->context.dataout = l;
+}
+
+static int omap_get_gpio_datain(struct gpio_bank *bank, int offset)
+{
+ void __iomem *reg = bank->base + bank->regs->datain;
+
+ return (readl_relaxed(reg) & (BIT(offset))) != 0;
+}
+
+static int omap_get_gpio_dataout(struct gpio_bank *bank, int offset)
+{
+ void __iomem *reg = bank->base + bank->regs->dataout;
+
+ return (readl_relaxed(reg) & (BIT(offset))) != 0;
+}
+
+static inline void omap_gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set)
+{
+ int l = readl_relaxed(base + reg);
+
+ if (set)
+ l |= mask;
+ else
+ l &= ~mask;
+
+ writel_relaxed(l, base + reg);
+}
+
+static inline void omap_gpio_dbck_enable(struct gpio_bank *bank)
+{
+ if (bank->dbck_enable_mask && !bank->dbck_enabled) {
+ clk_prepare_enable(bank->dbck);
+ bank->dbck_enabled = true;
+
+ writel_relaxed(bank->dbck_enable_mask,
+ bank->base + bank->regs->debounce_en);
+ }
+}
+
+static inline void omap_gpio_dbck_disable(struct gpio_bank *bank)
+{
+ if (bank->dbck_enable_mask && bank->dbck_enabled) {
+ /*
+ * Disable debounce before cutting it's clock. If debounce is
+ * enabled but the clock is not, GPIO module seems to be unable
+ * to detect events and generate interrupts at least on OMAP3.
+ */
+ writel_relaxed(0, bank->base + bank->regs->debounce_en);
+
+ clk_disable_unprepare(bank->dbck);
+ bank->dbck_enabled = false;
+ }
+}
+
+/**
+ * omap2_set_gpio_debounce - low level gpio debounce time
+ * @bank: the gpio bank we're acting upon
+ * @offset: the gpio number on this @bank
+ * @debounce: debounce time to use
+ *
+ * OMAP's debounce time is in 31us steps so we need
+ * to convert and round up to the closest unit.
+ */
+static void omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
+ unsigned debounce)
+{
+ void __iomem *reg;
+ u32 val;
+ u32 l;
+
+ if (!bank->dbck_flag)
+ return;
+
+ if (debounce < 32)
+ debounce = 0x01;
+ else if (debounce > 7936)
+ debounce = 0xff;
+ else
+ debounce = (debounce / 0x1f) - 1;
+
+ l = BIT(offset);
+
+ clk_prepare_enable(bank->dbck);
+ reg = bank->base + bank->regs->debounce;
+ writel_relaxed(debounce, reg);
+
+ reg = bank->base + bank->regs->debounce_en;
+ val = readl_relaxed(reg);
+
+ if (debounce)
+ val |= l;
+ else
+ val &= ~l;
+ bank->dbck_enable_mask = val;
+
+ writel_relaxed(val, reg);
+ clk_disable_unprepare(bank->dbck);
+ /*
+ * Enable debounce clock per module.
+ * This call is mandatory because in omap_gpio_request() when
+ * *_runtime_get_sync() is called, _gpio_dbck_enable() within
+ * runtime callbck fails to turn on dbck because dbck_enable_mask
+ * used within _gpio_dbck_enable() is still not initialized at
+ * that point. Therefore we have to enable dbck here.
+ */
+ omap_gpio_dbck_enable(bank);
+ if (bank->dbck_enable_mask) {
+ bank->context.debounce = debounce;
+ bank->context.debounce_en = val;
+ }
+}
+
+/**
+ * omap_clear_gpio_debounce - clear debounce settings for a gpio
+ * @bank: the gpio bank we're acting upon
+ * @offset: the gpio number on this @bank
+ *
+ * If a gpio is using debounce, then clear the debounce enable bit and if
+ * this is the only gpio in this bank using debounce, then clear the debounce
+ * time too. The debounce clock will also be disabled when calling this function
+ * if this is the only gpio in the bank using debounce.
+ */
+static void omap_clear_gpio_debounce(struct gpio_bank *bank, unsigned offset)
+{
+ u32 gpio_bit = BIT(offset);
+
+ if (!bank->dbck_flag)
+ return;
+
+ if (!(bank->dbck_enable_mask & gpio_bit))
+ return;
+
+ bank->dbck_enable_mask &= ~gpio_bit;
+ bank->context.debounce_en &= ~gpio_bit;
+ writel_relaxed(bank->context.debounce_en,
+ bank->base + bank->regs->debounce_en);
+
+ if (!bank->dbck_enable_mask) {
+ bank->context.debounce = 0;
+ writel_relaxed(bank->context.debounce, bank->base +
+ bank->regs->debounce);
+ clk_disable_unprepare(bank->dbck);
+ bank->dbck_enabled = false;
+ }
+}
+
+static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
+ unsigned trigger)
+{
+ void __iomem *base = bank->base;
+ u32 gpio_bit = BIT(gpio);
+
+ omap_gpio_rmw(base, bank->regs->leveldetect0, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_LOW);
+ omap_gpio_rmw(base, bank->regs->leveldetect1, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_HIGH);
+ omap_gpio_rmw(base, bank->regs->risingdetect, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_RISING);
+ omap_gpio_rmw(base, bank->regs->fallingdetect, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_FALLING);
+
+ bank->context.leveldetect0 =
+ readl_relaxed(bank->base + bank->regs->leveldetect0);
+ bank->context.leveldetect1 =
+ readl_relaxed(bank->base + bank->regs->leveldetect1);
+ bank->context.risingdetect =
+ readl_relaxed(bank->base + bank->regs->risingdetect);
+ bank->context.fallingdetect =
+ readl_relaxed(bank->base + bank->regs->fallingdetect);
+
+ if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
+ omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit, trigger != 0);
+ bank->context.wake_en =
+ readl_relaxed(bank->base + bank->regs->wkup_en);
+ }
+
+ /* This part needs to be executed always for OMAP{34xx, 44xx} */
+ if (!bank->regs->irqctrl) {
+ /* On omap24xx proceed only when valid GPIO bit is set */
+ if (bank->non_wakeup_gpios) {
+ if (!(bank->non_wakeup_gpios & gpio_bit))
+ goto exit;
+ }
+
+ /*
+ * Log the edge gpio and manually trigger the IRQ
+ * after resume if the input level changes
+ * to avoid irq lost during PER RET/OFF mode
+ * Applies for omap2 non-wakeup gpio and all omap3 gpios
+ */
+ if (trigger & IRQ_TYPE_EDGE_BOTH)
+ bank->enabled_non_wakeup_gpios |= gpio_bit;
+ else
+ bank->enabled_non_wakeup_gpios &= ~gpio_bit;
+ }
+
+exit:
+ bank->level_mask =
+ readl_relaxed(bank->base + bank->regs->leveldetect0) |
+ readl_relaxed(bank->base + bank->regs->leveldetect1);
+}
+
+#ifdef CONFIG_ARCH_OMAP1
+/*
+ * This only applies to chips that can't do both rising and falling edge
+ * detection at once. For all other chips, this function is a noop.
+ */
+static void omap_toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio)
+{
+ void __iomem *reg = bank->base;
+ u32 l = 0;
+
+ if (!bank->regs->irqctrl)
+ return;
+
+ reg += bank->regs->irqctrl;
+
+ l = readl_relaxed(reg);
+ if ((l >> gpio) & 1)
+ l &= ~(BIT(gpio));
+ else
+ l |= BIT(gpio);
+
+ writel_relaxed(l, reg);
+}
+#else
+static void omap_toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) {}
+#endif
+
+static int omap_set_gpio_triggering(struct gpio_bank *bank, int gpio,
+ unsigned trigger)
+{
+ void __iomem *reg = bank->base;
+ void __iomem *base = bank->base;
+ u32 l = 0;
+
+ if (bank->regs->leveldetect0 && bank->regs->wkup_en) {
+ omap_set_gpio_trigger(bank, gpio, trigger);
+ } else if (bank->regs->irqctrl) {
+ reg += bank->regs->irqctrl;
+
+ l = readl_relaxed(reg);
+ if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
+ bank->toggle_mask |= BIT(gpio);
+ if (trigger & IRQ_TYPE_EDGE_RISING)
+ l |= BIT(gpio);
+ else if (trigger & IRQ_TYPE_EDGE_FALLING)
+ l &= ~(BIT(gpio));
+ else
+ return -EINVAL;
+
+ writel_relaxed(l, reg);
+ } else if (bank->regs->edgectrl1) {
+ if (gpio & 0x08)
+ reg += bank->regs->edgectrl2;
+ else
+ reg += bank->regs->edgectrl1;
+
+ gpio &= 0x07;
+ l = readl_relaxed(reg);
+ l &= ~(3 << (gpio << 1));
+ if (trigger & IRQ_TYPE_EDGE_RISING)
+ l |= 2 << (gpio << 1);
+ if (trigger & IRQ_TYPE_EDGE_FALLING)
+ l |= BIT(gpio << 1);
+
+ /* Enable wake-up during idle for dynamic tick */
+ omap_gpio_rmw(base, bank->regs->wkup_en, BIT(gpio), trigger);
+ bank->context.wake_en =
+ readl_relaxed(bank->base + bank->regs->wkup_en);
+ writel_relaxed(l, reg);
+ }
+ return 0;
+}
+
+static void omap_enable_gpio_module(struct gpio_bank *bank, unsigned offset)
+{
+ if (bank->regs->pinctrl) {
+ void __iomem *reg = bank->base + bank->regs->pinctrl;
+
+ /* Claim the pin for MPU */
+ writel_relaxed(readl_relaxed(reg) | (BIT(offset)), reg);
+ }
+
+ if (bank->regs->ctrl && !BANK_USED(bank)) {
+ void __iomem *reg = bank->base + bank->regs->ctrl;
+ u32 ctrl;
+
+ ctrl = readl_relaxed(reg);
+ /* Module is enabled, clocks are not gated */
+ ctrl &= ~GPIO_MOD_CTRL_BIT;
+ writel_relaxed(ctrl, reg);
+ bank->context.ctrl = ctrl;
+ }
+}
+
+static void omap_disable_gpio_module(struct gpio_bank *bank, unsigned offset)
+{
+ void __iomem *base = bank->base;
+
+ if (bank->regs->wkup_en &&
+ !LINE_USED(bank->mod_usage, offset) &&
+ !LINE_USED(bank->irq_usage, offset)) {
+ /* Disable wake-up during idle for dynamic tick */
+ omap_gpio_rmw(base, bank->regs->wkup_en, BIT(offset), 0);
+ bank->context.wake_en =
+ readl_relaxed(bank->base + bank->regs->wkup_en);
+ }
+
+ if (bank->regs->ctrl && !BANK_USED(bank)) {
+ void __iomem *reg = bank->base + bank->regs->ctrl;
+ u32 ctrl;
+
+ ctrl = readl_relaxed(reg);
+ /* Module is disabled, clocks are gated */
+ ctrl |= GPIO_MOD_CTRL_BIT;
+ writel_relaxed(ctrl, reg);
+ bank->context.ctrl = ctrl;
+ }
+}
+
+static int omap_gpio_is_input(struct gpio_bank *bank, unsigned offset)
+{
+ void __iomem *reg = bank->base + bank->regs->direction;
+
+ return readl_relaxed(reg) & BIT(offset);
+}
+
+static void omap_gpio_init_irq(struct gpio_bank *bank, unsigned offset)
+{
+ if (!LINE_USED(bank->mod_usage, offset)) {
+ omap_enable_gpio_module(bank, offset);
+ omap_set_gpio_direction(bank, offset, 1);
+ }
+ bank->irq_usage |= BIT(offset);
+}
+
+static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
+{
+ struct gpio_bank *bank = omap_irq_data_get_bank(d);
+ int retval;
+ unsigned long flags;
+ unsigned offset = d->hwirq;
+
+ if (!BANK_USED(bank))
+ pm_runtime_get_sync(bank->dev);
+
+ if (type & ~IRQ_TYPE_SENSE_MASK)
+ return -EINVAL;
+
+ if (!bank->regs->leveldetect0 &&
+ (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
+ return -EINVAL;
+
+ spin_lock_irqsave(&bank->lock, flags);
+ retval = omap_set_gpio_triggering(bank, offset, type);
+ omap_gpio_init_irq(bank, offset);
+ if (!omap_gpio_is_input(bank, offset)) {
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+
+ return retval;
+}
+
+static void omap_clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
+{
+ void __iomem *reg = bank->base;
+
+ reg += bank->regs->irqstatus;
+ writel_relaxed(gpio_mask, reg);
+
+ /* Workaround for clearing DSP GPIO interrupts to allow retention */
+ if (bank->regs->irqstatus2) {
+ reg = bank->base + bank->regs->irqstatus2;
+ writel_relaxed(gpio_mask, reg);
+ }
+
+ /* Flush posted write for the irq status to avoid spurious interrupts */
+ readl_relaxed(reg);
+}
+
+static inline void omap_clear_gpio_irqstatus(struct gpio_bank *bank,
+ unsigned offset)
+{
+ omap_clear_gpio_irqbank(bank, BIT(offset));
+}
+
+static u32 omap_get_gpio_irqbank_mask(struct gpio_bank *bank)
+{
+ void __iomem *reg = bank->base;
+ u32 l;
+ u32 mask = (BIT(bank->width)) - 1;
+
+ reg += bank->regs->irqenable;
+ l = readl_relaxed(reg);
+ if (bank->regs->irqenable_inv)
+ l = ~l;
+ l &= mask;
+ return l;
+}
+
+static void omap_enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
+{
+ void __iomem *reg = bank->base;
+ u32 l;
+
+ if (bank->regs->set_irqenable) {
+ reg += bank->regs->set_irqenable;
+ l = gpio_mask;
+ bank->context.irqenable1 |= gpio_mask;
+ } else {
+ reg += bank->regs->irqenable;
+ l = readl_relaxed(reg);
+ if (bank->regs->irqenable_inv)
+ l &= ~gpio_mask;
+ else
+ l |= gpio_mask;
+ bank->context.irqenable1 = l;
+ }
+
+ writel_relaxed(l, reg);
+}
+
+static void omap_disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
+{
+ void __iomem *reg = bank->base;
+ u32 l;
+
+ if (bank->regs->clr_irqenable) {
+ reg += bank->regs->clr_irqenable;
+ l = gpio_mask;
+ bank->context.irqenable1 &= ~gpio_mask;
+ } else {
+ reg += bank->regs->irqenable;
+ l = readl_relaxed(reg);
+ if (bank->regs->irqenable_inv)
+ l |= gpio_mask;
+ else
+ l &= ~gpio_mask;
+ bank->context.irqenable1 = l;
+ }
+
+ writel_relaxed(l, reg);
+}
+
+static inline void omap_set_gpio_irqenable(struct gpio_bank *bank,
+ unsigned offset, int enable)
+{
+ if (enable)
+ omap_enable_gpio_irqbank(bank, BIT(offset));
+ else
+ omap_disable_gpio_irqbank(bank, BIT(offset));
+}
+
+/*
+ * Note that ENAWAKEUP needs to be enabled in GPIO_SYSCONFIG register.
+ * 1510 does not seem to have a wake-up register. If JTAG is connected
+ * to the target, system will wake up always on GPIO events. While
+ * system is running all registered GPIO interrupts need to have wake-up
+ * enabled. When system is suspended, only selected GPIO interrupts need
+ * to have wake-up enabled.
+ */
+static int omap_set_gpio_wakeup(struct gpio_bank *bank, unsigned offset,
+ int enable)
+{
+ u32 gpio_bit = BIT(offset);
+ unsigned long flags;
+
+ if (bank->non_wakeup_gpios & gpio_bit) {
+ dev_err(bank->dev,
+ "Unable to modify wakeup on non-wakeup GPIO%d\n",
+ offset);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bank->lock, flags);
+ if (enable)
+ bank->context.wake_en |= gpio_bit;
+ else
+ bank->context.wake_en &= ~gpio_bit;
+
+ writel_relaxed(bank->context.wake_en, bank->base + bank->regs->wkup_en);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
+}
+
+static void omap_reset_gpio(struct gpio_bank *bank, unsigned offset)
+{
+ omap_set_gpio_direction(bank, offset, 1);
+ omap_set_gpio_irqenable(bank, offset, 0);
+ omap_clear_gpio_irqstatus(bank, offset);
+ omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
+ omap_clear_gpio_debounce(bank, offset);
+}
+
+/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
+static int omap_gpio_wake_enable(struct irq_data *d, unsigned int enable)
+{
+ struct gpio_bank *bank = omap_irq_data_get_bank(d);
+ unsigned offset = d->hwirq;
+
+ return omap_set_gpio_wakeup(bank, offset, enable);
+}
+
+static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+ unsigned long flags;
+
+ /*
+ * If this is the first gpio_request for the bank,
+ * enable the bank module.
+ */
+ if (!BANK_USED(bank))
+ pm_runtime_get_sync(bank->dev);
+
+ spin_lock_irqsave(&bank->lock, flags);
+ /* Set trigger to none. You need to enable the desired trigger with
+ * request_irq() or set_irq_type(). Only do this if the IRQ line has
+ * not already been requested.
+ */
+ if (!LINE_USED(bank->irq_usage, offset)) {
+ omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
+ omap_enable_gpio_module(bank, offset);
+ }
+ bank->mod_usage |= BIT(offset);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
+}
+
+static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bank->lock, flags);
+ bank->mod_usage &= ~(BIT(offset));
+ omap_disable_gpio_module(bank, offset);
+ omap_reset_gpio(bank, offset);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ /*
+ * If this is the last gpio to be freed in the bank,
+ * disable the bank module.
+ */
+ if (!BANK_USED(bank))
+ pm_runtime_put(bank->dev);
+}
+
+/*
+ * We need to unmask the GPIO bank interrupt as soon as possible to
+ * avoid missing GPIO interrupts for other lines in the bank.
+ * Then we need to mask-read-clear-unmask the triggered GPIO lines
+ * in the bank to avoid missing nested interrupts for a GPIO line.
+ * If we wait to unmask individual GPIO lines in the bank after the
+ * line's interrupt handler has been run, we may miss some nested
+ * interrupts.
+ */
+static void omap_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ void __iomem *isr_reg = NULL;
+ u32 isr;
+ unsigned int bit;
+ struct gpio_bank *bank;
+ int unmasked = 0;
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+ struct gpio_chip *chip = irq_get_handler_data(irq);
+
+ chained_irq_enter(irqchip, desc);
+
+ bank = container_of(chip, struct gpio_bank, chip);
+ isr_reg = bank->base + bank->regs->irqstatus;
+ pm_runtime_get_sync(bank->dev);
+
+ if (WARN_ON(!isr_reg))
+ goto exit;
+
+ while (1) {
+ u32 isr_saved, level_mask = 0;
+ u32 enabled;
+
+ enabled = omap_get_gpio_irqbank_mask(bank);
+ isr_saved = isr = readl_relaxed(isr_reg) & enabled;
+
+ if (bank->level_mask)
+ level_mask = bank->level_mask & enabled;
+
+ /* clear edge sensitive interrupts before handler(s) are
+ called so that we don't miss any interrupt occurred while
+ executing them */
+ omap_disable_gpio_irqbank(bank, isr_saved & ~level_mask);
+ omap_clear_gpio_irqbank(bank, isr_saved & ~level_mask);
+ omap_enable_gpio_irqbank(bank, isr_saved & ~level_mask);
+
+ /* if there is only edge sensitive GPIO pin interrupts
+ configured, we could unmask GPIO bank interrupt immediately */
+ if (!level_mask && !unmasked) {
+ unmasked = 1;
+ chained_irq_exit(irqchip, desc);
+ }
+
+ if (!isr)
+ break;
+
+ while (isr) {
+ bit = __ffs(isr);
+ isr &= ~(BIT(bit));
+
+ /*
+ * Some chips can't respond to both rising and falling
+ * at the same time. If this irq was requested with
+ * both flags, we need to flip the ICR data for the IRQ
+ * to respond to the IRQ for the opposite direction.
+ * This will be indicated in the bank toggle_mask.
+ */
+ if (bank->toggle_mask & (BIT(bit)))
+ omap_toggle_gpio_edge_triggering(bank, bit);
+
+ generic_handle_irq(irq_find_mapping(bank->chip.irqdomain,
+ bit));
+ }
+ }
+ /* if bank has any level sensitive GPIO pin interrupt
+ configured, we must unmask the bank interrupt only after
+ handler(s) are executed in order to avoid spurious bank
+ interrupt */
+exit:
+ if (!unmasked)
+ chained_irq_exit(irqchip, desc);
+ pm_runtime_put(bank->dev);
+}
+
+static unsigned int omap_gpio_irq_startup(struct irq_data *d)
+{
+ struct gpio_bank *bank = omap_irq_data_get_bank(d);
+ unsigned long flags;
+ unsigned offset = d->hwirq;
+
+ if (!BANK_USED(bank))
+ pm_runtime_get_sync(bank->dev);
+
+ spin_lock_irqsave(&bank->lock, flags);
+ omap_gpio_init_irq(bank, offset);
+ spin_unlock_irqrestore(&bank->lock, flags);
+ omap_gpio_unmask_irq(d);
+
+ return 0;
+}
+
+static void omap_gpio_irq_shutdown(struct irq_data *d)
+{
+ struct gpio_bank *bank = omap_irq_data_get_bank(d);
+ unsigned long flags;
+ unsigned offset = d->hwirq;
+
+ spin_lock_irqsave(&bank->lock, flags);
+ bank->irq_usage &= ~(BIT(offset));
+ omap_disable_gpio_module(bank, offset);
+ omap_reset_gpio(bank, offset);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ /*
+ * If this is the last IRQ to be freed in the bank,
+ * disable the bank module.
+ */
+ if (!BANK_USED(bank))
+ pm_runtime_put(bank->dev);
+}
+
+static void omap_gpio_ack_irq(struct irq_data *d)
+{
+ struct gpio_bank *bank = omap_irq_data_get_bank(d);
+ unsigned offset = d->hwirq;
+
+ omap_clear_gpio_irqstatus(bank, offset);
+}
+
+static void omap_gpio_mask_irq(struct irq_data *d)
+{
+ struct gpio_bank *bank = omap_irq_data_get_bank(d);
+ unsigned offset = d->hwirq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bank->lock, flags);
+ omap_set_gpio_irqenable(bank, offset, 0);
+ omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
+ spin_unlock_irqrestore(&bank->lock, flags);
+}
+
+static void omap_gpio_unmask_irq(struct irq_data *d)
+{
+ struct gpio_bank *bank = omap_irq_data_get_bank(d);
+ unsigned offset = d->hwirq;
+ u32 trigger = irqd_get_trigger_type(d);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bank->lock, flags);
+ if (trigger)
+ omap_set_gpio_triggering(bank, offset, trigger);
+
+ /* For level-triggered GPIOs, the clearing must be done after
+ * the HW source is cleared, thus after the handler has run */
+ if (bank->level_mask & BIT(offset)) {
+ omap_set_gpio_irqenable(bank, offset, 0);
+ omap_clear_gpio_irqstatus(bank, offset);
+ }
+
+ omap_set_gpio_irqenable(bank, offset, 1);
+ spin_unlock_irqrestore(&bank->lock, flags);
+}
+
+/*---------------------------------------------------------------------*/
+
+static int omap_mpuio_suspend_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+ void __iomem *mask_reg = bank->base +
+ OMAP_MPUIO_GPIO_MASKIT / bank->stride;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bank->lock, flags);
+ writel_relaxed(0xffff & ~bank->context.wake_en, mask_reg);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
+}
+
+static int omap_mpuio_resume_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+ void __iomem *mask_reg = bank->base +
+ OMAP_MPUIO_GPIO_MASKIT / bank->stride;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bank->lock, flags);
+ writel_relaxed(bank->context.wake_en, mask_reg);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
+}
+
+static const struct dev_pm_ops omap_mpuio_dev_pm_ops = {
+ .suspend_noirq = omap_mpuio_suspend_noirq,
+ .resume_noirq = omap_mpuio_resume_noirq,
+};
+
+/* use platform_driver for this. */
+static struct platform_driver omap_mpuio_driver = {
+ .driver = {
+ .name = "mpuio",
+ .pm = &omap_mpuio_dev_pm_ops,
+ },
+};
+
+static struct platform_device omap_mpuio_device = {
+ .name = "mpuio",
+ .id = -1,
+ .dev = {
+ .driver = &omap_mpuio_driver.driver,
+ }
+ /* could list the /proc/iomem resources */
+};
+
+static inline void omap_mpuio_init(struct gpio_bank *bank)
+{
+ platform_set_drvdata(&omap_mpuio_device, bank);
+
+ if (platform_driver_register(&omap_mpuio_driver) == 0)
+ (void) platform_device_register(&omap_mpuio_device);
+}
+
+/*---------------------------------------------------------------------*/
+
+static int omap_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_bank *bank;
+ unsigned long flags;
+ void __iomem *reg;
+ int dir;
+
+ bank = container_of(chip, struct gpio_bank, chip);
+ reg = bank->base + bank->regs->direction;
+ spin_lock_irqsave(&bank->lock, flags);
+ dir = !!(readl_relaxed(reg) & BIT(offset));
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return dir;
+}
+
+static int omap_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_bank *bank;
+ unsigned long flags;
+
+ bank = container_of(chip, struct gpio_bank, chip);
+ spin_lock_irqsave(&bank->lock, flags);
+ omap_set_gpio_direction(bank, offset, 1);
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return 0;
+}
+
+static int omap_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_bank *bank;
+
+ bank = container_of(chip, struct gpio_bank, chip);
+
+ if (omap_gpio_is_input(bank, offset))
+ return omap_get_gpio_datain(bank, offset);
+ else
+ return omap_get_gpio_dataout(bank, offset);
+}
+
+static int omap_gpio_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct gpio_bank *bank;
+ unsigned long flags;
+
+ bank = container_of(chip, struct gpio_bank, chip);
+ spin_lock_irqsave(&bank->lock, flags);
+ bank->set_dataout(bank, offset, value);
+ omap_set_gpio_direction(bank, offset, 0);
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return 0;
+}
+
+static int omap_gpio_debounce(struct gpio_chip *chip, unsigned offset,
+ unsigned debounce)
+{
+ struct gpio_bank *bank;
+ unsigned long flags;
+
+ bank = container_of(chip, struct gpio_bank, chip);
+
+ spin_lock_irqsave(&bank->lock, flags);
+ omap2_set_gpio_debounce(bank, offset, debounce);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
+}
+
+static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct gpio_bank *bank;
+ unsigned long flags;
+
+ bank = container_of(chip, struct gpio_bank, chip);
+ spin_lock_irqsave(&bank->lock, flags);
+ bank->set_dataout(bank, offset, value);
+ spin_unlock_irqrestore(&bank->lock, flags);
+}
+
+/*---------------------------------------------------------------------*/
+
+static void __init omap_gpio_show_rev(struct gpio_bank *bank)
+{
+ static bool called;
+ u32 rev;
+
+ if (called || bank->regs->revision == USHRT_MAX)
+ return;
+
+ rev = readw_relaxed(bank->base + bank->regs->revision);
+ pr_info("OMAP GPIO hardware version %d.%d\n",
+ (rev >> 4) & 0x0f, rev & 0x0f);
+
+ called = true;
+}
+
+static void omap_gpio_mod_init(struct gpio_bank *bank)
+{
+ void __iomem *base = bank->base;
+ u32 l = 0xffffffff;
+
+ if (bank->width == 16)
+ l = 0xffff;
+
+ if (bank->is_mpuio) {
+ writel_relaxed(l, bank->base + bank->regs->irqenable);
+ return;
+ }
+
+ omap_gpio_rmw(base, bank->regs->irqenable, l,
+ bank->regs->irqenable_inv);
+ omap_gpio_rmw(base, bank->regs->irqstatus, l,
+ !bank->regs->irqenable_inv);
+ if (bank->regs->debounce_en)
+ writel_relaxed(0, base + bank->regs->debounce_en);
+
+ /* Save OE default value (0xffffffff) in the context */
+ bank->context.oe = readl_relaxed(bank->base + bank->regs->direction);
+ /* Initialize interface clk ungated, module enabled */
+ if (bank->regs->ctrl)
+ writel_relaxed(0, base + bank->regs->ctrl);
+
+ bank->dbck = clk_get(bank->dev, "dbclk");
+ if (IS_ERR(bank->dbck))
+ dev_err(bank->dev, "Could not get gpio dbck\n");
+}
+
+static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
+{
+ static int gpio;
+ int irq_base = 0;
+ int ret;
+
+ /*
+ * REVISIT eventually switch from OMAP-specific gpio structs
+ * over to the generic ones
+ */
+ bank->chip.request = omap_gpio_request;
+ bank->chip.free = omap_gpio_free;
+ bank->chip.get_direction = omap_gpio_get_direction;
+ bank->chip.direction_input = omap_gpio_input;
+ bank->chip.get = omap_gpio_get;
+ bank->chip.direction_output = omap_gpio_output;
+ bank->chip.set_debounce = omap_gpio_debounce;
+ bank->chip.set = omap_gpio_set;
+ if (bank->is_mpuio) {
+ bank->chip.label = "mpuio";
+ if (bank->regs->wkup_en)
+ bank->chip.dev = &omap_mpuio_device.dev;
+ bank->chip.base = OMAP_MPUIO(0);
+ } else {
+ bank->chip.label = "gpio";
+ bank->chip.base = gpio;
+ gpio += bank->width;
+ }
+ bank->chip.ngpio = bank->width;
+
+ ret = gpiochip_add(&bank->chip);
+ if (ret) {
+ dev_err(bank->dev, "Could not register gpio chip %d\n", ret);
+ return ret;
+ }
+
+#ifdef CONFIG_ARCH_OMAP1
+ /*
+ * REVISIT: Once we have OMAP1 supporting SPARSE_IRQ, we can drop
+ * irq_alloc_descs() since a base IRQ offset will no longer be needed.
+ */
+ irq_base = irq_alloc_descs(-1, 0, bank->width, 0);
+ if (irq_base < 0) {
+ dev_err(bank->dev, "Couldn't allocate IRQ numbers\n");
+ return -ENODEV;
+ }
+#endif
+
+ /* MPUIO is a bit different, reading IRQ status clears it */
+ if (bank->is_mpuio) {
+ irqc->irq_ack = dummy_irq_chip.irq_ack;
+ irqc->irq_mask = irq_gc_mask_set_bit;
+ irqc->irq_unmask = irq_gc_mask_clr_bit;
+ if (!bank->regs->wkup_en)
+ irqc->irq_set_wake = NULL;
+ }
+
+ ret = gpiochip_irqchip_add(&bank->chip, irqc,
+ irq_base, omap_gpio_irq_handler,
+ IRQ_TYPE_NONE);
+
+ if (ret) {
+ dev_err(bank->dev, "Couldn't add irqchip to gpiochip %d\n", ret);
+ gpiochip_remove(&bank->chip);
+ return -ENODEV;
+ }
+
+ gpiochip_set_chained_irqchip(&bank->chip, irqc,
+ bank->irq, omap_gpio_irq_handler);
+
+ return 0;
+}
+
+static const struct of_device_id omap_gpio_match[];
+
+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 resource *res;
+ struct gpio_bank *bank;
+ struct irq_chip *irqc;
+ int ret;
+
+ match = of_match_device(of_match_ptr(omap_gpio_match), dev);
+
+ pdata = match ? match->data : dev_get_platdata(dev);
+ if (!pdata)
+ return -EINVAL;
+
+ bank = devm_kzalloc(dev, sizeof(struct gpio_bank), GFP_KERNEL);
+ if (!bank) {
+ dev_err(dev, "Memory alloc failed\n");
+ return -ENOMEM;
+ }
+
+ irqc = devm_kzalloc(dev, sizeof(*irqc), GFP_KERNEL);
+ if (!irqc)
+ return -ENOMEM;
+
+ irqc->irq_startup = omap_gpio_irq_startup,
+ irqc->irq_shutdown = omap_gpio_irq_shutdown,
+ irqc->irq_ack = omap_gpio_ack_irq,
+ irqc->irq_mask = omap_gpio_mask_irq,
+ irqc->irq_unmask = omap_gpio_unmask_irq,
+ irqc->irq_set_type = omap_gpio_irq_type,
+ irqc->irq_set_wake = omap_gpio_wake_enable,
+ irqc->name = dev_name(&pdev->dev);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (unlikely(!res)) {
+ dev_err(dev, "Invalid IRQ resource\n");
+ return -ENODEV;
+ }
+
+ bank->irq = res->start;
+ bank->dev = dev;
+ bank->chip.dev = dev;
+ bank->dbck_flag = pdata->dbck_flag;
+ bank->stride = pdata->bank_stride;
+ bank->width = pdata->bank_width;
+ 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"))
+ bank->loses_context = true;
+ } else {
+ bank->loses_context = pdata->loses_context;
+
+ if (bank->loses_context)
+ bank->get_context_loss_count =
+ pdata->get_context_loss_count;
+ }
+
+ if (bank->regs->set_dataout && bank->regs->clr_dataout)
+ bank->set_dataout = omap_set_gpio_dataout_reg;
+ else
+ bank->set_dataout = omap_set_gpio_dataout_mask;
+
+ spin_lock_init(&bank->lock);
+
+ /* Static mapping, never released */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ bank->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(bank->base)) {
+ irq_domain_remove(bank->chip.irqdomain);
+ return PTR_ERR(bank->base);
+ }
+
+ platform_set_drvdata(pdev, bank);
+
+ pm_runtime_enable(bank->dev);
+ pm_runtime_irq_safe(bank->dev);
+ pm_runtime_get_sync(bank->dev);
+
+ if (bank->is_mpuio)
+ omap_mpuio_init(bank);
+
+ omap_gpio_mod_init(bank);
+
+ ret = omap_gpio_chip_init(bank, irqc);
+ if (ret)
+ return ret;
+
+ omap_gpio_show_rev(bank);
+
+ pm_runtime_put(bank->dev);
+
+ list_add_tail(&bank->node, &omap_gpio_list);
+
+ return 0;
+}
+
+#ifdef CONFIG_ARCH_OMAP2PLUS
+
+#if defined(CONFIG_PM)
+static void omap_gpio_restore_context(struct gpio_bank *bank);
+
+static int omap_gpio_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+ u32 l1 = 0, l2 = 0;
+ unsigned long flags;
+ u32 wake_low, wake_hi;
+
+ spin_lock_irqsave(&bank->lock, flags);
+
+ /*
+ * Only edges can generate a wakeup event to the PRCM.
+ *
+ * Therefore, ensure any wake-up capable GPIOs have
+ * edge-detection enabled before going idle to ensure a wakeup
+ * to the PRCM is generated on a GPIO transition. (c.f. 34xx
+ * NDA TRM 25.5.3.1)
+ *
+ * The normal values will be restored upon ->runtime_resume()
+ * by writing back the values saved in bank->context.
+ */
+ wake_low = bank->context.leveldetect0 & bank->context.wake_en;
+ if (wake_low)
+ writel_relaxed(wake_low | bank->context.fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
+ if (wake_hi)
+ writel_relaxed(wake_hi | bank->context.risingdetect,
+ bank->base + bank->regs->risingdetect);
+
+ if (!bank->enabled_non_wakeup_gpios)
+ goto update_gpio_context_count;
+
+ if (bank->power_mode != OFF_MODE) {
+ bank->power_mode = 0;
+ goto update_gpio_context_count;
+ }
+ /*
+ * If going to OFF, remove triggering for all
+ * non-wakeup GPIOs. Otherwise spurious IRQs will be
+ * generated. See OMAP2420 Errata item 1.101.
+ */
+ bank->saved_datain = readl_relaxed(bank->base +
+ bank->regs->datain);
+ l1 = bank->context.fallingdetect;
+ l2 = bank->context.risingdetect;
+
+ l1 &= ~bank->enabled_non_wakeup_gpios;
+ l2 &= ~bank->enabled_non_wakeup_gpios;
+
+ writel_relaxed(l1, bank->base + bank->regs->fallingdetect);
+ writel_relaxed(l2, bank->base + bank->regs->risingdetect);
+
+ bank->workaround_enabled = true;
+
+update_gpio_context_count:
+ if (bank->get_context_loss_count)
+ bank->context_loss_count =
+ bank->get_context_loss_count(bank->dev);
+
+ omap_gpio_dbck_disable(bank);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
+}
+
+static void omap_gpio_init_context(struct gpio_bank *p);
+
+static int omap_gpio_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+ u32 l = 0, gen, gen0, gen1;
+ unsigned long flags;
+ int c;
+
+ spin_lock_irqsave(&bank->lock, flags);
+
+ /*
+ * On the first resume during the probe, the context has not
+ * been initialised and so initialise it now. Also initialise
+ * the context loss count.
+ */
+ if (bank->loses_context && !bank->context_valid) {
+ omap_gpio_init_context(bank);
+
+ if (bank->get_context_loss_count)
+ bank->context_loss_count =
+ bank->get_context_loss_count(bank->dev);
+ }
+
+ omap_gpio_dbck_enable(bank);
+
+ /*
+ * In ->runtime_suspend(), level-triggered, wakeup-enabled
+ * GPIOs were set to edge trigger also in order to be able to
+ * generate a PRCM wakeup. Here we restore the
+ * pre-runtime_suspend() values for edge triggering.
+ */
+ writel_relaxed(bank->context.fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ writel_relaxed(bank->context.risingdetect,
+ bank->base + bank->regs->risingdetect);
+
+ if (bank->loses_context) {
+ if (!bank->get_context_loss_count) {
+ omap_gpio_restore_context(bank);
+ } else {
+ c = bank->get_context_loss_count(bank->dev);
+ if (c != bank->context_loss_count) {
+ omap_gpio_restore_context(bank);
+ } else {
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return 0;
+ }
+ }
+ }
+
+ if (!bank->workaround_enabled) {
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return 0;
+ }
+
+ l = readl_relaxed(bank->base + bank->regs->datain);
+
+ /*
+ * Check if any of the non-wakeup interrupt GPIOs have changed
+ * state. If so, generate an IRQ by software. This is
+ * horribly racy, but it's the best we can do to work around
+ * this silicon bug.
+ */
+ l ^= bank->saved_datain;
+ l &= bank->enabled_non_wakeup_gpios;
+
+ /*
+ * No need to generate IRQs for the rising edge for gpio IRQs
+ * configured with falling edge only; and vice versa.
+ */
+ gen0 = l & bank->context.fallingdetect;
+ gen0 &= bank->saved_datain;
+
+ gen1 = l & bank->context.risingdetect;
+ gen1 &= ~(bank->saved_datain);
+
+ /* FIXME: Consider GPIO IRQs with level detections properly! */
+ gen = l & (~(bank->context.fallingdetect) &
+ ~(bank->context.risingdetect));
+ /* Consider all GPIO IRQs needed to be updated */
+ gen |= gen0 | gen1;
+
+ if (gen) {
+ u32 old0, old1;
+
+ old0 = readl_relaxed(bank->base + bank->regs->leveldetect0);
+ old1 = readl_relaxed(bank->base + bank->regs->leveldetect1);
+
+ if (!bank->regs->irqstatus_raw0) {
+ writel_relaxed(old0 | gen, bank->base +
+ bank->regs->leveldetect0);
+ writel_relaxed(old1 | gen, bank->base +
+ bank->regs->leveldetect1);
+ }
+
+ if (bank->regs->irqstatus_raw0) {
+ writel_relaxed(old0 | l, bank->base +
+ bank->regs->leveldetect0);
+ writel_relaxed(old1 | l, bank->base +
+ bank->regs->leveldetect1);
+ }
+ writel_relaxed(old0, bank->base + bank->regs->leveldetect0);
+ writel_relaxed(old1, bank->base + bank->regs->leveldetect1);
+ }
+
+ bank->workaround_enabled = false;
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+void omap2_gpio_prepare_for_idle(int pwr_mode)
+{
+ struct gpio_bank *bank;
+
+ list_for_each_entry(bank, &omap_gpio_list, node) {
+ if (!BANK_USED(bank) || !bank->loses_context)
+ continue;
+
+ bank->power_mode = pwr_mode;
+
+ pm_runtime_put_sync_suspend(bank->dev);
+ }
+}
+
+void omap2_gpio_resume_after_idle(void)
+{
+ struct gpio_bank *bank;
+
+ list_for_each_entry(bank, &omap_gpio_list, node) {
+ if (!BANK_USED(bank) || !bank->loses_context)
+ continue;
+
+ pm_runtime_get_sync(bank->dev);
+ }
+}
+
+#if defined(CONFIG_PM)
+static void omap_gpio_init_context(struct gpio_bank *p)
+{
+ struct omap_gpio_reg_offs *regs = p->regs;
+ void __iomem *base = p->base;
+
+ 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);
+ p->context.leveldetect0 = readl_relaxed(base + regs->leveldetect0);
+ p->context.leveldetect1 = readl_relaxed(base + regs->leveldetect1);
+ p->context.risingdetect = readl_relaxed(base + regs->risingdetect);
+ p->context.fallingdetect = readl_relaxed(base + regs->fallingdetect);
+ p->context.irqenable1 = readl_relaxed(base + regs->irqenable);
+ p->context.irqenable2 = readl_relaxed(base + regs->irqenable2);
+
+ if (regs->set_dataout && p->regs->clr_dataout)
+ p->context.dataout = readl_relaxed(base + regs->set_dataout);
+ else
+ p->context.dataout = readl_relaxed(base + regs->dataout);
+
+ p->context_valid = true;
+}
+
+static void omap_gpio_restore_context(struct gpio_bank *bank)
+{
+ writel_relaxed(bank->context.wake_en,
+ bank->base + bank->regs->wkup_en);
+ writel_relaxed(bank->context.ctrl, bank->base + bank->regs->ctrl);
+ writel_relaxed(bank->context.leveldetect0,
+ bank->base + bank->regs->leveldetect0);
+ writel_relaxed(bank->context.leveldetect1,
+ bank->base + bank->regs->leveldetect1);
+ writel_relaxed(bank->context.risingdetect,
+ bank->base + bank->regs->risingdetect);
+ writel_relaxed(bank->context.fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ if (bank->regs->set_dataout && bank->regs->clr_dataout)
+ writel_relaxed(bank->context.dataout,
+ bank->base + bank->regs->set_dataout);
+ else
+ writel_relaxed(bank->context.dataout,
+ bank->base + bank->regs->dataout);
+ writel_relaxed(bank->context.oe, bank->base + bank->regs->direction);
+
+ if (bank->dbck_enable_mask) {
+ writel_relaxed(bank->context.debounce, bank->base +
+ bank->regs->debounce);
+ writel_relaxed(bank->context.debounce_en,
+ bank->base + bank->regs->debounce_en);
+ }
+
+ writel_relaxed(bank->context.irqenable1,
+ bank->base + bank->regs->irqenable);
+ writel_relaxed(bank->context.irqenable2,
+ bank->base + bank->regs->irqenable2);
+}
+#endif /* CONFIG_PM */
+#else
+#define omap_gpio_runtime_suspend NULL
+#define omap_gpio_runtime_resume NULL
+static inline void omap_gpio_init_context(struct gpio_bank *p) {}
+#endif
+
+static const struct dev_pm_ops gpio_pm_ops = {
+ SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
+ NULL)
+};
+
+#if defined(CONFIG_OF)
+static struct omap_gpio_reg_offs omap2_gpio_regs = {
+ .revision = OMAP24XX_GPIO_REVISION,
+ .direction = OMAP24XX_GPIO_OE,
+ .datain = OMAP24XX_GPIO_DATAIN,
+ .dataout = OMAP24XX_GPIO_DATAOUT,
+ .set_dataout = OMAP24XX_GPIO_SETDATAOUT,
+ .clr_dataout = OMAP24XX_GPIO_CLEARDATAOUT,
+ .irqstatus = OMAP24XX_GPIO_IRQSTATUS1,
+ .irqstatus2 = OMAP24XX_GPIO_IRQSTATUS2,
+ .irqenable = OMAP24XX_GPIO_IRQENABLE1,
+ .irqenable2 = OMAP24XX_GPIO_IRQENABLE2,
+ .set_irqenable = OMAP24XX_GPIO_SETIRQENABLE1,
+ .clr_irqenable = OMAP24XX_GPIO_CLEARIRQENABLE1,
+ .debounce = OMAP24XX_GPIO_DEBOUNCE_VAL,
+ .debounce_en = OMAP24XX_GPIO_DEBOUNCE_EN,
+ .ctrl = OMAP24XX_GPIO_CTRL,
+ .wkup_en = OMAP24XX_GPIO_WAKE_EN,
+ .leveldetect0 = OMAP24XX_GPIO_LEVELDETECT0,
+ .leveldetect1 = OMAP24XX_GPIO_LEVELDETECT1,
+ .risingdetect = OMAP24XX_GPIO_RISINGDETECT,
+ .fallingdetect = OMAP24XX_GPIO_FALLINGDETECT,
+};
+
+static struct omap_gpio_reg_offs omap4_gpio_regs = {
+ .revision = OMAP4_GPIO_REVISION,
+ .direction = OMAP4_GPIO_OE,
+ .datain = OMAP4_GPIO_DATAIN,
+ .dataout = OMAP4_GPIO_DATAOUT,
+ .set_dataout = OMAP4_GPIO_SETDATAOUT,
+ .clr_dataout = OMAP4_GPIO_CLEARDATAOUT,
+ .irqstatus = OMAP4_GPIO_IRQSTATUS0,
+ .irqstatus2 = OMAP4_GPIO_IRQSTATUS1,
+ .irqenable = OMAP4_GPIO_IRQSTATUSSET0,
+ .irqenable2 = OMAP4_GPIO_IRQSTATUSSET1,
+ .set_irqenable = OMAP4_GPIO_IRQSTATUSSET0,
+ .clr_irqenable = OMAP4_GPIO_IRQSTATUSCLR0,
+ .debounce = OMAP4_GPIO_DEBOUNCINGTIME,
+ .debounce_en = OMAP4_GPIO_DEBOUNCENABLE,
+ .ctrl = OMAP4_GPIO_CTRL,
+ .wkup_en = OMAP4_GPIO_IRQWAKEN0,
+ .leveldetect0 = OMAP4_GPIO_LEVELDETECT0,
+ .leveldetect1 = OMAP4_GPIO_LEVELDETECT1,
+ .risingdetect = OMAP4_GPIO_RISINGDETECT,
+ .fallingdetect = OMAP4_GPIO_FALLINGDETECT,
+};
+
+static const struct omap_gpio_platform_data omap2_pdata = {
+ .regs = &omap2_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = false,
+};
+
+static const struct omap_gpio_platform_data omap3_pdata = {
+ .regs = &omap2_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = true,
+};
+
+static const struct omap_gpio_platform_data omap4_pdata = {
+ .regs = &omap4_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = true,
+};
+
+static const struct of_device_id omap_gpio_match[] = {
+ {
+ .compatible = "ti,omap4-gpio",
+ .data = &omap4_pdata,
+ },
+ {
+ .compatible = "ti,omap3-gpio",
+ .data = &omap3_pdata,
+ },
+ {
+ .compatible = "ti,omap2-gpio",
+ .data = &omap2_pdata,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_gpio_match);
+#endif
+
+static struct platform_driver omap_gpio_driver = {
+ .probe = omap_gpio_probe,
+ .driver = {
+ .name = "omap_gpio",
+ .pm = &gpio_pm_ops,
+ .of_match_table = of_match_ptr(omap_gpio_match),
+ },
+};
+
+/*
+ * gpio driver register needs to be done before
+ * machine_init functions access gpio APIs.
+ * Hence omap_gpio_drv_reg() is a postcore_initcall.
+ */
+static int __init omap_gpio_drv_reg(void)
+{
+ return platform_driver_register(&omap_gpio_driver);
+}
+postcore_initcall(omap_gpio_drv_reg);
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
new file mode 100644
index 000000000..171a6389f
--- /dev/null
+++ b/drivers/gpio/gpio-palmas.c
@@ -0,0 +1,240 @@
+/*
+ * TI Palma series PMIC's GPIO driver.
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/palmas.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+struct palmas_gpio {
+ struct gpio_chip gpio_chip;
+ struct palmas *palmas;
+};
+
+struct palmas_device_data {
+ int ngpio;
+};
+
+static inline struct palmas_gpio *to_palmas_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct palmas_gpio, gpio_chip);
+}
+
+static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+ unsigned int val;
+ int ret;
+ unsigned int reg;
+ int gpio16 = (offset/8);
+
+ offset %= 8;
+ reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
+
+ ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val);
+ if (ret < 0) {
+ dev_err(gc->dev, "Reg 0x%02x read failed, %d\n", reg, ret);
+ return ret;
+ }
+
+ if (val & BIT(offset))
+ reg = (gpio16) ? PALMAS_GPIO_DATA_OUT2 : PALMAS_GPIO_DATA_OUT;
+ else
+ reg = (gpio16) ? PALMAS_GPIO_DATA_IN2 : PALMAS_GPIO_DATA_IN;
+
+ ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val);
+ if (ret < 0) {
+ dev_err(gc->dev, "Reg 0x%02x read failed, %d\n", reg, ret);
+ return ret;
+ }
+ return !!(val & BIT(offset));
+}
+
+static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+ int ret;
+ unsigned int reg;
+ int gpio16 = (offset/8);
+
+ offset %= 8;
+ if (gpio16)
+ reg = (value) ?
+ PALMAS_GPIO_SET_DATA_OUT2 : PALMAS_GPIO_CLEAR_DATA_OUT2;
+ else
+ reg = (value) ?
+ PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT;
+
+ ret = palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset));
+ if (ret < 0)
+ dev_err(gc->dev, "Reg 0x%02x write failed, %d\n", reg, ret);
+}
+
+static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+ int ret;
+ unsigned int reg;
+ int gpio16 = (offset/8);
+
+ offset %= 8;
+ reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
+
+ /* Set the initial value */
+ palmas_gpio_set(gc, offset, value);
+
+ ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg,
+ BIT(offset), BIT(offset));
+ if (ret < 0)
+ dev_err(gc->dev, "Reg 0x%02x update failed, %d\n", reg, ret);
+ return ret;
+}
+
+static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+ int ret;
+ unsigned int reg;
+ int gpio16 = (offset/8);
+
+ offset %= 8;
+ reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
+
+ ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg, BIT(offset), 0);
+ if (ret < 0)
+ dev_err(gc->dev, "Reg 0x%02x update failed, %d\n", reg, ret);
+ return ret;
+}
+
+static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct palmas_gpio *pg = to_palmas_gpio(gc);
+ struct palmas *palmas = pg->palmas;
+
+ return palmas_irq_get_virq(palmas, PALMAS_GPIO_0_IRQ + offset);
+}
+
+static const struct palmas_device_data palmas_dev_data = {
+ .ngpio = 8,
+};
+
+static const struct palmas_device_data tps80036_dev_data = {
+ .ngpio = 16,
+};
+
+static const struct of_device_id of_palmas_gpio_match[] = {
+ { .compatible = "ti,palmas-gpio", .data = &palmas_dev_data,},
+ { .compatible = "ti,tps65913-gpio", .data = &palmas_dev_data,},
+ { .compatible = "ti,tps65914-gpio", .data = &palmas_dev_data,},
+ { .compatible = "ti,tps80036-gpio", .data = &tps80036_dev_data,},
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_palmas_gpio_match);
+
+static int palmas_gpio_probe(struct platform_device *pdev)
+{
+ struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+ struct palmas_platform_data *palmas_pdata;
+ struct palmas_gpio *palmas_gpio;
+ int ret;
+ const struct of_device_id *match;
+ const struct palmas_device_data *dev_data;
+
+ match = of_match_device(of_palmas_gpio_match, &pdev->dev);
+ dev_data = match->data;
+ if (!dev_data)
+ dev_data = &palmas_dev_data;
+
+ palmas_gpio = devm_kzalloc(&pdev->dev,
+ sizeof(*palmas_gpio), GFP_KERNEL);
+ if (!palmas_gpio)
+ return -ENOMEM;
+
+ palmas_gpio->palmas = palmas;
+ palmas_gpio->gpio_chip.owner = THIS_MODULE;
+ palmas_gpio->gpio_chip.label = dev_name(&pdev->dev);
+ palmas_gpio->gpio_chip.ngpio = dev_data->ngpio;
+ palmas_gpio->gpio_chip.can_sleep = true;
+ palmas_gpio->gpio_chip.direction_input = palmas_gpio_input;
+ palmas_gpio->gpio_chip.direction_output = palmas_gpio_output;
+ palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq;
+ palmas_gpio->gpio_chip.set = palmas_gpio_set;
+ palmas_gpio->gpio_chip.get = palmas_gpio_get;
+ palmas_gpio->gpio_chip.dev = &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;
+ else
+ palmas_gpio->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&palmas_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, palmas_gpio);
+ return ret;
+}
+
+static int palmas_gpio_remove(struct platform_device *pdev)
+{
+ struct palmas_gpio *palmas_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&palmas_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver palmas_gpio_driver = {
+ .driver.name = "palmas-gpio",
+ .driver.owner = THIS_MODULE,
+ .driver.of_match_table = of_palmas_gpio_match,
+ .probe = palmas_gpio_probe,
+ .remove = palmas_gpio_remove,
+};
+
+static int __init palmas_gpio_init(void)
+{
+ return platform_driver_register(&palmas_gpio_driver);
+}
+subsys_initcall(palmas_gpio_init);
+
+static void __exit palmas_gpio_exit(void)
+{
+ platform_driver_unregister(&palmas_gpio_driver);
+}
+module_exit(palmas_gpio_exit);
+
+MODULE_ALIAS("platform:palmas-gpio");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("GPIO driver for TI Palmas series PMICs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
new file mode 100644
index 000000000..e2da64abb
--- /dev/null
+++ b/drivers/gpio/gpio-pca953x.c
@@ -0,0 +1,784 @@
+/*
+ * PCA953x 4/8/16/24/40 bit I/O ports
+ *
+ * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
+ * Copyright (C) 2007 Marvell International Ltd.
+ *
+ * Derived from drivers/i2c/chips/pca9539.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/platform_data/pca953x.h>
+#include <linux/slab.h>
+#ifdef CONFIG_OF_GPIO
+#include <linux/of_platform.h>
+#endif
+
+#define PCA953X_INPUT 0
+#define PCA953X_OUTPUT 1
+#define PCA953X_INVERT 2
+#define PCA953X_DIRECTION 3
+
+#define REG_ADDR_AI 0x80
+
+#define PCA957X_IN 0
+#define PCA957X_INVRT 1
+#define PCA957X_BKEN 2
+#define PCA957X_PUPD 3
+#define PCA957X_CFG 4
+#define PCA957X_OUT 5
+#define PCA957X_MSK 6
+#define PCA957X_INTS 7
+
+#define PCA_GPIO_MASK 0x00FF
+#define PCA_INT 0x0100
+#define PCA953X_TYPE 0x1000
+#define PCA957X_TYPE 0x2000
+
+static const struct i2c_device_id pca953x_id[] = {
+ { "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
+ { "pca9534", 8 | PCA953X_TYPE | PCA_INT, },
+ { "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
+ { "pca9536", 4 | PCA953X_TYPE, },
+ { "pca9537", 4 | PCA953X_TYPE | PCA_INT, },
+ { "pca9538", 8 | PCA953X_TYPE | PCA_INT, },
+ { "pca9539", 16 | PCA953X_TYPE | PCA_INT, },
+ { "pca9554", 8 | PCA953X_TYPE | PCA_INT, },
+ { "pca9555", 16 | PCA953X_TYPE | PCA_INT, },
+ { "pca9556", 8 | PCA953X_TYPE, },
+ { "pca9557", 8 | PCA953X_TYPE, },
+ { "pca9574", 8 | PCA957X_TYPE | PCA_INT, },
+ { "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
+ { "pca9698", 40 | PCA953X_TYPE, },
+
+ { "max7310", 8 | PCA953X_TYPE, },
+ { "max7312", 16 | PCA953X_TYPE | PCA_INT, },
+ { "max7313", 16 | PCA953X_TYPE | PCA_INT, },
+ { "max7315", 8 | PCA953X_TYPE | PCA_INT, },
+ { "pca6107", 8 | PCA953X_TYPE | PCA_INT, },
+ { "tca6408", 8 | PCA953X_TYPE | PCA_INT, },
+ { "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
+ { "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
+ { "xra1202", 8 | PCA953X_TYPE },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca953x_id);
+
+#define MAX_BANK 5
+#define BANK_SZ 8
+
+#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ)
+
+struct pca953x_chip {
+ unsigned gpio_start;
+ u8 reg_output[MAX_BANK];
+ u8 reg_direction[MAX_BANK];
+ struct mutex i2c_lock;
+
+#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];
+#endif
+
+ struct i2c_client *client;
+ struct gpio_chip gpio_chip;
+ const char *const *names;
+ int chip_type;
+};
+
+static inline struct pca953x_chip *to_pca(struct gpio_chip *gc)
+{
+ return container_of(gc, struct pca953x_chip, gpio_chip);
+}
+
+static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
+ int off)
+{
+ int ret;
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ int offset = off / BANK_SZ;
+
+ ret = i2c_smbus_read_byte_data(chip->client,
+ (reg << bank_shift) + offset);
+ *val = ret;
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed reading register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
+ int off)
+{
+ int ret = 0;
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ int offset = off / BANK_SZ;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ (reg << bank_shift) + offset, val);
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed writing register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
+{
+ int ret = 0;
+
+ if (chip->gpio_chip.ngpio <= 8)
+ ret = i2c_smbus_write_byte_data(chip->client, reg, *val);
+ else if (chip->gpio_chip.ngpio >= 24) {
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ ret = i2c_smbus_write_i2c_block_data(chip->client,
+ (reg << bank_shift) | REG_ADDR_AI,
+ NBANK(chip), val);
+ } else {
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ ret = i2c_smbus_write_word_data(chip->client,
+ reg << 1, (u16) *val);
+ break;
+ case PCA957X_TYPE:
+ ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
+ val[0]);
+ if (ret < 0)
+ break;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ (reg << 1) + 1,
+ val[1]);
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed writing register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
+{
+ int ret;
+
+ if (chip->gpio_chip.ngpio <= 8) {
+ ret = i2c_smbus_read_byte_data(chip->client, reg);
+ *val = ret;
+ } else if (chip->gpio_chip.ngpio >= 24) {
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+
+ ret = i2c_smbus_read_i2c_block_data(chip->client,
+ (reg << bank_shift) | REG_ADDR_AI,
+ NBANK(chip), val);
+ } else {
+ ret = i2c_smbus_read_word_data(chip->client, reg << 1);
+ val[0] = (u16)ret & 0xFF;
+ val[1] = (u16)ret >> 8;
+ }
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed reading register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u8 reg_val;
+ int ret, offset = 0;
+
+ mutex_lock(&chip->i2c_lock);
+ reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ));
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_DIRECTION;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_CFG;
+ break;
+ }
+ ret = pca953x_write_single(chip, offset, reg_val, off);
+ if (ret)
+ goto exit;
+
+ chip->reg_direction[off / BANK_SZ] = reg_val;
+ ret = 0;
+exit:
+ mutex_unlock(&chip->i2c_lock);
+ return ret;
+}
+
+static int pca953x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned off, int val)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u8 reg_val;
+ int ret, offset = 0;
+
+ mutex_lock(&chip->i2c_lock);
+ /* set output level */
+ if (val)
+ reg_val = chip->reg_output[off / BANK_SZ]
+ | (1u << (off % BANK_SZ));
+ else
+ reg_val = chip->reg_output[off / BANK_SZ]
+ & ~(1u << (off % BANK_SZ));
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_OUTPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_OUT;
+ break;
+ }
+ ret = pca953x_write_single(chip, offset, reg_val, off);
+ if (ret)
+ goto exit;
+
+ chip->reg_output[off / BANK_SZ] = reg_val;
+
+ /* then direction */
+ reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ));
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_DIRECTION;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_CFG;
+ break;
+ }
+ ret = pca953x_write_single(chip, offset, reg_val, off);
+ if (ret)
+ goto exit;
+
+ chip->reg_direction[off / BANK_SZ] = reg_val;
+ ret = 0;
+exit:
+ mutex_unlock(&chip->i2c_lock);
+ return ret;
+}
+
+static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u32 reg_val;
+ int ret, offset = 0;
+
+ mutex_lock(&chip->i2c_lock);
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_INPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_IN;
+ break;
+ }
+ ret = pca953x_read_single(chip, offset, &reg_val, off);
+ 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;
+ }
+
+ return (reg_val & (1u << (off % BANK_SZ))) ? 1 : 0;
+}
+
+static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
+{
+ struct pca953x_chip *chip = to_pca(gc);
+ u8 reg_val;
+ int ret, offset = 0;
+
+ mutex_lock(&chip->i2c_lock);
+ if (val)
+ reg_val = chip->reg_output[off / BANK_SZ]
+ | (1u << (off % BANK_SZ));
+ else
+ reg_val = chip->reg_output[off / BANK_SZ]
+ & ~(1u << (off % BANK_SZ));
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_OUTPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_OUT;
+ break;
+ }
+ ret = pca953x_write_single(chip, offset, reg_val, off);
+ if (ret)
+ goto exit;
+
+ chip->reg_output[off / BANK_SZ] = reg_val;
+exit:
+ mutex_unlock(&chip->i2c_lock);
+}
+
+static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
+{
+ struct gpio_chip *gc;
+
+ gc = &chip->gpio_chip;
+
+ gc->direction_input = pca953x_gpio_direction_input;
+ gc->direction_output = pca953x_gpio_direction_output;
+ gc->get = pca953x_gpio_get_value;
+ gc->set = pca953x_gpio_set_value;
+ gc->can_sleep = true;
+
+ gc->base = chip->gpio_start;
+ gc->ngpio = gpios;
+ gc->label = chip->client->name;
+ gc->dev = &chip->client->dev;
+ gc->owner = THIS_MODULE;
+ gc->names = chip->names;
+}
+
+#ifdef CONFIG_GPIO_PCA953X_IRQ
+static void pca953x_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pca953x_chip *chip = to_pca(gc);
+
+ chip->irq_mask[d->hwirq / BANK_SZ] &= ~(1 << (d->hwirq % BANK_SZ));
+}
+
+static void pca953x_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pca953x_chip *chip = to_pca(gc);
+
+ chip->irq_mask[d->hwirq / BANK_SZ] |= 1 << (d->hwirq % BANK_SZ);
+}
+
+static void pca953x_irq_bus_lock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pca953x_chip *chip = to_pca(gc);
+
+ mutex_lock(&chip->irq_lock);
+}
+
+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 = to_pca(gc);
+ u8 new_irqs;
+ int level, i;
+
+ /* 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 &= ~chip->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);
+ }
+ }
+
+ mutex_unlock(&chip->irq_lock);
+}
+
+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 = to_pca(gc);
+ int bank_nb = d->hwirq / BANK_SZ;
+ u8 mask = 1 << (d->hwirq % BANK_SZ);
+
+ if (!(type & IRQ_TYPE_EDGE_BOTH)) {
+ dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
+ d->irq, 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;
+
+ return 0;
+}
+
+static struct irq_chip pca953x_irq_chip = {
+ .name = "pca953x",
+ .irq_mask = pca953x_irq_mask,
+ .irq_unmask = pca953x_irq_unmask,
+ .irq_bus_lock = pca953x_irq_bus_lock,
+ .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock,
+ .irq_set_type = pca953x_irq_set_type,
+};
+
+static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
+{
+ u8 cur_stat[MAX_BANK];
+ u8 old_stat[MAX_BANK];
+ u8 pendings = 0;
+ u8 trigger[MAX_BANK], triggers = 0;
+ int ret, i, offset = 0;
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_INPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_IN;
+ break;
+ }
+ ret = pca953x_read_regs(chip, offset, cur_stat);
+ if (ret)
+ return 0;
+
+ /* Remove output pins from the equation */
+ for (i = 0; i < NBANK(chip); i++)
+ cur_stat[i] &= chip->reg_direction[i];
+
+ memcpy(old_stat, chip->irq_stat, NBANK(chip));
+
+ for (i = 0; i < NBANK(chip); i++) {
+ trigger[i] = (cur_stat[i] ^ old_stat[i]) & chip->irq_mask[i];
+ triggers += trigger[i];
+ }
+
+ if (!triggers)
+ return 0;
+
+ memcpy(chip->irq_stat, cur_stat, NBANK(chip));
+
+ 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];
+ pendings += pending[i];
+ }
+
+ return pendings;
+}
+
+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.irqdomain,
+ level + (BANK_SZ * i)));
+ pending[i] &= ~(1 << level);
+ nhandled++;
+ }
+ }
+
+ return (nhandled > 0) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int pca953x_irq_setup(struct pca953x_chip *chip,
+ const struct i2c_device_id *id,
+ int irq_base)
+{
+ struct i2c_client *client = chip->client;
+ int ret, i, offset = 0;
+
+ if (client->irq && irq_base != -1
+ && (id->driver_data & PCA_INT)) {
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_INPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_IN;
+ break;
+ }
+ ret = pca953x_read_regs(chip, offset, chip->irq_stat);
+ if (ret)
+ return ret;
+
+ /*
+ * There is no way to know which GPIO line generated the
+ * interrupt. We have to rely on the previous read for
+ * this purpose.
+ */
+ for (i = 0; i < NBANK(chip); i++)
+ chip->irq_stat[i] &= chip->reg_direction[i];
+ mutex_init(&chip->irq_lock);
+
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL,
+ pca953x_irq_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT |
+ IRQF_SHARED,
+ dev_name(&client->dev), chip);
+ if (ret) {
+ dev_err(&client->dev, "failed to request irq %d\n",
+ client->irq);
+ return ret;
+ }
+
+ ret = gpiochip_irqchip_add(&chip->gpio_chip,
+ &pca953x_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;
+ }
+ }
+
+ return 0;
+}
+
+#else /* CONFIG_GPIO_PCA953X_IRQ */
+static int pca953x_irq_setup(struct pca953x_chip *chip,
+ const struct i2c_device_id *id,
+ int irq_base)
+{
+ struct i2c_client *client = chip->client;
+
+ if (irq_base != -1 && (id->driver_data & PCA_INT))
+ dev_warn(&client->dev, "interrupt support not compiled in\n");
+
+ return 0;
+}
+#endif
+
+static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
+{
+ int ret;
+ u8 val[MAX_BANK];
+
+ ret = pca953x_read_regs(chip, PCA953X_OUTPUT, chip->reg_output);
+ if (ret)
+ goto out;
+
+ ret = pca953x_read_regs(chip, PCA953X_DIRECTION,
+ chip->reg_direction);
+ if (ret)
+ goto out;
+
+ /* set platform specific polarity inversion */
+ if (invert)
+ memset(val, 0xFF, NBANK(chip));
+ else
+ memset(val, 0, NBANK(chip));
+
+ ret = pca953x_write_regs(chip, PCA953X_INVERT, val);
+out:
+ return ret;
+}
+
+static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
+{
+ int ret;
+ u8 val[MAX_BANK];
+
+ ret = pca953x_read_regs(chip, PCA957X_OUT, chip->reg_output);
+ if (ret)
+ goto out;
+ ret = pca953x_read_regs(chip, PCA957X_CFG, chip->reg_direction);
+ if (ret)
+ goto out;
+
+ /* set platform specific polarity inversion */
+ if (invert)
+ memset(val, 0xFF, NBANK(chip));
+ else
+ memset(val, 0, NBANK(chip));
+ pca953x_write_regs(chip, PCA957X_INVRT, val);
+
+ /* To enable register 6, 7 to controll pull up and pull down */
+ memset(val, 0x02, NBANK(chip));
+ pca953x_write_regs(chip, PCA957X_BKEN, val);
+
+ return 0;
+out:
+ return ret;
+}
+
+static int pca953x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pca953x_platform_data *pdata;
+ struct pca953x_chip *chip;
+ int irq_base = 0;
+ int ret;
+ u32 invert = 0;
+
+ chip = devm_kzalloc(&client->dev,
+ sizeof(struct pca953x_chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (pdata) {
+ irq_base = pdata->irq_base;
+ chip->gpio_start = pdata->gpio_base;
+ invert = pdata->invert;
+ chip->names = pdata->names;
+ } else {
+ chip->gpio_start = -1;
+ irq_base = 0;
+ }
+
+ chip->client = client;
+
+ chip->chip_type = id->driver_data & (PCA953X_TYPE | PCA957X_TYPE);
+
+ mutex_init(&chip->i2c_lock);
+
+ /* initialize cached registers from their original values.
+ * we can't share this chip with another i2c master.
+ */
+ pca953x_setup_gpio(chip, id->driver_data & PCA_GPIO_MASK);
+
+ if (chip->chip_type == PCA953X_TYPE)
+ ret = device_pca953x_init(chip, invert);
+ else
+ ret = device_pca957x_init(chip, invert);
+ if (ret)
+ return ret;
+
+ ret = gpiochip_add(&chip->gpio_chip);
+ if (ret)
+ return ret;
+
+ ret = pca953x_irq_setup(chip, id, irq_base);
+ 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 pca953x_remove(struct i2c_client *client)
+{
+ struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct pca953x_chip *chip = i2c_get_clientdata(client);
+ int ret = 0;
+
+ if (pdata && pdata->teardown) {
+ 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;
+ }
+ }
+
+ gpiochip_remove(&chip->gpio_chip);
+
+ return 0;
+}
+
+static const struct of_device_id pca953x_dt_ids[] = {
+ { .compatible = "nxp,pca9505", },
+ { .compatible = "nxp,pca9534", },
+ { .compatible = "nxp,pca9535", },
+ { .compatible = "nxp,pca9536", },
+ { .compatible = "nxp,pca9537", },
+ { .compatible = "nxp,pca9538", },
+ { .compatible = "nxp,pca9539", },
+ { .compatible = "nxp,pca9554", },
+ { .compatible = "nxp,pca9555", },
+ { .compatible = "nxp,pca9556", },
+ { .compatible = "nxp,pca9557", },
+ { .compatible = "nxp,pca9574", },
+ { .compatible = "nxp,pca9575", },
+ { .compatible = "nxp,pca9698", },
+
+ { .compatible = "maxim,max7310", },
+ { .compatible = "maxim,max7312", },
+ { .compatible = "maxim,max7313", },
+ { .compatible = "maxim,max7315", },
+
+ { .compatible = "ti,pca6107", },
+ { .compatible = "ti,tca6408", },
+ { .compatible = "ti,tca6416", },
+ { .compatible = "ti,tca6424", },
+
+ { .compatible = "exar,xra1202", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
+
+static struct i2c_driver pca953x_driver = {
+ .driver = {
+ .name = "pca953x",
+ .of_match_table = pca953x_dt_ids,
+ },
+ .probe = pca953x_probe,
+ .remove = pca953x_remove,
+ .id_table = pca953x_id,
+};
+
+static int __init pca953x_init(void)
+{
+ return i2c_add_driver(&pca953x_driver);
+}
+/* register after i2c postcore initcall and before
+ * subsys initcalls that may rely on these GPIOs
+ */
+subsys_initcall(pca953x_init);
+
+static void __exit pca953x_exit(void)
+{
+ i2c_del_driver(&pca953x_driver);
+}
+module_exit(pca953x_exit);
+
+MODULE_AUTHOR("eric miao <eric.miao@marvell.com>");
+MODULE_DESCRIPTION("GPIO expander driver for PCA953x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
new file mode 100644
index 000000000..945f0cda8
--- /dev/null
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -0,0 +1,442 @@
+/*
+ * Driver for pcf857x, pca857x, and pca967x I2C GPIO expanders
+ *
+ * Copyright (C) 2007 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/i2c/pcf857x.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+
+static const struct i2c_device_id pcf857x_id[] = {
+ { "pcf8574", 8 },
+ { "pcf8574a", 8 },
+ { "pca8574", 8 },
+ { "pca9670", 8 },
+ { "pca9672", 8 },
+ { "pca9674", 8 },
+ { "pcf8575", 16 },
+ { "pca8575", 16 },
+ { "pca9671", 16 },
+ { "pca9673", 16 },
+ { "pca9675", 16 },
+ { "max7328", 8 },
+ { "max7329", 8 },
+ { "tca9554", 8 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf857x_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf857x_of_table[] = {
+ { .compatible = "nxp,pcf8574" },
+ { .compatible = "nxp,pcf8574a" },
+ { .compatible = "nxp,pca8574" },
+ { .compatible = "nxp,pca9670" },
+ { .compatible = "nxp,pca9672" },
+ { .compatible = "nxp,pca9674" },
+ { .compatible = "nxp,pcf8575" },
+ { .compatible = "nxp,pca8575" },
+ { .compatible = "nxp,pca9671" },
+ { .compatible = "nxp,pca9673" },
+ { .compatible = "nxp,pca9675" },
+ { .compatible = "maxim,max7328" },
+ { .compatible = "maxim,max7329" },
+ { .compatible = "ti,tca9554" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcf857x_of_table);
+#endif
+
+/*
+ * The pcf857x, pca857x, and pca967x chips only expose one read and one
+ * write register. Writing a "one" bit (to match the reset state) lets
+ * that pin be used as an input; it's not an open-drain model, but acts
+ * a bit like one. This is described as "quasi-bidirectional"; read the
+ * chip documentation for details.
+ *
+ * Many other I2C GPIO expander chips (like the pca953x models) have
+ * more complex register models and more conventional circuitry using
+ * push/pull drivers. They often use the same 0x20..0x27 addresses as
+ * pcf857x parts, making the "legacy" I2C driver model problematic.
+ */
+struct pcf857x {
+ struct gpio_chip chip;
+ struct i2c_client *client;
+ struct mutex lock; /* protect 'out' */
+ spinlock_t slock; /* protect irq demux */
+ unsigned out; /* software latch */
+ unsigned status; /* current status */
+
+ int (*write)(struct i2c_client *client, unsigned data);
+ int (*read)(struct i2c_client *client);
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Talk to 8-bit I/O expander */
+
+static int i2c_write_le8(struct i2c_client *client, unsigned data)
+{
+ return i2c_smbus_write_byte(client, data);
+}
+
+static int i2c_read_le8(struct i2c_client *client)
+{
+ return (int)i2c_smbus_read_byte(client);
+}
+
+/* Talk to 16-bit I/O expander */
+
+static int i2c_write_le16(struct i2c_client *client, unsigned word)
+{
+ u8 buf[2] = { word & 0xff, word >> 8, };
+ int status;
+
+ status = i2c_master_send(client, buf, 2);
+ return (status < 0) ? status : 0;
+}
+
+static int i2c_read_le16(struct i2c_client *client)
+{
+ u8 buf[2];
+ int status;
+
+ status = i2c_master_recv(client, buf, 2);
+ if (status < 0)
+ return status;
+ return (buf[1] << 8) | buf[0];
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pcf857x_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+ int status;
+
+ mutex_lock(&gpio->lock);
+ gpio->out |= (1 << offset);
+ status = gpio->write(gpio->client, gpio->out);
+ mutex_unlock(&gpio->lock);
+
+ return status;
+}
+
+static int pcf857x_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+ int value;
+
+ value = gpio->read(gpio->client);
+ return (value < 0) ? 0 : (value & (1 << offset));
+}
+
+static int pcf857x_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+ unsigned bit = 1 << offset;
+ int status;
+
+ mutex_lock(&gpio->lock);
+ if (value)
+ gpio->out |= bit;
+ else
+ gpio->out &= ~bit;
+ status = gpio->write(gpio->client, gpio->out);
+ mutex_unlock(&gpio->lock);
+
+ return status;
+}
+
+static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ pcf857x_output(chip, offset, value);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static irqreturn_t pcf857x_irq(int irq, void *data)
+{
+ struct pcf857x *gpio = data;
+ unsigned long change, i, status, flags;
+
+ status = gpio->read(gpio->client);
+
+ spin_lock_irqsave(&gpio->slock, flags);
+
+ /*
+ * call the interrupt handler iff gpio is used as
+ * interrupt source, just to avoid bad irqs
+ */
+
+ change = (gpio->status ^ status);
+ for_each_set_bit(i, &change, gpio->chip.ngpio)
+ handle_nested_irq(irq_find_mapping(gpio->chip.irqdomain, i));
+ gpio->status = status;
+
+ spin_unlock_irqrestore(&gpio->slock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * NOP functions
+ */
+static void noop(struct irq_data *data) { }
+
+static unsigned int noop_ret(struct irq_data *data)
+{
+ return 0;
+}
+
+static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+ irq_set_irq_wake(gpio->client->irq, on);
+ return 0;
+}
+
+static struct irq_chip pcf857x_irq_chip = {
+ .name = "pcf857x",
+ .irq_startup = noop_ret,
+ .irq_shutdown = noop,
+ .irq_enable = noop,
+ .irq_disable = noop,
+ .irq_ack = noop,
+ .irq_mask = noop,
+ .irq_unmask = noop,
+ .irq_set_wake = pcf857x_irq_set_wake,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int pcf857x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf857x_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct device_node *np = client->dev.of_node;
+ struct pcf857x *gpio;
+ unsigned int n_latch = 0;
+ int status;
+
+ if (IS_ENABLED(CONFIG_OF) && np)
+ of_property_read_u32(np, "lines-initial-states", &n_latch);
+ else if (pdata)
+ n_latch = pdata->n_latch;
+ else
+ dev_dbg(&client->dev, "no platform data\n");
+
+ /* Allocate, initialize, and register this gpio_chip. */
+ gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ mutex_init(&gpio->lock);
+ spin_lock_init(&gpio->slock);
+
+ gpio->chip.base = pdata ? pdata->gpio_base : -1;
+ gpio->chip.can_sleep = true;
+ gpio->chip.dev = &client->dev;
+ gpio->chip.owner = THIS_MODULE;
+ gpio->chip.get = pcf857x_get;
+ gpio->chip.set = pcf857x_set;
+ gpio->chip.direction_input = pcf857x_input;
+ gpio->chip.direction_output = pcf857x_output;
+ gpio->chip.ngpio = id->driver_data;
+
+ /* NOTE: the OnSemi jlc1562b is also largely compatible with
+ * these parts, notably for output. It has a low-resolution
+ * DAC instead of pin change IRQs; and its inputs can be the
+ * result of comparators.
+ */
+
+ /* 8574 addresses are 0x20..0x27; 8574a uses 0x38..0x3f;
+ * 9670, 9672, 9764, and 9764a use quite a variety.
+ *
+ * NOTE: we don't distinguish here between *4 and *4a parts.
+ */
+ if (gpio->chip.ngpio == 8) {
+ gpio->write = i2c_write_le8;
+ gpio->read = i2c_read_le8;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE))
+ status = -EIO;
+
+ /* fail if there's no chip present */
+ else
+ status = i2c_smbus_read_byte(client);
+
+ /* '75/'75c addresses are 0x20..0x27, just like the '74;
+ * the '75c doesn't have a current source pulling high.
+ * 9671, 9673, and 9765 use quite a variety of addresses.
+ *
+ * NOTE: we don't distinguish here between '75 and '75c parts.
+ */
+ } else if (gpio->chip.ngpio == 16) {
+ gpio->write = i2c_write_le16;
+ gpio->read = i2c_read_le16;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ status = -EIO;
+
+ /* fail if there's no chip present */
+ else
+ status = i2c_read_le16(client);
+
+ } else {
+ dev_dbg(&client->dev, "unsupported number of gpios\n");
+ status = -EINVAL;
+ }
+
+ if (status < 0)
+ goto fail;
+
+ gpio->chip.label = client->name;
+
+ gpio->client = client;
+ i2c_set_clientdata(client, gpio);
+
+ /* NOTE: these chips have strange "quasi-bidirectional" I/O pins.
+ * We can't actually know whether a pin is configured (a) as output
+ * and driving the signal low, or (b) as input and reporting a low
+ * value ... without knowing the last value written since the chip
+ * came out of reset (if any). We can't read the latched output.
+ *
+ * In short, the only reliable solution for setting up pin direction
+ * is to do it explicitly. The setup() method can do that, but it
+ * may cause transient glitching since it can't know the last value
+ * written (some pins may need to be driven low).
+ *
+ * Using n_latch avoids that trouble. When left initialized to zero,
+ * our software copy of the "latch" then matches the chip's all-ones
+ * reset state. Otherwise it flags pins to be driven low.
+ */
+ gpio->out = ~n_latch;
+ gpio->status = gpio->out;
+
+ status = gpiochip_add(&gpio->chip);
+ if (status < 0)
+ goto fail;
+
+ /* Enable irqchip if we have an interrupt */
+ if (client->irq) {
+ status = gpiochip_irqchip_add(&gpio->chip, &pcf857x_irq_chip,
+ 0, handle_level_irq,
+ IRQ_TYPE_NONE);
+ if (status) {
+ dev_err(&client->dev, "cannot add irqchip\n");
+ goto fail_irq;
+ }
+
+ status = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, pcf857x_irq, IRQF_ONESHOT |
+ IRQF_TRIGGER_FALLING | IRQF_SHARED,
+ dev_name(&client->dev), gpio);
+ if (status)
+ goto fail_irq;
+
+ gpiochip_set_chained_irqchip(&gpio->chip, &pcf857x_irq_chip,
+ client->irq, NULL);
+ }
+
+ /* Let platform code set up the GPIOs and their users.
+ * Now is the first time anyone could use them.
+ */
+ if (pdata && pdata->setup) {
+ status = pdata->setup(client,
+ gpio->chip.base, gpio->chip.ngpio,
+ pdata->context);
+ if (status < 0)
+ dev_warn(&client->dev, "setup --> %d\n", status);
+ }
+
+ dev_info(&client->dev, "probed\n");
+
+ return 0;
+
+fail_irq:
+ gpiochip_remove(&gpio->chip);
+
+fail:
+ dev_dbg(&client->dev, "probe error %d for '%s'\n", status,
+ client->name);
+
+ return status;
+}
+
+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,
+ pdata->context);
+ if (status < 0) {
+ dev_err(&client->dev, "%s --> %d\n",
+ "teardown", status);
+ return status;
+ }
+ }
+
+ gpiochip_remove(&gpio->chip);
+ return status;
+}
+
+static struct i2c_driver pcf857x_driver = {
+ .driver = {
+ .name = "pcf857x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf857x_of_table),
+ },
+ .probe = pcf857x_probe,
+ .remove = pcf857x_remove,
+ .id_table = pcf857x_id,
+};
+
+static int __init pcf857x_init(void)
+{
+ return i2c_add_driver(&pcf857x_driver);
+}
+/* register after i2c postcore initcall and before
+ * subsys initcalls that may rely on these GPIOs
+ */
+subsys_initcall(pcf857x_init);
+
+static void __exit pcf857x_exit(void)
+{
+ i2c_del_driver(&pcf857x_driver);
+}
+module_exit(pcf857x_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
new file mode 100644
index 000000000..2d9a950ca
--- /dev/null
+++ b/drivers/gpio/gpio-pch.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#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_IRQ_BASE 24
+
+struct pch_regs {
+ u32 ien;
+ u32 istatus;
+ u32 idisp;
+ u32 iclr;
+ u32 imask;
+ u32 imaskclr;
+ u32 po;
+ u32 pi;
+ u32 pm;
+ u32 im0;
+ u32 im1;
+ u32 reserved[3];
+ u32 gpio_use_sel;
+ u32 reset;
+};
+
+enum pch_type_t {
+ INTEL_EG20T_PCH,
+ OKISEMI_ML7223m_IOH, /* LAPIS Semiconductor ML7223 IOH PCIe Bus-m */
+ OKISEMI_ML7223n_IOH /* LAPIS Semiconductor ML7223 IOH PCIe Bus-n */
+};
+
+/* Specifies number of GPIO PINS */
+static int gpio_pins[] = {
+ [INTEL_EG20T_PCH] = 12,
+ [OKISEMI_ML7223m_IOH] = 8,
+ [OKISEMI_ML7223n_IOH] = 8,
+};
+
+/**
+ * struct pch_gpio_reg_data - The register store data.
+ * @ien_reg: To store contents of IEN register.
+ * @imask_reg: To store contents of IMASK register.
+ * @po_reg: To store contents of PO register.
+ * @pm_reg: To store contents of PM register.
+ * @im0_reg: To store contents of IM0 register.
+ * @im1_reg: To store contents of IM1 register.
+ * @gpio_use_sel_reg : To store contents of GPIO_USE_SEL register.
+ * (Only ML7223 Bus-n)
+ */
+struct pch_gpio_reg_data {
+ u32 ien_reg;
+ u32 imask_reg;
+ u32 po_reg;
+ u32 pm_reg;
+ u32 im0_reg;
+ u32 im1_reg;
+ u32 gpio_use_sel_reg;
+};
+
+/**
+ * struct pch_gpio - GPIO private data structure.
+ * @base: PCI base address of Memory mapped I/O register.
+ * @reg: Memory mapped PCH GPIO register list.
+ * @dev: Pointer to device structure.
+ * @gpio: Data for GPIO infrastructure.
+ * @pch_gpio_reg: Memory mapped Register data is saved here
+ * when suspend.
+ * @lock: Used for register access protection
+ * @irq_base: Save base of IRQ number for interrupt
+ * @ioh: IOH ID
+ * @spinlock: Used for register access protection
+ */
+struct pch_gpio {
+ void __iomem *base;
+ struct pch_regs __iomem *reg;
+ struct device *dev;
+ struct gpio_chip gpio;
+ struct pch_gpio_reg_data pch_gpio_reg;
+ int irq_base;
+ enum pch_type_t ioh;
+ spinlock_t spinlock;
+};
+
+static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
+{
+ u32 reg_val;
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ reg_val = ioread32(&chip->reg->po);
+ if (val)
+ reg_val |= (1 << nr);
+ else
+ reg_val &= ~(1 << nr);
+
+ iowrite32(reg_val, &chip->reg->po);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+
+ return ioread32(&chip->reg->pi) & (1 << nr);
+}
+
+static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+ int val)
+{
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+ u32 pm;
+ u32 reg_val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+
+ reg_val = ioread32(&chip->reg->po);
+ if (val)
+ reg_val |= (1 << nr);
+ else
+ reg_val &= ~(1 << nr);
+ iowrite32(reg_val, &chip->reg->po);
+
+ pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
+ pm |= (1 << nr);
+ iowrite32(pm, &chip->reg->pm);
+
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, 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);
+ iowrite32(pm, &chip->reg->pm);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Save register configuration and disable interrupts.
+ */
+static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
+{
+ chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien);
+ chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask);
+ chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po);
+ chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm);
+ chip->pch_gpio_reg.im0_reg = ioread32(&chip->reg->im0);
+ if (chip->ioh == INTEL_EG20T_PCH)
+ chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1);
+ if (chip->ioh == OKISEMI_ML7223n_IOH)
+ chip->pch_gpio_reg.gpio_use_sel_reg =\
+ ioread32(&chip->reg->gpio_use_sel);
+}
+
+/*
+ * This function restores the register configuration of the GPIO device.
+ */
+static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
+{
+ iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien);
+ iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask);
+ /* to store contents of PO register */
+ iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po);
+ /* to store contents of PM register */
+ iowrite32(chip->pch_gpio_reg.pm_reg, &chip->reg->pm);
+ iowrite32(chip->pch_gpio_reg.im0_reg, &chip->reg->im0);
+ if (chip->ioh == INTEL_EG20T_PCH)
+ iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1);
+ if (chip->ioh == OKISEMI_ML7223n_IOH)
+ iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg,
+ &chip->reg->gpio_use_sel);
+}
+#endif
+
+static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+ return chip->irq_base + offset;
+}
+
+static void pch_gpio_setup(struct pch_gpio *chip)
+{
+ struct gpio_chip *gpio = &chip->gpio;
+
+ gpio->label = dev_name(chip->dev);
+ gpio->dev = chip->dev;
+ gpio->owner = THIS_MODULE;
+ gpio->direction_input = pch_gpio_direction_input;
+ gpio->get = pch_gpio_get;
+ gpio->direction_output = pch_gpio_direction_output;
+ gpio->set = pch_gpio_set;
+ gpio->dbg_show = NULL;
+ gpio->base = -1;
+ gpio->ngpio = gpio_pins[chip->ioh];
+ gpio->can_sleep = false;
+ gpio->to_irq = pch_gpio_to_irq;
+}
+
+static int pch_irq_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct pch_gpio *chip = gc->private;
+ u32 im, im_pos, val;
+ u32 __iomem *im_reg;
+ unsigned long flags;
+ int ch, irq = d->irq;
+
+ ch = irq - chip->irq_base;
+ if (irq <= chip->irq_base + 7) {
+ im_reg = &chip->reg->im0;
+ im_pos = ch;
+ } else {
+ im_reg = &chip->reg->im1;
+ im_pos = ch - 8;
+ }
+ dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d\n",
+ __func__, irq, type, ch, im_pos);
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ val = PCH_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ val = PCH_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = PCH_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val = PCH_LEVEL_H;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val = PCH_LEVEL_L;
+ break;
+ default:
+ goto unlock;
+ }
+
+ /* 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))
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+
+unlock:
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+ return 0;
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
+{
+ struct pch_gpio *chip = dev_id;
+ u32 reg_val = ioread32(&chip->reg->istatus);
+ int i, ret = IRQ_NONE;
+
+ for (i = 0; i < gpio_pins[chip->ioh]; i++) {
+ if (reg_val & BIT(i)) {
+ dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n",
+ __func__, i, irq, reg_val);
+ generic_handle_irq(chip->irq_base + i);
+ ret = IRQ_HANDLED;
+ }
+ }
+ return ret;
+}
+
+static void pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
+ unsigned int irq_start, unsigned int num)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base,
+ handle_simple_irq);
+ gc->private = chip;
+ ct = gc->chip_types;
+
+ ct->chip.irq_ack = pch_irq_ack;
+ ct->chip.irq_mask = pch_irq_mask;
+ ct->chip.irq_unmask = pch_irq_unmask;
+ ct->chip.irq_set_type = pch_irq_type;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST | IRQ_NOPROBE, 0);
+}
+
+static int pch_gpio_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ s32 ret;
+ struct pch_gpio *chip;
+ int irq_base;
+ u32 msk;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__);
+ goto err_pci_enable;
+ }
+
+ ret = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (ret) {
+ dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret);
+ goto err_request_regions;
+ }
+
+ chip->base = pci_iomap(pdev, 1, 0);
+ if (!chip->base) {
+ dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__);
+ ret = -ENOMEM;
+ goto err_iomap;
+ }
+
+ 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->reg = chip->base;
+ pci_set_drvdata(pdev, chip);
+ spin_lock_init(&chip->spinlock);
+ pch_gpio_setup(chip);
+ ret = gpiochip_add(&chip->gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n");
+ goto err_gpiochip_add;
+ }
+
+ irq_base = irq_alloc_descs(-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");
+ chip->irq_base = -1;
+ goto end;
+ }
+ 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);
+
+ ret = request_irq(pdev->irq, pch_gpio_handler,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (ret != 0) {
+ dev_err(&pdev->dev,
+ "%s request_irq failed\n", __func__);
+ goto err_request_irq;
+ }
+
+ pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
+
+end:
+ return 0;
+
+err_request_irq:
+ irq_free_descs(irq_base, gpio_pins[chip->ioh]);
+ gpiochip_remove(&chip->gpio);
+
+err_gpiochip_add:
+ pci_iounmap(pdev, chip->base);
+
+err_iomap:
+ pci_release_regions(pdev);
+
+err_request_regions:
+ pci_disable_device(pdev);
+
+err_pci_enable:
+ kfree(chip);
+ dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret);
+ return ret;
+}
+
+static void pch_gpio_remove(struct pci_dev *pdev)
+{
+ struct pch_gpio *chip = pci_get_drvdata(pdev);
+
+ if (chip->irq_base != -1) {
+ free_irq(pdev->irq, chip);
+
+ irq_free_descs(chip->irq_base, gpio_pins[chip->ioh]);
+ }
+
+ gpiochip_remove(&chip->gpio);
+ pci_iounmap(pdev, chip->base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ kfree(chip);
+}
+
+#ifdef CONFIG_PM
+static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ s32 ret;
+ struct pch_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ pch_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 pch_gpio_resume(struct pci_dev *pdev)
+{
+ s32 ret;
+ struct pch_gpio *chip = pci_get_drvdata(pdev);
+ 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->reset);
+ iowrite32(0x00, &chip->reg->reset);
+ pch_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+#else
+#define pch_gpio_suspend NULL
+#define pch_gpio_resume NULL
+#endif
+
+#define PCI_VENDOR_ID_ROHM 0x10DB
+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) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id);
+
+static struct pci_driver pch_gpio_driver = {
+ .name = "pch_gpio",
+ .id_table = pch_gpio_pcidev_id,
+ .probe = pch_gpio_probe,
+ .remove = pch_gpio_remove,
+ .suspend = pch_gpio_suspend,
+ .resume = pch_gpio_resume
+};
+
+module_pci_driver(pch_gpio_driver);
+
+MODULE_DESCRIPTION("PCH GPIO PCI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
new file mode 100644
index 000000000..047561304
--- /dev/null
+++ b/drivers/gpio/gpio-pl061.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2008, 2009 Provigent Ltd.
+ *
+ * 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.
+ *
+ * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)
+ *
+ * Data sheet: ARM DDI 0190B, September 2000
+ */
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl061.h>
+#include <linux/slab.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm.h>
+
+#define GPIODIR 0x400
+#define GPIOIS 0x404
+#define GPIOIBE 0x408
+#define GPIOIEV 0x40C
+#define GPIOIE 0x410
+#define GPIORIS 0x414
+#define GPIOMIS 0x418
+#define GPIOIC 0x41C
+
+#define PL061_GPIO_NR 8
+
+#ifdef CONFIG_PM
+struct pl061_context_save_regs {
+ u8 gpio_data;
+ u8 gpio_dir;
+ u8 gpio_is;
+ u8 gpio_ibe;
+ u8 gpio_iev;
+ u8 gpio_ie;
+};
+#endif
+
+struct pl061_gpio {
+ spinlock_t lock;
+
+ void __iomem *base;
+ struct gpio_chip gc;
+ bool uses_pinctrl;
+
+#ifdef CONFIG_PM
+ struct pl061_context_save_regs csave_regs;
+#endif
+};
+
+static int pl061_gpio_request(struct gpio_chip *gc, unsigned offset)
+{
+ /*
+ * Map back to global GPIO space and request muxing, the direction
+ * parameter does not matter for this controller.
+ */
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+ int gpio = gc->base + offset;
+
+ if (chip->uses_pinctrl)
+ return pinctrl_request_gpio(gpio);
+ return 0;
+}
+
+static void pl061_gpio_free(struct gpio_chip *gc, unsigned offset)
+{
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+ int gpio = gc->base + offset;
+
+ if (chip->uses_pinctrl)
+ pinctrl_free_gpio(gpio);
+}
+
+static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+ unsigned long flags;
+ unsigned char gpiodir;
+
+ if (offset >= gc->ngpio)
+ return -EINVAL;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ gpiodir = readb(chip->base + GPIODIR);
+ gpiodir &= ~(BIT(offset));
+ writeb(gpiodir, chip->base + GPIODIR);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int pl061_direction_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+ unsigned long flags;
+ unsigned char gpiodir;
+
+ if (offset >= gc->ngpio)
+ return -EINVAL;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ writeb(!!value << offset, chip->base + (BIT(offset + 2)));
+ gpiodir = readb(chip->base + GPIODIR);
+ gpiodir |= BIT(offset);
+ writeb(gpiodir, chip->base + GPIODIR);
+
+ /*
+ * gpio value is set again, because pl061 doesn't allow to set value of
+ * a gpio pin before configuring it in OUT mode.
+ */
+ writeb(!!value << offset, chip->base + (BIT(offset + 2)));
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int pl061_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+ return !!readb(chip->base + (BIT(offset + 2)));
+}
+
+static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+ writeb(!!value << offset, chip->base + (BIT(offset + 2)));
+}
+
+static int pl061_irq_type(struct irq_data *d, unsigned trigger)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+ int offset = irqd_to_hwirq(d);
+ unsigned long flags;
+ u8 gpiois, gpioibe, gpioiev;
+ u8 bit = BIT(offset);
+
+ if (offset < 0 || offset >= PL061_GPIO_NR)
+ return -EINVAL;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ gpioiev = readb(chip->base + GPIOIEV);
+ gpiois = readb(chip->base + GPIOIS);
+ gpioibe = readb(chip->base + GPIOIBE);
+
+ 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)
+ /* Setting this makes GPIOEV be ignored */
+ gpioibe |= bit;
+ else {
+ gpioibe &= ~bit;
+ if (trigger & IRQ_TYPE_EDGE_RISING)
+ gpioiev |= bit;
+ else if (trigger & IRQ_TYPE_EDGE_FALLING)
+ gpioiev &= ~bit;
+ }
+
+ writeb(gpiois, chip->base + GPIOIS);
+ writeb(gpioibe, chip->base + GPIOIBE);
+ writeb(gpioiev, chip->base + GPIOIEV);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ unsigned long pending;
+ int offset;
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(irqchip, desc);
+
+ pending = readb(chip->base + GPIOMIS);
+ writeb(pending, chip->base + GPIOIC);
+ if (pending) {
+ for_each_set_bit(offset, &pending, PL061_GPIO_NR)
+ generic_handle_irq(irq_find_mapping(gc->irqdomain,
+ offset));
+ }
+
+ chained_irq_exit(irqchip, desc);
+}
+
+static void pl061_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+ u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
+ u8 gpioie;
+
+ spin_lock(&chip->lock);
+ gpioie = readb(chip->base + GPIOIE) & ~mask;
+ writeb(gpioie, chip->base + GPIOIE);
+ spin_unlock(&chip->lock);
+}
+
+static void pl061_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+ u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
+ u8 gpioie;
+
+ spin_lock(&chip->lock);
+ gpioie = readb(chip->base + GPIOIE) | mask;
+ writeb(gpioie, chip->base + GPIOIE);
+ spin_unlock(&chip->lock);
+}
+
+static struct irq_chip pl061_irqchip = {
+ .name = "pl061",
+ .irq_mask = pl061_irq_mask,
+ .irq_unmask = pl061_irq_unmask,
+ .irq_set_type = pl061_irq_type,
+};
+
+static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct pl061_platform_data *pdata = dev_get_platdata(dev);
+ struct pl061_gpio *chip;
+ int ret, irq, i, irq_base;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ if (pdata) {
+ chip->gc.base = pdata->gpio_base;
+ irq_base = pdata->irq_base;
+ if (irq_base <= 0) {
+ dev_err(&adev->dev, "invalid IRQ base in pdata\n");
+ return -ENODEV;
+ }
+ } else {
+ chip->gc.base = -1;
+ irq_base = 0;
+ }
+
+ chip->base = devm_ioremap_resource(dev, &adev->res);
+ if (IS_ERR(chip->base))
+ return PTR_ERR(chip->base);
+
+ spin_lock_init(&chip->lock);
+ if (of_property_read_bool(dev->of_node, "gpio-ranges"))
+ chip->uses_pinctrl = true;
+
+ chip->gc.request = pl061_gpio_request;
+ chip->gc.free = pl061_gpio_free;
+ chip->gc.direction_input = pl061_direction_input;
+ chip->gc.direction_output = pl061_direction_output;
+ chip->gc.get = pl061_get_value;
+ chip->gc.set = pl061_set_value;
+ chip->gc.ngpio = PL061_GPIO_NR;
+ chip->gc.label = dev_name(dev);
+ chip->gc.dev = dev;
+ chip->gc.owner = THIS_MODULE;
+
+ ret = gpiochip_add(&chip->gc);
+ if (ret)
+ return ret;
+
+ /*
+ * irq_chip support
+ */
+ writeb(0, chip->base + GPIOIE); /* disable irqs */
+ irq = adev->irq[0];
+ if (irq < 0) {
+ dev_err(&adev->dev, "invalid IRQ\n");
+ return -ENODEV;
+ }
+
+ ret = gpiochip_irqchip_add(&chip->gc, &pl061_irqchip,
+ irq_base, handle_simple_irq,
+ IRQ_TYPE_NONE);
+ if (ret) {
+ dev_info(&adev->dev, "could not add irqchip\n");
+ return ret;
+ }
+ gpiochip_set_chained_irqchip(&chip->gc, &pl061_irqchip,
+ irq, pl061_irq_handler);
+
+ for (i = 0; i < PL061_GPIO_NR; i++) {
+ if (pdata) {
+ if (pdata->directions & (BIT(i)))
+ pl061_direction_output(&chip->gc, i,
+ pdata->values & (BIT(i)));
+ else
+ pl061_direction_input(&chip->gc, i);
+ }
+ }
+
+ amba_set_drvdata(adev, chip);
+ dev_info(&adev->dev, "PL061 GPIO chip @%pa registered\n",
+ &adev->res.start);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pl061_suspend(struct device *dev)
+{
+ struct pl061_gpio *chip = dev_get_drvdata(dev);
+ int offset;
+
+ chip->csave_regs.gpio_data = 0;
+ chip->csave_regs.gpio_dir = readb(chip->base + GPIODIR);
+ chip->csave_regs.gpio_is = readb(chip->base + GPIOIS);
+ chip->csave_regs.gpio_ibe = readb(chip->base + GPIOIBE);
+ chip->csave_regs.gpio_iev = readb(chip->base + GPIOIEV);
+ chip->csave_regs.gpio_ie = readb(chip->base + GPIOIE);
+
+ for (offset = 0; offset < PL061_GPIO_NR; offset++) {
+ if (chip->csave_regs.gpio_dir & (BIT(offset)))
+ chip->csave_regs.gpio_data |=
+ pl061_get_value(&chip->gc, offset) << offset;
+ }
+
+ return 0;
+}
+
+static int pl061_resume(struct device *dev)
+{
+ struct pl061_gpio *chip = dev_get_drvdata(dev);
+ int offset;
+
+ for (offset = 0; offset < PL061_GPIO_NR; offset++) {
+ if (chip->csave_regs.gpio_dir & (BIT(offset)))
+ pl061_direction_output(&chip->gc, offset,
+ chip->csave_regs.gpio_data &
+ (BIT(offset)));
+ else
+ pl061_direction_input(&chip->gc, offset);
+ }
+
+ writeb(chip->csave_regs.gpio_is, chip->base + GPIOIS);
+ writeb(chip->csave_regs.gpio_ibe, chip->base + GPIOIBE);
+ writeb(chip->csave_regs.gpio_iev, chip->base + GPIOIEV);
+ writeb(chip->csave_regs.gpio_ie, chip->base + GPIOIE);
+
+ return 0;
+}
+
+static const struct dev_pm_ops pl061_dev_pm_ops = {
+ .suspend = pl061_suspend,
+ .resume = pl061_resume,
+ .freeze = pl061_suspend,
+ .restore = pl061_resume,
+};
+#endif
+
+static struct amba_id pl061_ids[] = {
+ {
+ .id = 0x00041061,
+ .mask = 0x000fffff,
+ },
+ { 0, 0 },
+};
+
+MODULE_DEVICE_TABLE(amba, pl061_ids);
+
+static struct amba_driver pl061_gpio_driver = {
+ .drv = {
+ .name = "pl061_gpio",
+#ifdef CONFIG_PM
+ .pm = &pl061_dev_pm_ops,
+#endif
+ },
+ .id_table = pl061_ids,
+ .probe = pl061_probe,
+};
+
+static int __init pl061_gpio_init(void)
+{
+ return amba_driver_register(&pl061_gpio_driver);
+}
+module_init(pl061_gpio_init);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("PL061 GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
new file mode 100644
index 000000000..cdbbcf0fa
--- /dev/null
+++ b/drivers/gpio/gpio-pxa.c
@@ -0,0 +1,747 @@
+/*
+ * linux/arch/arm/plat-pxa/gpio.c
+ *
+ * Generic PXA GPIO handling
+ *
+ * Author: Nicolas Pitre
+ * Created: Jun 15, 2001
+ * Copyright: MontaVista Software Inc.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio-pxa.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/syscore_ops.h>
+#include <linux/slab.h>
+
+/*
+ * We handle the GPIOs by banks, each bank covers up to 32 GPIOs with
+ * one set of registers. The register offsets are organized below:
+ *
+ * GPLR GPDR GPSR GPCR GRER GFER GEDR
+ * BANK 0 - 0x0000 0x000C 0x0018 0x0024 0x0030 0x003C 0x0048
+ * BANK 1 - 0x0004 0x0010 0x001C 0x0028 0x0034 0x0040 0x004C
+ * BANK 2 - 0x0008 0x0014 0x0020 0x002C 0x0038 0x0044 0x0050
+ *
+ * BANK 3 - 0x0100 0x010C 0x0118 0x0124 0x0130 0x013C 0x0148
+ * BANK 4 - 0x0104 0x0110 0x011C 0x0128 0x0134 0x0140 0x014C
+ * BANK 5 - 0x0108 0x0114 0x0120 0x012C 0x0138 0x0144 0x0150
+ *
+ * BANK 6 - 0x0200 0x020C 0x0218 0x0224 0x0230 0x023C 0x0248
+ *
+ * NOTE:
+ * BANK 3 is only available on PXA27x and later processors.
+ * BANK 4 and 5 are only available on PXA935, PXA1928
+ * BANK 6 is only available on PXA1928
+ */
+
+#define GPLR_OFFSET 0x00
+#define GPDR_OFFSET 0x0C
+#define GPSR_OFFSET 0x18
+#define GPCR_OFFSET 0x24
+#define GRER_OFFSET 0x30
+#define GFER_OFFSET 0x3C
+#define GEDR_OFFSET 0x48
+#define GAFR_OFFSET 0x54
+#define ED_MASK_OFFSET 0x9C /* GPIO edge detection for AP side */
+
+#define BANK_OFF(n) (((n) / 3) << 8) + (((n) % 3) << 2)
+
+int pxa_last_gpio;
+static int irq_base;
+
+#ifdef CONFIG_OF
+static struct irq_domain *domain;
+static struct device_node *pxa_gpio_of_node;
+#endif
+
+struct pxa_gpio_chip {
+ struct gpio_chip chip;
+ void __iomem *regbase;
+ char label[10];
+
+ unsigned long irq_mask;
+ unsigned long irq_edge_rise;
+ unsigned long irq_edge_fall;
+ int (*set_wake)(unsigned int gpio, unsigned int on);
+
+#ifdef CONFIG_PM
+ unsigned long saved_gplr;
+ unsigned long saved_gpdr;
+ unsigned long saved_grer;
+ unsigned long saved_gfer;
+#endif
+};
+
+enum pxa_gpio_type {
+ PXA25X_GPIO = 0,
+ PXA26X_GPIO,
+ PXA27X_GPIO,
+ PXA3XX_GPIO,
+ PXA93X_GPIO,
+ MMP_GPIO = 0x10,
+ MMP2_GPIO,
+ PXA1928_GPIO,
+};
+
+struct pxa_gpio_id {
+ enum pxa_gpio_type type;
+ int gpio_nums;
+};
+
+static DEFINE_SPINLOCK(gpio_lock);
+static struct pxa_gpio_chip *pxa_gpio_chips;
+static enum pxa_gpio_type gpio_type;
+static void __iomem *gpio_reg_base;
+
+static struct pxa_gpio_id pxa25x_id = {
+ .type = PXA25X_GPIO,
+ .gpio_nums = 85,
+};
+
+static struct pxa_gpio_id pxa26x_id = {
+ .type = PXA26X_GPIO,
+ .gpio_nums = 90,
+};
+
+static struct pxa_gpio_id pxa27x_id = {
+ .type = PXA27X_GPIO,
+ .gpio_nums = 121,
+};
+
+static struct pxa_gpio_id pxa3xx_id = {
+ .type = PXA3XX_GPIO,
+ .gpio_nums = 128,
+};
+
+static struct pxa_gpio_id pxa93x_id = {
+ .type = PXA93X_GPIO,
+ .gpio_nums = 192,
+};
+
+static struct pxa_gpio_id mmp_id = {
+ .type = MMP_GPIO,
+ .gpio_nums = 128,
+};
+
+static struct pxa_gpio_id mmp2_id = {
+ .type = MMP2_GPIO,
+ .gpio_nums = 192,
+};
+
+static struct pxa_gpio_id pxa1928_id = {
+ .type = PXA1928_GPIO,
+ .gpio_nums = 224,
+};
+
+#define for_each_gpio_chip(i, c) \
+ for (i = 0, c = &pxa_gpio_chips[0]; i <= pxa_last_gpio; i += 32, c++)
+
+static inline void __iomem *gpio_chip_base(struct gpio_chip *c)
+{
+ return container_of(c, struct pxa_gpio_chip, chip)->regbase;
+}
+
+static inline struct pxa_gpio_chip *gpio_to_pxachip(unsigned gpio)
+{
+ return &pxa_gpio_chips[gpio_to_bank(gpio)];
+}
+
+static inline int gpio_is_pxa_type(int type)
+{
+ return (type & MMP_GPIO) == 0;
+}
+
+static inline int gpio_is_mmp_type(int type)
+{
+ return (type & MMP_GPIO) != 0;
+}
+
+/* GPIO86/87/88/89 on PXA26x have their direction bits in PXA_GPDR(2 inverted,
+ * as well as their Alternate Function value being '1' for GPIO in GAFRx.
+ */
+static inline int __gpio_is_inverted(int gpio)
+{
+ if ((gpio_type == PXA26X_GPIO) && (gpio > 85))
+ return 1;
+ return 0;
+}
+
+/*
+ * On PXA25x and PXA27x, GAFRx and GPDRx together decide the alternate
+ * function of a GPIO, and GPDRx cannot be altered once configured. It
+ * is attributed as "occupied" here (I know this terminology isn't
+ * accurate, you are welcome to propose a better one :-)
+ */
+static inline int __gpio_is_occupied(unsigned gpio)
+{
+ struct pxa_gpio_chip *pxachip;
+ void __iomem *base;
+ unsigned long gafr = 0, gpdr = 0;
+ int ret, af = 0, dir = 0;
+
+ pxachip = gpio_to_pxachip(gpio);
+ base = gpio_chip_base(&pxachip->chip);
+ gpdr = readl_relaxed(base + GPDR_OFFSET);
+
+ switch (gpio_type) {
+ case PXA25X_GPIO:
+ case PXA26X_GPIO:
+ case PXA27X_GPIO:
+ gafr = readl_relaxed(base + GAFR_OFFSET);
+ af = (gafr >> ((gpio & 0xf) * 2)) & 0x3;
+ dir = gpdr & GPIO_bit(gpio);
+
+ if (__gpio_is_inverted(gpio))
+ ret = (af != 1) || (dir == 0);
+ else
+ ret = (af != 0) || (dir != 0);
+ break;
+ default:
+ ret = gpdr & GPIO_bit(gpio);
+ break;
+ }
+ return ret;
+}
+
+static int pxa_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return chip->base + offset + irq_base;
+}
+
+int pxa_irq_to_gpio(int irq)
+{
+ return irq - irq_base;
+}
+
+static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ void __iomem *base = gpio_chip_base(chip);
+ uint32_t value, mask = 1 << offset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ value = readl_relaxed(base + GPDR_OFFSET);
+ if (__gpio_is_inverted(chip->base + offset))
+ value |= mask;
+ else
+ value &= ~mask;
+ writel_relaxed(value, base + GPDR_OFFSET);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return 0;
+}
+
+static int pxa_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ void __iomem *base = gpio_chip_base(chip);
+ uint32_t tmp, mask = 1 << offset;
+ unsigned long flags;
+
+ writel_relaxed(mask, base + (value ? GPSR_OFFSET : GPCR_OFFSET));
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ tmp = readl_relaxed(base + GPDR_OFFSET);
+ if (__gpio_is_inverted(chip->base + offset))
+ tmp &= ~mask;
+ else
+ tmp |= mask;
+ writel_relaxed(tmp, base + GPDR_OFFSET);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return 0;
+}
+
+static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ u32 gplr = readl_relaxed(gpio_chip_base(chip) + GPLR_OFFSET);
+ return !!(gplr & (1 << offset));
+}
+
+static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ writel_relaxed(1 << offset, gpio_chip_base(chip) +
+ (value ? GPSR_OFFSET : GPCR_OFFSET));
+}
+
+#ifdef CONFIG_OF_GPIO
+static int pxa_gpio_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ if (gpiospec->args[0] > pxa_last_gpio)
+ return -EINVAL;
+
+ if (gc != &pxa_gpio_chips[gpiospec->args[0] / 32].chip)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return gpiospec->args[0] % 32;
+}
+#endif
+
+static int pxa_init_gpio_chip(int gpio_end,
+ int (*set_wake)(unsigned int, unsigned int))
+{
+ int i, gpio, nbanks = gpio_to_bank(gpio_end) + 1;
+ struct pxa_gpio_chip *chips;
+
+ chips = kzalloc(nbanks * sizeof(struct pxa_gpio_chip), GFP_KERNEL);
+ if (chips == NULL) {
+ pr_err("%s: failed to allocate GPIO chips\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0, gpio = 0; i < nbanks; i++, gpio += 32) {
+ struct gpio_chip *c = &chips[i].chip;
+
+ sprintf(chips[i].label, "gpio-%d", i);
+ chips[i].regbase = gpio_reg_base + BANK_OFF(i);
+ chips[i].set_wake = set_wake;
+
+ c->base = gpio;
+ c->label = chips[i].label;
+
+ c->direction_input = pxa_gpio_direction_input;
+ c->direction_output = pxa_gpio_direction_output;
+ c->get = pxa_gpio_get;
+ c->set = pxa_gpio_set;
+ c->to_irq = pxa_gpio_to_irq;
+#ifdef CONFIG_OF_GPIO
+ c->of_node = pxa_gpio_of_node;
+ c->of_xlate = pxa_gpio_of_xlate;
+ c->of_gpio_n_cells = 2;
+#endif
+
+ /* number of GPIOs on last bank may be less than 32 */
+ c->ngpio = (gpio + 31 > gpio_end) ? (gpio_end - gpio + 1) : 32;
+ gpiochip_add(c);
+ }
+ pxa_gpio_chips = chips;
+ return 0;
+}
+
+/* Update only those GRERx and GFERx edge detection register bits if those
+ * bits are set in c->irq_mask
+ */
+static inline void update_edge_detect(struct pxa_gpio_chip *c)
+{
+ uint32_t grer, gfer;
+
+ grer = readl_relaxed(c->regbase + GRER_OFFSET) & ~c->irq_mask;
+ gfer = readl_relaxed(c->regbase + GFER_OFFSET) & ~c->irq_mask;
+ grer |= c->irq_edge_rise & c->irq_mask;
+ gfer |= c->irq_edge_fall & c->irq_mask;
+ writel_relaxed(grer, c->regbase + GRER_OFFSET);
+ writel_relaxed(gfer, c->regbase + GFER_OFFSET);
+}
+
+static int pxa_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+ struct pxa_gpio_chip *c;
+ int gpio = pxa_irq_to_gpio(d->irq);
+ unsigned long gpdr, mask = GPIO_bit(gpio);
+
+ c = gpio_to_pxachip(gpio);
+
+ if (type == IRQ_TYPE_PROBE) {
+ /* Don't mess with enabled GPIOs using preconfigured edges or
+ * GPIOs set to alternate function or to output during probe
+ */
+ if ((c->irq_edge_rise | c->irq_edge_fall) & GPIO_bit(gpio))
+ return 0;
+
+ if (__gpio_is_occupied(gpio))
+ return 0;
+
+ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ }
+
+ gpdr = readl_relaxed(c->regbase + GPDR_OFFSET);
+
+ if (__gpio_is_inverted(gpio))
+ writel_relaxed(gpdr | mask, c->regbase + GPDR_OFFSET);
+ else
+ writel_relaxed(gpdr & ~mask, c->regbase + GPDR_OFFSET);
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ c->irq_edge_rise |= mask;
+ else
+ c->irq_edge_rise &= ~mask;
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ c->irq_edge_fall |= mask;
+ else
+ c->irq_edge_fall &= ~mask;
+
+ update_edge_detect(c);
+
+ pr_debug("%s: IRQ%d (GPIO%d) - edge%s%s\n", __func__, d->irq, gpio,
+ ((type & IRQ_TYPE_EDGE_RISING) ? " rising" : ""),
+ ((type & IRQ_TYPE_EDGE_FALLING) ? " falling" : ""));
+ return 0;
+}
+
+static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct pxa_gpio_chip *c;
+ int loop, gpio, gpio_base, n;
+ unsigned long gedr;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+
+ do {
+ loop = 0;
+ for_each_gpio_chip(gpio, c) {
+ gpio_base = c->chip.base;
+
+ gedr = readl_relaxed(c->regbase + GEDR_OFFSET);
+ gedr = gedr & c->irq_mask;
+ writel_relaxed(gedr, c->regbase + GEDR_OFFSET);
+
+ for_each_set_bit(n, &gedr, BITS_PER_LONG) {
+ loop = 1;
+
+ generic_handle_irq(gpio_to_irq(gpio_base + n));
+ }
+ }
+ } while (loop);
+
+ chained_irq_exit(chip, desc);
+}
+
+static void pxa_ack_muxed_gpio(struct irq_data *d)
+{
+ int gpio = pxa_irq_to_gpio(d->irq);
+ struct pxa_gpio_chip *c = gpio_to_pxachip(gpio);
+
+ writel_relaxed(GPIO_bit(gpio), c->regbase + GEDR_OFFSET);
+}
+
+static void pxa_mask_muxed_gpio(struct irq_data *d)
+{
+ int gpio = pxa_irq_to_gpio(d->irq);
+ struct pxa_gpio_chip *c = gpio_to_pxachip(gpio);
+ uint32_t grer, gfer;
+
+ c->irq_mask &= ~GPIO_bit(gpio);
+
+ grer = readl_relaxed(c->regbase + GRER_OFFSET) & ~GPIO_bit(gpio);
+ gfer = readl_relaxed(c->regbase + GFER_OFFSET) & ~GPIO_bit(gpio);
+ writel_relaxed(grer, c->regbase + GRER_OFFSET);
+ writel_relaxed(gfer, c->regbase + GFER_OFFSET);
+}
+
+static int pxa_gpio_set_wake(struct irq_data *d, unsigned int on)
+{
+ int gpio = pxa_irq_to_gpio(d->irq);
+ struct pxa_gpio_chip *c = gpio_to_pxachip(gpio);
+
+ if (c->set_wake)
+ return c->set_wake(gpio, on);
+ else
+ return 0;
+}
+
+static void pxa_unmask_muxed_gpio(struct irq_data *d)
+{
+ int gpio = pxa_irq_to_gpio(d->irq);
+ struct pxa_gpio_chip *c = gpio_to_pxachip(gpio);
+
+ c->irq_mask |= GPIO_bit(gpio);
+ update_edge_detect(c);
+}
+
+static struct irq_chip pxa_muxed_gpio_chip = {
+ .name = "GPIO",
+ .irq_ack = pxa_ack_muxed_gpio,
+ .irq_mask = pxa_mask_muxed_gpio,
+ .irq_unmask = pxa_unmask_muxed_gpio,
+ .irq_set_type = pxa_gpio_irq_type,
+ .irq_set_wake = pxa_gpio_set_wake,
+};
+
+static int pxa_gpio_nums(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct pxa_gpio_id *pxa_id = (struct pxa_gpio_id *)id->driver_data;
+ int count = 0;
+
+ switch (pxa_id->type) {
+ case PXA25X_GPIO:
+ case PXA26X_GPIO:
+ case PXA27X_GPIO:
+ case PXA3XX_GPIO:
+ case PXA93X_GPIO:
+ case MMP_GPIO:
+ case MMP2_GPIO:
+ case PXA1928_GPIO:
+ gpio_type = pxa_id->type;
+ count = pxa_id->gpio_nums - 1;
+ break;
+ default:
+ count = -EINVAL;
+ break;
+ }
+ return count;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_gpio_dt_ids[] = {
+ { .compatible = "intel,pxa25x-gpio", .data = &pxa25x_id, },
+ { .compatible = "intel,pxa26x-gpio", .data = &pxa26x_id, },
+ { .compatible = "intel,pxa27x-gpio", .data = &pxa27x_id, },
+ { .compatible = "intel,pxa3xx-gpio", .data = &pxa3xx_id, },
+ { .compatible = "marvell,pxa93x-gpio", .data = &pxa93x_id, },
+ { .compatible = "marvell,mmp-gpio", .data = &mmp_id, },
+ { .compatible = "marvell,mmp2-gpio", .data = &mmp2_id, },
+ { .compatible = "marvell,pxa1928-gpio", .data = &pxa1928_id, },
+ {}
+};
+
+static int pxa_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip,
+ handle_edge_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ return 0;
+}
+
+const struct irq_domain_ops pxa_irq_domain_ops = {
+ .map = pxa_irq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int pxa_gpio_probe_dt(struct platform_device *pdev)
+{
+ int ret = 0, nr_gpios;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(pxa_gpio_dt_ids, &pdev->dev);
+ const struct pxa_gpio_id *gpio_id;
+
+ if (!of_id || !of_id->data) {
+ dev_err(&pdev->dev, "Failed to find gpio controller\n");
+ return -EFAULT;
+ }
+ gpio_id = of_id->data;
+ gpio_type = gpio_id->type;
+
+ nr_gpios = gpio_id->gpio_nums;
+ pxa_last_gpio = nr_gpios - 1;
+
+ irq_base = irq_alloc_descs(-1, 0, nr_gpios, 0);
+ if (irq_base < 0) {
+ dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
+ ret = irq_base;
+ goto err;
+ }
+ domain = irq_domain_add_legacy(np, nr_gpios, irq_base, 0,
+ &pxa_irq_domain_ops, NULL);
+ pxa_gpio_of_node = np;
+ return 0;
+err:
+ iounmap(gpio_reg_base);
+ return ret;
+}
+#else
+#define pxa_gpio_probe_dt(pdev) (-1)
+#endif
+
+static int pxa_gpio_probe(struct platform_device *pdev)
+{
+ struct pxa_gpio_chip *c;
+ struct resource *res;
+ struct clk *clk;
+ struct pxa_gpio_platform_data *info;
+ int gpio, irq, ret, use_of = 0;
+ int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0;
+
+ info = dev_get_platdata(&pdev->dev);
+ if (info) {
+ irq_base = info->irq_base;
+ if (irq_base <= 0)
+ return -EINVAL;
+ pxa_last_gpio = pxa_gpio_nums(pdev);
+ } else {
+ irq_base = 0;
+ use_of = 1;
+ ret = pxa_gpio_probe_dt(pdev);
+ if (ret < 0)
+ return -EINVAL;
+ }
+
+ if (!pxa_last_gpio)
+ return -EINVAL;
+
+ irq0 = platform_get_irq_byname(pdev, "gpio0");
+ irq1 = platform_get_irq_byname(pdev, "gpio1");
+ irq_mux = platform_get_irq_byname(pdev, "gpio_mux");
+ if ((irq0 > 0 && irq1 <= 0) || (irq0 <= 0 && irq1 > 0)
+ || (irq_mux <= 0))
+ return -EINVAL;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+ gpio_reg_base = ioremap(res->start, resource_size(res));
+ if (!gpio_reg_base)
+ return -EINVAL;
+
+ if (irq0 > 0)
+ gpio_offset = 2;
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Error %ld to get gpio clock\n",
+ PTR_ERR(clk));
+ iounmap(gpio_reg_base);
+ return PTR_ERR(clk);
+ }
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ clk_put(clk);
+ iounmap(gpio_reg_base);
+ return ret;
+ }
+
+ /* Initialize GPIO chips */
+ pxa_init_gpio_chip(pxa_last_gpio, info ? info->gpio_set_wake : NULL);
+
+ /* clear all GPIO edge detects */
+ for_each_gpio_chip(gpio, c) {
+ writel_relaxed(0, c->regbase + GFER_OFFSET);
+ writel_relaxed(0, c->regbase + GRER_OFFSET);
+ writel_relaxed(~0, c->regbase + GEDR_OFFSET);
+ /* unmask GPIO edge detect for AP side */
+ if (gpio_is_mmp_type(gpio_type))
+ writel_relaxed(~0, c->regbase + ED_MASK_OFFSET);
+ }
+
+ if (!use_of) {
+ if (irq0 > 0) {
+ irq = gpio_to_irq(0);
+ irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip,
+ handle_edge_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+ if (irq1 > 0) {
+ irq = gpio_to_irq(1);
+ irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip,
+ handle_edge_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+
+ for (irq = gpio_to_irq(gpio_offset);
+ irq <= gpio_to_irq(pxa_last_gpio); irq++) {
+ irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip,
+ handle_edge_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+ }
+
+ if (irq0 > 0)
+ irq_set_chained_handler(irq0, pxa_gpio_demux_handler);
+ if (irq1 > 0)
+ irq_set_chained_handler(irq1, pxa_gpio_demux_handler);
+
+ irq_set_chained_handler(irq_mux, pxa_gpio_demux_handler);
+ return 0;
+}
+
+static const struct platform_device_id gpio_id_table[] = {
+ { "pxa25x-gpio", (unsigned long)&pxa25x_id },
+ { "pxa26x-gpio", (unsigned long)&pxa26x_id },
+ { "pxa27x-gpio", (unsigned long)&pxa27x_id },
+ { "pxa3xx-gpio", (unsigned long)&pxa3xx_id },
+ { "pxa93x-gpio", (unsigned long)&pxa93x_id },
+ { "mmp-gpio", (unsigned long)&mmp_id },
+ { "mmp2-gpio", (unsigned long)&mmp2_id },
+ { "pxa1928-gpio", (unsigned long)&pxa1928_id },
+ { },
+};
+
+static struct platform_driver pxa_gpio_driver = {
+ .probe = pxa_gpio_probe,
+ .driver = {
+ .name = "pxa-gpio",
+ .of_match_table = of_match_ptr(pxa_gpio_dt_ids),
+ },
+ .id_table = gpio_id_table,
+};
+
+static int __init pxa_gpio_init(void)
+{
+ return platform_driver_register(&pxa_gpio_driver);
+}
+postcore_initcall(pxa_gpio_init);
+
+#ifdef CONFIG_PM
+static int pxa_gpio_suspend(void)
+{
+ struct pxa_gpio_chip *c;
+ int gpio;
+
+ for_each_gpio_chip(gpio, c) {
+ c->saved_gplr = readl_relaxed(c->regbase + GPLR_OFFSET);
+ c->saved_gpdr = readl_relaxed(c->regbase + GPDR_OFFSET);
+ c->saved_grer = readl_relaxed(c->regbase + GRER_OFFSET);
+ c->saved_gfer = readl_relaxed(c->regbase + GFER_OFFSET);
+
+ /* Clear GPIO transition detect bits */
+ writel_relaxed(0xffffffff, c->regbase + GEDR_OFFSET);
+ }
+ return 0;
+}
+
+static void pxa_gpio_resume(void)
+{
+ struct pxa_gpio_chip *c;
+ int gpio;
+
+ for_each_gpio_chip(gpio, c) {
+ /* restore level with set/clear */
+ writel_relaxed(c->saved_gplr, c->regbase + GPSR_OFFSET);
+ writel_relaxed(~c->saved_gplr, c->regbase + GPCR_OFFSET);
+
+ writel_relaxed(c->saved_grer, c->regbase + GRER_OFFSET);
+ writel_relaxed(c->saved_gfer, c->regbase + GFER_OFFSET);
+ writel_relaxed(c->saved_gpdr, c->regbase + GPDR_OFFSET);
+ }
+}
+#else
+#define pxa_gpio_suspend NULL
+#define pxa_gpio_resume NULL
+#endif
+
+struct syscore_ops pxa_gpio_syscore_ops = {
+ .suspend = pxa_gpio_suspend,
+ .resume = pxa_gpio_resume,
+};
+
+static int __init pxa_gpio_sysinit(void)
+{
+ register_syscore_ops(&pxa_gpio_syscore_ops);
+ return 0;
+}
+postcore_initcall(pxa_gpio_sysinit);
diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c
new file mode 100644
index 000000000..6eabf2396
--- /dev/null
+++ b/drivers/gpio/gpio-rc5t583.c
@@ -0,0 +1,178 @@
+/*
+ * GPIO driver for RICOH583 power management chip.
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * Based on code
+ * Copyright (C) 2011 RICOH COMPANY,LTD
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/rc5t583.h>
+
+struct rc5t583_gpio {
+ struct gpio_chip gpio_chip;
+ struct rc5t583 *rc5t583;
+};
+
+static inline struct rc5t583_gpio *to_rc5t583_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct rc5t583_gpio, gpio_chip);
+}
+
+static int rc5t583_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc);
+ struct device *parent = rc5t583_gpio->rc5t583->dev;
+ uint8_t val = 0;
+ int ret;
+
+ ret = rc5t583_read(parent, RC5T583_GPIO_MON_IOIN, &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & BIT(offset));
+}
+
+static void rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc);
+ struct device *parent = rc5t583_gpio->rc5t583->dev;
+ if (val)
+ rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset));
+ else
+ rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset));
+}
+
+static int rc5t583_gpio_dir_input(struct gpio_chip *gc, unsigned int offset)
+{
+ struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc);
+ struct device *parent = rc5t583_gpio->rc5t583->dev;
+ int ret;
+
+ ret = rc5t583_clear_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset));
+ if (ret < 0)
+ return ret;
+
+ /* Set pin to gpio mode */
+ return rc5t583_clear_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset));
+}
+
+static int rc5t583_gpio_dir_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc);
+ struct device *parent = rc5t583_gpio->rc5t583->dev;
+ int ret;
+
+ rc5t583_gpio_set(gc, offset, value);
+ ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset));
+ if (ret < 0)
+ return ret;
+
+ /* Set pin to gpio mode */
+ return rc5t583_clear_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset));
+}
+
+static int rc5t583_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc);
+
+ if (offset < RC5T583_MAX_GPIO)
+ return rc5t583_gpio->rc5t583->irq_base +
+ RC5T583_IRQ_GPIO0 + offset;
+ return -EINVAL;
+}
+
+static void rc5t583_gpio_free(struct gpio_chip *gc, unsigned offset)
+{
+ struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc);
+ struct device *parent = rc5t583_gpio->rc5t583->dev;
+
+ rc5t583_set_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset));
+}
+
+static int rc5t583_gpio_probe(struct platform_device *pdev)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent);
+ struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev);
+ struct rc5t583_gpio *rc5t583_gpio;
+
+ rc5t583_gpio = devm_kzalloc(&pdev->dev, sizeof(*rc5t583_gpio),
+ GFP_KERNEL);
+ if (!rc5t583_gpio)
+ return -ENOMEM;
+
+ rc5t583_gpio->gpio_chip.label = "gpio-rc5t583",
+ rc5t583_gpio->gpio_chip.owner = THIS_MODULE,
+ rc5t583_gpio->gpio_chip.free = rc5t583_gpio_free,
+ rc5t583_gpio->gpio_chip.direction_input = rc5t583_gpio_dir_input,
+ rc5t583_gpio->gpio_chip.direction_output = rc5t583_gpio_dir_output,
+ rc5t583_gpio->gpio_chip.set = rc5t583_gpio_set,
+ rc5t583_gpio->gpio_chip.get = rc5t583_gpio_get,
+ rc5t583_gpio->gpio_chip.to_irq = rc5t583_gpio_to_irq,
+ rc5t583_gpio->gpio_chip.ngpio = RC5T583_MAX_GPIO,
+ rc5t583_gpio->gpio_chip.can_sleep = true,
+ rc5t583_gpio->gpio_chip.dev = &pdev->dev;
+ rc5t583_gpio->gpio_chip.base = -1;
+ rc5t583_gpio->rc5t583 = rc5t583;
+
+ if (pdata && pdata->gpio_base)
+ rc5t583_gpio->gpio_chip.base = pdata->gpio_base;
+
+ platform_set_drvdata(pdev, rc5t583_gpio);
+
+ return gpiochip_add(&rc5t583_gpio->gpio_chip);
+}
+
+static int rc5t583_gpio_remove(struct platform_device *pdev)
+{
+ struct rc5t583_gpio *rc5t583_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&rc5t583_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver rc5t583_gpio_driver = {
+ .driver = {
+ .name = "rc5t583-gpio",
+ },
+ .probe = rc5t583_gpio_probe,
+ .remove = rc5t583_gpio_remove,
+};
+
+static int __init rc5t583_gpio_init(void)
+{
+ return platform_driver_register(&rc5t583_gpio_driver);
+}
+subsys_initcall(rc5t583_gpio_init);
+
+static void __exit rc5t583_gpio_exit(void)
+{
+ platform_driver_unregister(&rc5t583_gpio_driver);
+}
+module_exit(rc5t583_gpio_exit);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("GPIO interface for RC5T583");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:rc5t583-gpio");
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
new file mode 100644
index 000000000..1e14a6c74
--- /dev/null
+++ b/drivers/gpio/gpio-rcar.c
@@ -0,0 +1,519 @@
+/*
+ * Renesas R-Car GPIO Support
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_data/gpio-rcar.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+struct gpio_rcar_priv {
+ void __iomem *base;
+ spinlock_t lock;
+ struct gpio_rcar_config config;
+ struct platform_device *pdev;
+ struct gpio_chip gpio_chip;
+ struct irq_chip irq_chip;
+ unsigned int irq_parent;
+ struct clk *clk;
+};
+
+#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 BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
+
+#define RCAR_MAX_GPIO_PER_BANK 32
+
+static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs)
+{
+ return ioread32(p->base + offs);
+}
+
+static inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs,
+ u32 value)
+{
+ iowrite32(value, p->base + offs);
+}
+
+static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs,
+ int bit, bool value)
+{
+ u32 tmp = gpio_rcar_read(p, offs);
+
+ if (value)
+ tmp |= BIT(bit);
+ else
+ tmp &= ~BIT(bit);
+
+ gpio_rcar_write(p, offs, tmp);
+}
+
+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 = container_of(gc, struct gpio_rcar_priv,
+ gpio_chip);
+
+ gpio_rcar_write(p, INTMSK, ~BIT(irqd_to_hwirq(d)));
+}
+
+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 = container_of(gc, struct gpio_rcar_priv,
+ gpio_chip);
+
+ gpio_rcar_write(p, MSKCLR, BIT(irqd_to_hwirq(d)));
+}
+
+static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p,
+ unsigned int hwirq,
+ bool active_high_rising_edge,
+ bool level_trigger,
+ bool both)
+{
+ unsigned long flags;
+
+ /* follow steps in the GPIO documentation for
+ * "Setting Edge-Sensitive Interrupt Input Mode" and
+ * "Setting Level-Sensitive Interrupt Input Mode"
+ */
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ /* Configure postive 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->config.has_both_edge_trigger)
+ gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both);
+
+ /* Select "Interrupt Input Mode" in IOINTSEL */
+ gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true);
+
+ /* Write INTCLR in case of edge trigger */
+ if (!level_trigger)
+ gpio_rcar_write(p, INTCLR, BIT(hwirq));
+
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
+ gpio_chip);
+ unsigned int hwirq = irqd_to_hwirq(d);
+
+ dev_dbg(&p->pdev->dev, "sense irq = %d, type = %d\n", hwirq, type);
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true,
+ false);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true,
+ false);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false,
+ false);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false,
+ false);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ if (!p->config.has_both_edge_trigger)
+ return -EINVAL;
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false,
+ true);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
+ gpio_chip);
+ int error;
+
+ if (p->irq_parent) {
+ error = irq_set_irq_wake(p->irq_parent, on);
+ if (error) {
+ dev_dbg(&p->pdev->dev,
+ "irq %u doesn't support irq_set_wake\n",
+ p->irq_parent);
+ p->irq_parent = 0;
+ }
+ }
+
+ if (!p->clk)
+ return 0;
+
+ if (on)
+ clk_enable(p->clk);
+ else
+ clk_disable(p->clk);
+
+ return 0;
+}
+
+static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_rcar_priv *p = dev_id;
+ u32 pending;
+ unsigned int offset, irqs_handled = 0;
+
+ while ((pending = gpio_rcar_read(p, INTDT) &
+ gpio_rcar_read(p, INTMSK))) {
+ offset = __ffs(pending);
+ gpio_rcar_write(p, INTCLR, BIT(offset));
+ generic_handle_irq(irq_find_mapping(p->gpio_chip.irqdomain,
+ offset));
+ irqs_handled++;
+ }
+
+ return irqs_handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline struct gpio_rcar_priv *gpio_to_priv(struct gpio_chip *chip)
+{
+ return container_of(chip, struct gpio_rcar_priv, gpio_chip);
+}
+
+static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
+ unsigned int gpio,
+ bool output)
+{
+ struct gpio_rcar_priv *p = gpio_to_priv(chip);
+ unsigned long flags;
+
+ /* follow steps in the GPIO documentation for
+ * "Setting General Output Mode" and
+ * "Setting General Input Mode"
+ */
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ /* Configure postive logic in POSNEG */
+ gpio_rcar_modify_bit(p, POSNEG, gpio, false);
+
+ /* Select "General Input/Output Mode" in IOINTSEL */
+ gpio_rcar_modify_bit(p, IOINTSEL, gpio, false);
+
+ /* Select Input Mode or Output Mode in INOUTSEL */
+ gpio_rcar_modify_bit(p, INOUTSEL, gpio, output);
+
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+
+ /* Set the GPIO as an input to ensure that the next GPIO request won't
+ * drive the GPIO pin as an output.
+ */
+ gpio_rcar_config_general_input_output_mode(chip, offset, false);
+}
+
+static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ gpio_rcar_config_general_input_output_mode(chip, offset, false);
+ return 0;
+}
+
+static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset)
+{
+ 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(gpio_to_priv(chip), INOUTSEL) & bit)
+ return !!(gpio_rcar_read(gpio_to_priv(chip), OUTDT) & bit);
+ else
+ return !!(gpio_rcar_read(gpio_to_priv(chip), INDT) & bit);
+}
+
+static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct gpio_rcar_priv *p = gpio_to_priv(chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+ gpio_rcar_modify_bit(p, OUTDT, offset, value);
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ /* write GPIO value to output before selecting output mode of pin */
+ gpio_rcar_set(chip, offset, value);
+ gpio_rcar_config_general_input_output_mode(chip, offset, true);
+ return 0;
+}
+
+struct gpio_rcar_info {
+ bool has_both_edge_trigger;
+};
+
+static const struct gpio_rcar_info gpio_rcar_info_gen1 = {
+ .has_both_edge_trigger = false,
+};
+
+static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
+ .has_both_edge_trigger = true,
+};
+
+static const struct of_device_id gpio_rcar_of_table[] = {
+ {
+ .compatible = "renesas,gpio-r8a7790",
+ .data = &gpio_rcar_info_gen2,
+ }, {
+ .compatible = "renesas,gpio-r8a7791",
+ .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-rcar",
+ .data = &gpio_rcar_info_gen1,
+ }, {
+ /* Terminator */
+ },
+};
+
+MODULE_DEVICE_TABLE(of, gpio_rcar_of_table);
+
+static int gpio_rcar_parse_pdata(struct gpio_rcar_priv *p)
+{
+ struct gpio_rcar_config *pdata = dev_get_platdata(&p->pdev->dev);
+ struct device_node *np = p->pdev->dev.of_node;
+ struct of_phandle_args args;
+ int ret;
+
+ if (pdata) {
+ p->config = *pdata;
+ } else if (IS_ENABLED(CONFIG_OF) && np) {
+ const struct of_device_id *match;
+ const struct gpio_rcar_info *info;
+
+ match = of_match_node(gpio_rcar_of_table, np);
+ if (!match)
+ return -EINVAL;
+
+ info = match->data;
+
+ ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0,
+ &args);
+ p->config.number_of_pins = ret == 0 ? args.args[2]
+ : RCAR_MAX_GPIO_PER_BANK;
+ p->config.gpio_base = -1;
+ p->config.has_both_edge_trigger = info->has_both_edge_trigger;
+ }
+
+ if (p->config.number_of_pins == 0 ||
+ p->config.number_of_pins > RCAR_MAX_GPIO_PER_BANK) {
+ dev_warn(&p->pdev->dev,
+ "Invalid number of gpio lines %u, using %u\n",
+ p->config.number_of_pins, RCAR_MAX_GPIO_PER_BANK);
+ p->config.number_of_pins = RCAR_MAX_GPIO_PER_BANK;
+ }
+
+ return 0;
+}
+
+static int gpio_rcar_probe(struct platform_device *pdev)
+{
+ struct gpio_rcar_priv *p;
+ struct resource *io, *irq;
+ struct gpio_chip *gpio_chip;
+ struct irq_chip *irq_chip;
+ struct device *dev = &pdev->dev;
+ const char *name = dev_name(dev);
+ int ret;
+
+ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->pdev = pdev;
+ spin_lock_init(&p->lock);
+
+ /* Get device configuration from DT node or platform data. */
+ ret = gpio_rcar_parse_pdata(p);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, p);
+
+ p->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(p->clk)) {
+ dev_warn(dev, "unable to get clock\n");
+ p->clk = NULL;
+ }
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ if (!io || !irq) {
+ dev_err(dev, "missing IRQ or IOMEM\n");
+ ret = -EINVAL;
+ goto err0;
+ }
+
+ p->base = devm_ioremap_nocache(dev, io->start, resource_size(io));
+ if (!p->base) {
+ dev_err(dev, "failed to remap I/O memory\n");
+ ret = -ENXIO;
+ goto err0;
+ }
+
+ gpio_chip = &p->gpio_chip;
+ gpio_chip->request = gpio_rcar_request;
+ gpio_chip->free = gpio_rcar_free;
+ gpio_chip->direction_input = gpio_rcar_direction_input;
+ gpio_chip->get = gpio_rcar_get;
+ gpio_chip->direction_output = gpio_rcar_direction_output;
+ gpio_chip->set = gpio_rcar_set;
+ gpio_chip->label = name;
+ gpio_chip->dev = dev;
+ gpio_chip->owner = THIS_MODULE;
+ gpio_chip->base = p->config.gpio_base;
+ gpio_chip->ngpio = p->config.number_of_pins;
+
+ irq_chip = &p->irq_chip;
+ irq_chip->name = name;
+ 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;
+
+ ret = gpiochip_add(gpio_chip);
+ if (ret) {
+ dev_err(dev, "failed to add GPIO controller\n");
+ goto err0;
+ }
+
+ ret = gpiochip_irqchip_add(gpio_chip, irq_chip, p->config.irq_base,
+ handle_level_irq, IRQ_TYPE_NONE);
+ if (ret) {
+ dev_err(dev, "cannot add irqchip\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;
+ }
+
+ dev_info(dev, "driving %d GPIOs\n", p->config.number_of_pins);
+
+ /* warn in case of mismatch if irq base is specified */
+ if (p->config.irq_base) {
+ ret = irq_find_mapping(gpio_chip->irqdomain, 0);
+ if (p->config.irq_base != ret)
+ dev_warn(dev, "irq base mismatch (%u/%u)\n",
+ p->config.irq_base, ret);
+ }
+
+ if (p->config.pctl_name) {
+ ret = gpiochip_add_pin_range(gpio_chip, p->config.pctl_name, 0,
+ gpio_chip->base, gpio_chip->ngpio);
+ if (ret < 0)
+ dev_warn(dev, "failed to add pin range\n");
+ }
+
+ return 0;
+
+err1:
+ gpiochip_remove(gpio_chip);
+err0:
+ pm_runtime_put(dev);
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static int gpio_rcar_remove(struct platform_device *pdev)
+{
+ struct gpio_rcar_priv *p = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&p->gpio_chip);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver gpio_rcar_device_driver = {
+ .probe = gpio_rcar_probe,
+ .remove = gpio_rcar_remove,
+ .driver = {
+ .name = "gpio_rcar",
+ .of_match_table = of_match_ptr(gpio_rcar_of_table),
+ }
+};
+
+module_platform_driver(gpio_rcar_device_driver);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas R-Car GPIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c
new file mode 100644
index 000000000..d729bc8a5
--- /dev/null
+++ b/drivers/gpio/gpio-rdc321x.c
@@ -0,0 +1,221 @@
+/*
+ * RDC321x GPIO driver
+ *
+ * Copyright (C) 2008, Volker Weiss <dev@tintuc.de>
+ * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/mfd/rdc321x.h>
+#include <linux/slab.h>
+
+struct rdc321x_gpio {
+ spinlock_t lock;
+ struct pci_dev *sb_pdev;
+ u32 data_reg[2];
+ int reg1_ctrl_base;
+ int reg1_data_base;
+ int reg2_ctrl_base;
+ int reg2_data_base;
+ struct gpio_chip chip;
+};
+
+/* read GPIO pin */
+static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct rdc321x_gpio *gpch;
+ u32 value = 0;
+ int reg;
+
+ gpch = container_of(chip, struct rdc321x_gpio, chip);
+ reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base;
+
+ spin_lock(&gpch->lock);
+ pci_write_config_dword(gpch->sb_pdev, reg,
+ gpch->data_reg[gpio < 32 ? 0 : 1]);
+ pci_read_config_dword(gpch->sb_pdev, reg, &value);
+ spin_unlock(&gpch->lock);
+
+ return (1 << (gpio & 0x1f)) & value ? 1 : 0;
+}
+
+static void rdc_gpio_set_value_impl(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct rdc321x_gpio *gpch;
+ int reg = (gpio < 32) ? 0 : 1;
+
+ gpch = container_of(chip, struct rdc321x_gpio, chip);
+
+ if (value)
+ gpch->data_reg[reg] |= 1 << (gpio & 0x1f);
+ else
+ gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f));
+
+ pci_write_config_dword(gpch->sb_pdev,
+ reg ? gpch->reg2_data_base : gpch->reg1_data_base,
+ gpch->data_reg[reg]);
+}
+
+/* set GPIO pin to value */
+static void rdc_gpio_set_value(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct rdc321x_gpio *gpch;
+
+ gpch = container_of(chip, struct rdc321x_gpio, chip);
+ spin_lock(&gpch->lock);
+ rdc_gpio_set_value_impl(chip, gpio, value);
+ spin_unlock(&gpch->lock);
+}
+
+static int rdc_gpio_config(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct rdc321x_gpio *gpch;
+ int err;
+ u32 reg;
+
+ gpch = container_of(chip, struct rdc321x_gpio, chip);
+
+ spin_lock(&gpch->lock);
+ err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ?
+ gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, &reg);
+ if (err)
+ goto unlock;
+
+ reg |= 1 << (gpio & 0x1f);
+
+ err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ?
+ gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg);
+ if (err)
+ goto unlock;
+
+ rdc_gpio_set_value_impl(chip, gpio, value);
+
+unlock:
+ spin_unlock(&gpch->lock);
+
+ return err;
+}
+
+/* configure GPIO pin as input */
+static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ return rdc_gpio_config(chip, gpio, 1);
+}
+
+/*
+ * Cache the initial value of both GPIO data registers
+ */
+static int rdc321x_gpio_probe(struct platform_device *pdev)
+{
+ int err;
+ struct resource *r;
+ struct rdc321x_gpio *rdc321x_gpio_dev;
+ struct rdc321x_gpio_pdata *pdata;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data supplied\n");
+ return -ENODEV;
+ }
+
+ rdc321x_gpio_dev = devm_kzalloc(&pdev->dev, sizeof(struct rdc321x_gpio),
+ GFP_KERNEL);
+ if (!rdc321x_gpio_dev)
+ return -ENOMEM;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1");
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n");
+ return -ENODEV;
+ }
+
+ spin_lock_init(&rdc321x_gpio_dev->lock);
+ rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev;
+ rdc321x_gpio_dev->reg1_ctrl_base = r->start;
+ rdc321x_gpio_dev->reg1_data_base = r->start + 0x4;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2");
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n");
+ return -ENODEV;
+ }
+
+ rdc321x_gpio_dev->reg2_ctrl_base = r->start;
+ rdc321x_gpio_dev->reg2_data_base = r->start + 0x4;
+
+ rdc321x_gpio_dev->chip.label = "rdc321x-gpio";
+ rdc321x_gpio_dev->chip.owner = THIS_MODULE;
+ rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input;
+ rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config;
+ rdc321x_gpio_dev->chip.get = rdc_gpio_get_value;
+ rdc321x_gpio_dev->chip.set = rdc_gpio_set_value;
+ rdc321x_gpio_dev->chip.base = 0;
+ rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios;
+
+ platform_set_drvdata(pdev, rdc321x_gpio_dev);
+
+ /* This might not be, what others (BIOS, bootloader, etc.)
+ wrote to these registers before, but it's a good guess. Still
+ better than just using 0xffffffff. */
+ err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
+ rdc321x_gpio_dev->reg1_data_base,
+ &rdc321x_gpio_dev->data_reg[0]);
+ if (err)
+ return err;
+
+ err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
+ rdc321x_gpio_dev->reg2_data_base,
+ &rdc321x_gpio_dev->data_reg[1]);
+ if (err)
+ return err;
+
+ dev_info(&pdev->dev, "registering %d GPIOs\n",
+ rdc321x_gpio_dev->chip.ngpio);
+ return gpiochip_add(&rdc321x_gpio_dev->chip);
+}
+
+static int rdc321x_gpio_remove(struct platform_device *pdev)
+{
+ struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&rdc321x_gpio_dev->chip);
+
+ return 0;
+}
+
+static struct platform_driver rdc321x_gpio_driver = {
+ .driver.name = "rdc321x-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = rdc321x_gpio_probe,
+ .remove = rdc321x_gpio_remove,
+};
+
+module_platform_driver(rdc321x_gpio_driver);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_DESCRIPTION("RDC321x GPIO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rdc321x-gpio");
diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c
new file mode 100644
index 000000000..bec397a60
--- /dev/null
+++ b/drivers/gpio/gpio-sa1100.c
@@ -0,0 +1,267 @@
+/*
+ * linux/arch/arm/mach-sa1100/gpio.c
+ *
+ * Generic SA-1100 GPIO handling
+ *
+ * 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.
+ */
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/syscore_ops.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return GPLR & GPIO_GPIO(offset);
+}
+
+static void sa1100_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ if (value)
+ GPSR = GPIO_GPIO(offset);
+ else
+ GPCR = GPIO_GPIO(offset);
+}
+
+static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ GPDR &= ~GPIO_GPIO(offset);
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int sa1100_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sa1100_gpio_set(chip, offset, value);
+ GPDR |= GPIO_GPIO(offset);
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int sa1100_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return IRQ_GPIO0 + offset;
+}
+
+static struct gpio_chip sa1100_gpio_chip = {
+ .label = "gpio",
+ .direction_input = sa1100_direction_input,
+ .direction_output = sa1100_direction_output,
+ .set = sa1100_gpio_set,
+ .get = sa1100_gpio_get,
+ .to_irq = sa1100_to_irq,
+ .base = 0,
+ .ngpio = GPIO_MAX + 1,
+};
+
+/*
+ * SA1100 GPIO edge detection for IRQs:
+ * IRQs are generated on Falling-Edge, Rising-Edge, or both.
+ * Use this instead of directly setting GRER/GFER.
+ */
+static int GPIO_IRQ_rising_edge;
+static int GPIO_IRQ_falling_edge;
+static int GPIO_IRQ_mask;
+
+static int sa1100_gpio_type(struct irq_data *d, unsigned int type)
+{
+ unsigned int mask;
+
+ mask = BIT(d->hwirq);
+
+ if (type == IRQ_TYPE_PROBE) {
+ if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask)
+ return 0;
+ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ }
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ GPIO_IRQ_rising_edge |= mask;
+ else
+ GPIO_IRQ_rising_edge &= ~mask;
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ GPIO_IRQ_falling_edge |= mask;
+ else
+ GPIO_IRQ_falling_edge &= ~mask;
+
+ GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask;
+ GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask;
+
+ return 0;
+}
+
+/*
+ * GPIO IRQs must be acknowledged.
+ */
+static void sa1100_gpio_ack(struct irq_data *d)
+{
+ GEDR = BIT(d->hwirq);
+}
+
+static void sa1100_gpio_mask(struct irq_data *d)
+{
+ unsigned int mask = BIT(d->hwirq);
+
+ GPIO_IRQ_mask &= ~mask;
+
+ GRER &= ~mask;
+ GFER &= ~mask;
+}
+
+static void sa1100_gpio_unmask(struct irq_data *d)
+{
+ unsigned int mask = BIT(d->hwirq);
+
+ GPIO_IRQ_mask |= mask;
+
+ GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask;
+ GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask;
+}
+
+static int sa1100_gpio_wake(struct irq_data *d, unsigned int on)
+{
+ if (on)
+ PWER |= BIT(d->hwirq);
+ else
+ PWER &= ~BIT(d->hwirq);
+ return 0;
+}
+
+/*
+ * This is for GPIO IRQs
+ */
+static struct irq_chip sa1100_gpio_irq_chip = {
+ .name = "GPIO",
+ .irq_ack = sa1100_gpio_ack,
+ .irq_mask = sa1100_gpio_mask,
+ .irq_unmask = sa1100_gpio_unmask,
+ .irq_set_type = sa1100_gpio_type,
+ .irq_set_wake = sa1100_gpio_wake,
+};
+
+static int sa1100_gpio_irqdomain_map(struct irq_domain *d,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &sa1100_gpio_irq_chip,
+ handle_edge_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+
+ return 0;
+}
+
+static struct irq_domain_ops sa1100_gpio_irqdomain_ops = {
+ .map = sa1100_gpio_irqdomain_map,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+static struct irq_domain *sa1100_gpio_irqdomain;
+
+/*
+ * IRQ 0-11 (GPIO) handler. We enter here with the
+ * irq_controller_lock held, and IRQs disabled. Decode the IRQ
+ * and call the handler.
+ */
+static void
+sa1100_gpio_handler(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned int mask;
+
+ mask = GEDR;
+ do {
+ /*
+ * clear down all currently active IRQ sources.
+ * We will be processing them all.
+ */
+ GEDR = mask;
+
+ irq = IRQ_GPIO0;
+ do {
+ if (mask & 1)
+ generic_handle_irq(irq);
+ mask >>= 1;
+ irq++;
+ } while (mask);
+
+ mask = GEDR;
+ } while (mask);
+}
+
+static int sa1100_gpio_suspend(void)
+{
+ /*
+ * Set the appropriate edges for wakeup.
+ */
+ GRER = PWER & GPIO_IRQ_rising_edge;
+ GFER = PWER & GPIO_IRQ_falling_edge;
+
+ /*
+ * Clear any pending GPIO interrupts.
+ */
+ GEDR = GEDR;
+
+ return 0;
+}
+
+static void sa1100_gpio_resume(void)
+{
+ GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask;
+ GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask;
+}
+
+static struct syscore_ops sa1100_gpio_syscore_ops = {
+ .suspend = sa1100_gpio_suspend,
+ .resume = sa1100_gpio_resume,
+};
+
+static int __init sa1100_gpio_init_devicefs(void)
+{
+ register_syscore_ops(&sa1100_gpio_syscore_ops);
+ return 0;
+}
+
+device_initcall(sa1100_gpio_init_devicefs);
+
+void __init sa1100_init_gpio(void)
+{
+ /* clear all GPIO edge detects */
+ GFER = 0;
+ GRER = 0;
+ GEDR = -1;
+
+ gpiochip_add(&sa1100_gpio_chip);
+
+ sa1100_gpio_irqdomain = irq_domain_add_simple(NULL,
+ 28, IRQ_GPIO0,
+ &sa1100_gpio_irqdomain_ops, NULL);
+
+ /*
+ * Install handlers for GPIO 0-10 edge detect interrupts
+ */
+ irq_set_chained_handler(IRQ_GPIO0_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO1_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO2_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO3_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO4_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO5_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO6_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO7_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO8_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO9_SC, sa1100_gpio_handler);
+ irq_set_chained_handler(IRQ_GPIO10_SC, sa1100_gpio_handler);
+ /*
+ * Install handler for GPIO 11-27 edge detect interrupts
+ */
+ irq_set_chained_handler(IRQ_GPIO11_27, sa1100_gpio_handler);
+
+}
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
new file mode 100644
index 000000000..7c288ba4d
--- /dev/null
+++ b/drivers/gpio/gpio-samsung.c
@@ -0,0 +1,1328 @@
+/*
+ * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * SAMSUNG - GPIOlib support
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+
+#include <asm/irq.h>
+
+#include <mach/map.h>
+#include <mach/regs-gpio.h>
+#include <mach/gpio-samsung.h>
+
+#include <plat/cpu.h>
+#include <plat/gpio-core.h>
+#include <plat/gpio-cfg.h>
+#include <plat/gpio-cfg-helpers.h>
+#include <plat/pm.h>
+
+int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ void __iomem *reg = chip->base + 0x08;
+ int shift = off * 2;
+ u32 pup;
+
+ pup = __raw_readl(reg);
+ pup &= ~(3 << shift);
+ pup |= pull << shift;
+ __raw_writel(pup, reg);
+
+ return 0;
+}
+
+samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ void __iomem *reg = chip->base + 0x08;
+ int shift = off * 2;
+ u32 pup = __raw_readl(reg);
+
+ pup >>= shift;
+ pup &= 0x3;
+
+ return (__force samsung_gpio_pull_t)pup;
+}
+
+int s3c2443_gpio_setpull(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ switch (pull) {
+ case S3C_GPIO_PULL_NONE:
+ pull = 0x01;
+ break;
+ case S3C_GPIO_PULL_UP:
+ pull = 0x00;
+ break;
+ case S3C_GPIO_PULL_DOWN:
+ pull = 0x02;
+ break;
+ }
+ return samsung_gpio_setpull_updown(chip, off, pull);
+}
+
+samsung_gpio_pull_t s3c2443_gpio_getpull(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ samsung_gpio_pull_t pull;
+
+ pull = samsung_gpio_getpull_updown(chip, off);
+
+ switch (pull) {
+ case 0x00:
+ pull = S3C_GPIO_PULL_UP;
+ break;
+ case 0x01:
+ case 0x03:
+ pull = S3C_GPIO_PULL_NONE;
+ break;
+ case 0x02:
+ pull = S3C_GPIO_PULL_DOWN;
+ break;
+ }
+
+ return pull;
+}
+
+static int s3c24xx_gpio_setpull_1(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull,
+ samsung_gpio_pull_t updown)
+{
+ void __iomem *reg = chip->base + 0x08;
+ u32 pup = __raw_readl(reg);
+
+ if (pull == updown)
+ pup &= ~(1 << off);
+ else if (pull == S3C_GPIO_PULL_NONE)
+ pup |= (1 << off);
+ else
+ return -EINVAL;
+
+ __raw_writel(pup, reg);
+ return 0;
+}
+
+static samsung_gpio_pull_t s3c24xx_gpio_getpull_1(struct samsung_gpio_chip *chip,
+ unsigned int off,
+ samsung_gpio_pull_t updown)
+{
+ void __iomem *reg = chip->base + 0x08;
+ u32 pup = __raw_readl(reg);
+
+ pup &= (1 << off);
+ return pup ? S3C_GPIO_PULL_NONE : updown;
+}
+
+samsung_gpio_pull_t s3c24xx_gpio_getpull_1up(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ return s3c24xx_gpio_getpull_1(chip, off, S3C_GPIO_PULL_UP);
+}
+
+int s3c24xx_gpio_setpull_1up(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_UP);
+}
+
+samsung_gpio_pull_t s3c24xx_gpio_getpull_1down(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ return s3c24xx_gpio_getpull_1(chip, off, S3C_GPIO_PULL_DOWN);
+}
+
+int s3c24xx_gpio_setpull_1down(struct samsung_gpio_chip *chip,
+ unsigned int off, samsung_gpio_pull_t pull)
+{
+ return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_DOWN);
+}
+
+/*
+ * samsung_gpio_setcfg_2bit - Samsung 2bit style GPIO configuration.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register
+ * has two bits of configuration per gpio, which have the following
+ * functions:
+ * 00 = input
+ * 01 = output
+ * 1x = special function
+ */
+
+static int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = off * 2;
+ u32 con;
+
+ if (samsung_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+ if (cfg > 3)
+ return -EINVAL;
+
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0x3 << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+
+/*
+ * samsung_gpio_getcfg_2bit - Samsung 2bit style GPIO configuration read.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ *
+ * The reverse of samsung_gpio_setcfg_2bit(). Will return a value which
+ * could be directly passed back to samsung_gpio_setcfg_2bit(), from the
+ * S3C_GPIO_SPECIAL() macro.
+ */
+
+static unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ u32 con;
+
+ con = __raw_readl(chip->base);
+ con >>= off * 2;
+ con &= 3;
+
+ /* this conversion works for IN and OUT as well as special mode */
+ return S3C_GPIO_SPECIAL(con);
+}
+
+/*
+ * samsung_gpio_setcfg_4bit - Samsung 4bit single register GPIO config.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register has 4 bits
+ * of control per GPIO, generally in the form of:
+ * 0000 = Input
+ * 0001 = Output
+ * others = Special functions (dependent on bank)
+ *
+ * Note, since the code to deal with the case where there are two control
+ * registers instead of one, we do not have a separate set of functions for
+ * each case.
+ */
+
+static int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = (off & 7) * 4;
+ u32 con;
+
+ if (off < 8 && chip->chip.ngpio > 8)
+ reg -= 4;
+
+ if (samsung_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0xf << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+
+/*
+ * samsung_gpio_getcfg_4bit - Samsung 4bit single register GPIO config read.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ *
+ * The reverse of samsung_gpio_setcfg_4bit(), turning a gpio configuration
+ * register setting into a value the software can use, such as could be passed
+ * to samsung_gpio_setcfg_4bit().
+ *
+ * @sa samsung_gpio_getcfg_2bit
+ */
+
+static unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = (off & 7) * 4;
+ u32 con;
+
+ if (off < 8 && chip->chip.ngpio > 8)
+ reg -= 4;
+
+ con = __raw_readl(reg);
+ con >>= shift;
+ con &= 0xf;
+
+ /* this conversion works for IN and OUT as well as special mode */
+ return S3C_GPIO_SPECIAL(con);
+}
+
+#ifdef CONFIG_PLAT_S3C24XX
+/*
+ * s3c24xx_gpio_setcfg_abank - S3C24XX style GPIO configuration (Bank A)
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register
+ * has one bit of configuration for the gpio, where setting the bit
+ * means the pin is in special function mode and unset means output.
+ */
+
+static int s3c24xx_gpio_setcfg_abank(struct samsung_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = off;
+ u32 con;
+
+ if (samsung_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+
+ /* Map output to 0, and SFN2 to 1 */
+ cfg -= 1;
+ if (cfg > 1)
+ return -EINVAL;
+
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0x1 << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+
+/*
+ * s3c24xx_gpio_getcfg_abank - S3C24XX style GPIO configuration read (Bank A)
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ *
+ * The reverse of s3c24xx_gpio_setcfg_abank() turning an GPIO into a usable
+ * GPIO configuration value.
+ *
+ * @sa samsung_gpio_getcfg_2bit
+ * @sa samsung_gpio_getcfg_4bit
+ */
+
+static unsigned s3c24xx_gpio_getcfg_abank(struct samsung_gpio_chip *chip,
+ unsigned int off)
+{
+ u32 con;
+
+ con = __raw_readl(chip->base);
+ con >>= off;
+ con &= 1;
+ con++;
+
+ return S3C_GPIO_SFN(con);
+}
+#endif
+
+static void __init samsung_gpiolib_set_cfg(struct samsung_gpio_cfg *chipcfg,
+ int nr_chips)
+{
+ for (; nr_chips > 0; nr_chips--, chipcfg++) {
+ if (!chipcfg->set_config)
+ chipcfg->set_config = samsung_gpio_setcfg_4bit;
+ if (!chipcfg->get_config)
+ chipcfg->get_config = samsung_gpio_getcfg_4bit;
+ if (!chipcfg->set_pull)
+ chipcfg->set_pull = samsung_gpio_setpull_updown;
+ if (!chipcfg->get_pull)
+ chipcfg->get_pull = samsung_gpio_getpull_updown;
+ }
+}
+
+struct samsung_gpio_cfg s3c24xx_gpiocfg_default = {
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+};
+
+#ifdef CONFIG_PLAT_S3C24XX
+static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = {
+ .set_config = s3c24xx_gpio_setcfg_abank,
+ .get_config = s3c24xx_gpio_getcfg_abank,
+};
+#endif
+
+static struct samsung_gpio_cfg samsung_gpio_cfgs[] = {
+ [0] = {
+ .cfg_eint = 0x0,
+ },
+ [1] = {
+ .cfg_eint = 0x3,
+ },
+ [2] = {
+ .cfg_eint = 0x7,
+ },
+ [3] = {
+ .cfg_eint = 0xF,
+ },
+ [4] = {
+ .cfg_eint = 0x0,
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+ },
+ [5] = {
+ .cfg_eint = 0x2,
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+ },
+ [6] = {
+ .cfg_eint = 0x3,
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+ },
+ [7] = {
+ .set_config = samsung_gpio_setcfg_2bit,
+ .get_config = samsung_gpio_getcfg_2bit,
+ },
+};
+
+/*
+ * Default routines for controlling GPIO, based on the original S3C24XX
+ * GPIO functions which deal with the case where each gpio bank of the
+ * chip is as following:
+ *
+ * base + 0x00: Control register, 2 bits per gpio
+ * gpio n: 2 bits starting at (2*n)
+ * 00 = input, 01 = output, others mean special-function
+ * base + 0x04: Data register, 1 bit per gpio
+ * bit n: data bit n
+*/
+
+static int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long con;
+
+ samsung_gpio_lock(ourchip, flags);
+
+ con = __raw_readl(base + 0x00);
+ con &= ~(3 << (offset * 2));
+
+ __raw_writel(con, base + 0x00);
+
+ samsung_gpio_unlock(ourchip, flags);
+ return 0;
+}
+
+static int samsung_gpiolib_2bit_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long dat;
+ unsigned long con;
+
+ samsung_gpio_lock(ourchip, flags);
+
+ dat = __raw_readl(base + 0x04);
+ dat &= ~(1 << offset);
+ if (value)
+ dat |= 1 << offset;
+ __raw_writel(dat, base + 0x04);
+
+ con = __raw_readl(base + 0x00);
+ con &= ~(3 << (offset * 2));
+ con |= 1 << (offset * 2);
+
+ __raw_writel(con, base + 0x00);
+ __raw_writel(dat, base + 0x04);
+
+ samsung_gpio_unlock(ourchip, flags);
+ return 0;
+}
+
+/*
+ * The samsung_gpiolib_4bit routines are to control the gpio banks where
+ * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the
+ * following example:
+ *
+ * base + 0x00: Control register, 4 bits per gpio
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x04: Data register, 1 bit per gpio
+ * bit n: data bit n
+ *
+ * Note, since the data register is one bit per gpio and is at base + 0x4
+ * we can use samsung_gpiolib_get and samsung_gpiolib_set to change the
+ * state of the output.
+ */
+
+static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long con;
+
+ con = __raw_readl(base + GPIOCON_OFF);
+ if (ourchip->bitmap_gpio_int & BIT(offset))
+ con |= 0xf << con_4bit_shift(offset);
+ else
+ con &= ~(0xf << con_4bit_shift(offset));
+ __raw_writel(con, base + GPIOCON_OFF);
+
+ pr_debug("%s: %p: CON now %08lx\n", __func__, base, con);
+
+ return 0;
+}
+
+static int samsung_gpiolib_4bit_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long con;
+ unsigned long dat;
+
+ con = __raw_readl(base + GPIOCON_OFF);
+ con &= ~(0xf << con_4bit_shift(offset));
+ con |= 0x1 << con_4bit_shift(offset);
+
+ dat = __raw_readl(base + GPIODAT_OFF);
+
+ if (value)
+ dat |= 1 << offset;
+ else
+ dat &= ~(1 << offset);
+
+ __raw_writel(dat, base + GPIODAT_OFF);
+ __raw_writel(con, base + GPIOCON_OFF);
+ __raw_writel(dat, base + GPIODAT_OFF);
+
+ pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
+
+ return 0;
+}
+
+/*
+ * The next set of routines are for the case where the GPIO configuration
+ * registers are 4 bits per GPIO but there is more than one register (the
+ * bank has more than 8 GPIOs.
+ *
+ * This case is the similar to the 4 bit case, but the registers are as
+ * follows:
+ *
+ * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs)
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs)
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x08: Data register, 1 bit per gpio
+ * bit n: data bit n
+ *
+ * To allow us to use the samsung_gpiolib_get and samsung_gpiolib_set
+ * routines we store the 'base + 0x4' address so that these routines see
+ * the data register at ourchip->base + 0x04.
+ */
+
+static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ void __iomem *regcon = base;
+ unsigned long con;
+
+ if (offset > 7)
+ offset -= 8;
+ else
+ regcon -= 4;
+
+ con = __raw_readl(regcon);
+ con &= ~(0xf << con_4bit_shift(offset));
+ __raw_writel(con, regcon);
+
+ pr_debug("%s: %p: CON %08lx\n", __func__, base, con);
+
+ return 0;
+}
+
+static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ void __iomem *regcon = base;
+ unsigned long con;
+ unsigned long dat;
+ unsigned con_offset = offset;
+
+ if (con_offset > 7)
+ con_offset -= 8;
+ else
+ regcon -= 4;
+
+ con = __raw_readl(regcon);
+ con &= ~(0xf << con_4bit_shift(con_offset));
+ con |= 0x1 << con_4bit_shift(con_offset);
+
+ dat = __raw_readl(base + GPIODAT_OFF);
+
+ if (value)
+ dat |= 1 << offset;
+ else
+ dat &= ~(1 << offset);
+
+ __raw_writel(dat, base + GPIODAT_OFF);
+ __raw_writel(con, regcon);
+ __raw_writel(dat, base + GPIODAT_OFF);
+
+ pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
+
+ return 0;
+}
+
+#ifdef CONFIG_PLAT_S3C24XX
+/* The next set of routines are for the case of s3c24xx bank a */
+
+static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset)
+{
+ return -EINVAL;
+}
+
+static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long dat;
+ unsigned long con;
+
+ local_irq_save(flags);
+
+ con = __raw_readl(base + 0x00);
+ dat = __raw_readl(base + 0x04);
+
+ dat &= ~(1 << offset);
+ if (value)
+ dat |= 1 << offset;
+
+ __raw_writel(dat, base + 0x04);
+
+ con &= ~(1 << offset);
+
+ __raw_writel(con, base + 0x00);
+ __raw_writel(dat, base + 0x04);
+
+ local_irq_restore(flags);
+ return 0;
+}
+#endif
+
+static void samsung_gpiolib_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long dat;
+
+ samsung_gpio_lock(ourchip, flags);
+
+ dat = __raw_readl(base + 0x04);
+ dat &= ~(1 << offset);
+ if (value)
+ dat |= 1 << offset;
+ __raw_writel(dat, base + 0x04);
+
+ samsung_gpio_unlock(ourchip, flags);
+}
+
+static int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+ unsigned long val;
+
+ val = __raw_readl(ourchip->base + 0x04);
+ val >>= offset;
+ val &= 1;
+
+ return val;
+}
+
+/*
+ * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios
+ * for use with the configuration calls, and other parts of the s3c gpiolib
+ * support code.
+ *
+ * Not all s3c support code will need this, as some configurations of cpu
+ * may only support one or two different configuration options and have an
+ * easy gpio to samsung_gpio_chip mapping function. If this is the case, then
+ * the machine support file should provide its own samsung_gpiolib_getchip()
+ * and any other necessary functions.
+ */
+
+#ifdef CONFIG_S3C_GPIO_TRACK
+struct samsung_gpio_chip *s3c_gpios[S3C_GPIO_END];
+
+static __init void s3c_gpiolib_track(struct samsung_gpio_chip *chip)
+{
+ unsigned int gpn;
+ int i;
+
+ gpn = chip->chip.base;
+ for (i = 0; i < chip->chip.ngpio; i++, gpn++) {
+ BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios));
+ s3c_gpios[gpn] = chip;
+ }
+}
+#endif /* CONFIG_S3C_GPIO_TRACK */
+
+/*
+ * samsung_gpiolib_add() - add the Samsung gpio_chip.
+ * @chip: The chip to register
+ *
+ * This is a wrapper to gpiochip_add() that takes our specific gpio chip
+ * information and makes the necessary alterations for the platform and
+ * notes the information for use with the configuration systems and any
+ * other parts of the system.
+ */
+
+static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip)
+{
+ struct gpio_chip *gc = &chip->chip;
+ int ret;
+
+ BUG_ON(!chip->base);
+ BUG_ON(!gc->label);
+ BUG_ON(!gc->ngpio);
+
+ spin_lock_init(&chip->lock);
+
+ if (!gc->direction_input)
+ gc->direction_input = samsung_gpiolib_2bit_input;
+ if (!gc->direction_output)
+ gc->direction_output = samsung_gpiolib_2bit_output;
+ if (!gc->set)
+ gc->set = samsung_gpiolib_set;
+ if (!gc->get)
+ gc->get = samsung_gpiolib_get;
+
+#ifdef CONFIG_PM
+ if (chip->pm != NULL) {
+ if (!chip->pm->save || !chip->pm->resume)
+ pr_err("gpio: %s has missing PM functions\n",
+ gc->label);
+ } else
+ pr_err("gpio: %s has no PM function\n", gc->label);
+#endif
+
+ /* gpiochip_add() prints own failure message on error. */
+ ret = gpiochip_add(gc);
+ if (ret >= 0)
+ s3c_gpiolib_track(chip);
+}
+
+static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip,
+ int nr_chips, void __iomem *base)
+{
+ int i;
+ struct gpio_chip *gc = &chip->chip;
+
+ for (i = 0 ; i < nr_chips; i++, chip++) {
+ /* skip banks not present on SoC */
+ if (chip->chip.base >= S3C_GPIO_END)
+ continue;
+
+ if (!chip->config)
+ chip->config = &s3c24xx_gpiocfg_default;
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_2bit);
+ if ((base != NULL) && (chip->base == NULL))
+ chip->base = base + ((i) * 0x10);
+
+ if (!gc->direction_input)
+ gc->direction_input = samsung_gpiolib_2bit_input;
+ if (!gc->direction_output)
+ gc->direction_output = samsung_gpiolib_2bit_output;
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+static void __init samsung_gpiolib_add_2bit_chips(struct samsung_gpio_chip *chip,
+ int nr_chips, void __iomem *base,
+ unsigned int offset)
+{
+ int i;
+
+ for (i = 0 ; i < nr_chips; i++, chip++) {
+ chip->chip.direction_input = samsung_gpiolib_2bit_input;
+ chip->chip.direction_output = samsung_gpiolib_2bit_output;
+
+ if (!chip->config)
+ chip->config = &samsung_gpio_cfgs[7];
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_2bit);
+ if ((base != NULL) && (chip->base == NULL))
+ chip->base = base + ((i) * offset);
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+/*
+ * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config.
+ * @chip: The gpio chip that is being configured.
+ * @nr_chips: The no of chips (gpio ports) for the GPIO being configured.
+ *
+ * This helper deal with the GPIO cases where the control register has 4 bits
+ * of control per GPIO, generally in the form of:
+ * 0000 = Input
+ * 0001 = Output
+ * others = Special functions (dependent on bank)
+ *
+ * Note, since the code to deal with the case where there are two control
+ * registers instead of one, we do not have a separate set of function
+ * (samsung_gpiolib_add_4bit2_chips)for each case.
+ */
+
+static void __init samsung_gpiolib_add_4bit_chips(struct samsung_gpio_chip *chip,
+ int nr_chips, void __iomem *base)
+{
+ int i;
+
+ for (i = 0 ; i < nr_chips; i++, chip++) {
+ chip->chip.direction_input = samsung_gpiolib_4bit_input;
+ chip->chip.direction_output = samsung_gpiolib_4bit_output;
+
+ if (!chip->config)
+ chip->config = &samsung_gpio_cfgs[2];
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_4bit);
+ if ((base != NULL) && (chip->base == NULL))
+ chip->base = base + ((i) * 0x20);
+
+ chip->bitmap_gpio_int = 0;
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+static void __init samsung_gpiolib_add_4bit2_chips(struct samsung_gpio_chip *chip,
+ int nr_chips)
+{
+ for (; nr_chips > 0; nr_chips--, chip++) {
+ chip->chip.direction_input = samsung_gpiolib_4bit2_input;
+ chip->chip.direction_output = samsung_gpiolib_4bit2_output;
+
+ if (!chip->config)
+ chip->config = &samsung_gpio_cfgs[2];
+ if (!chip->pm)
+ chip->pm = __gpio_pm(&samsung_gpio_pm_4bit);
+
+ samsung_gpiolib_add(chip);
+ }
+}
+
+int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct samsung_gpio_chip *samsung_chip = container_of(chip, struct samsung_gpio_chip, chip);
+
+ return samsung_chip->irq_base + offset;
+}
+
+#ifdef CONFIG_PLAT_S3C24XX
+static int s3c24xx_gpiolib_fbank_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset < 4) {
+ if (soc_is_s3c2412())
+ return IRQ_EINT0_2412 + offset;
+ else
+ return IRQ_EINT0 + offset;
+ }
+
+ if (offset < 8)
+ return IRQ_EINT4 + offset - 4;
+
+ return -EINVAL;
+}
+#endif
+
+#ifdef CONFIG_ARCH_S3C64XX
+static int s3c64xx_gpiolib_mbank_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO;
+}
+
+static int s3c64xx_gpiolib_lbank_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO;
+}
+#endif
+
+struct samsung_gpio_chip s3c24xx_gpios[] = {
+#ifdef CONFIG_PLAT_S3C24XX
+ {
+ .config = &s3c24xx_gpiocfg_banka,
+ .chip = {
+ .base = S3C2410_GPA(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOA",
+ .ngpio = 27,
+ .direction_input = s3c24xx_gpiolib_banka_input,
+ .direction_output = s3c24xx_gpiolib_banka_output,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPB(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOB",
+ .ngpio = 11,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPC(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOC",
+ .ngpio = 16,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPD(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOD",
+ .ngpio = 16,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPE(0),
+ .label = "GPIOE",
+ .owner = THIS_MODULE,
+ .ngpio = 16,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPF(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOF",
+ .ngpio = 8,
+ .to_irq = s3c24xx_gpiolib_fbank_to_irq,
+ },
+ }, {
+ .irq_base = IRQ_EINT8,
+ .chip = {
+ .base = S3C2410_GPG(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOG",
+ .ngpio = 16,
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .chip = {
+ .base = S3C2410_GPH(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOH",
+ .ngpio = 15,
+ },
+ },
+ /* GPIOS for the S3C2443 and later devices. */
+ {
+ .base = S3C2440_GPJCON,
+ .chip = {
+ .base = S3C2410_GPJ(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOJ",
+ .ngpio = 16,
+ },
+ }, {
+ .base = S3C2443_GPKCON,
+ .chip = {
+ .base = S3C2410_GPK(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOK",
+ .ngpio = 16,
+ },
+ }, {
+ .base = S3C2443_GPLCON,
+ .chip = {
+ .base = S3C2410_GPL(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOL",
+ .ngpio = 15,
+ },
+ }, {
+ .base = S3C2443_GPMCON,
+ .chip = {
+ .base = S3C2410_GPM(0),
+ .owner = THIS_MODULE,
+ .label = "GPIOM",
+ .ngpio = 2,
+ },
+ },
+#endif
+};
+
+/*
+ * GPIO bank summary:
+ *
+ * Bank GPIOs Style SlpCon ExtInt Group
+ * A 8 4Bit Yes 1
+ * B 7 4Bit Yes 1
+ * C 8 4Bit Yes 2
+ * D 5 4Bit Yes 3
+ * E 5 4Bit Yes None
+ * F 16 2Bit Yes 4 [1]
+ * G 7 4Bit Yes 5
+ * H 10 4Bit[2] Yes 6
+ * I 16 2Bit Yes None
+ * J 12 2Bit Yes None
+ * K 16 4Bit[2] No None
+ * L 15 4Bit[2] No None
+ * M 6 4Bit No IRQ_EINT
+ * N 16 2Bit No IRQ_EINT
+ * O 16 2Bit Yes 7
+ * P 15 2Bit Yes 8
+ * Q 9 2Bit Yes 9
+ *
+ * [1] BANKF pins 14,15 do not form part of the external interrupt sources
+ * [2] BANK has two control registers, GPxCON0 and GPxCON1
+ */
+
+static struct samsung_gpio_chip s3c64xx_gpios_4bit[] = {
+#ifdef CONFIG_ARCH_S3C64XX
+ {
+ .chip = {
+ .base = S3C64XX_GPA(0),
+ .ngpio = S3C64XX_GPIO_A_NR,
+ .label = "GPA",
+ },
+ }, {
+ .chip = {
+ .base = S3C64XX_GPB(0),
+ .ngpio = S3C64XX_GPIO_B_NR,
+ .label = "GPB",
+ },
+ }, {
+ .chip = {
+ .base = S3C64XX_GPC(0),
+ .ngpio = S3C64XX_GPIO_C_NR,
+ .label = "GPC",
+ },
+ }, {
+ .chip = {
+ .base = S3C64XX_GPD(0),
+ .ngpio = S3C64XX_GPIO_D_NR,
+ .label = "GPD",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[0],
+ .chip = {
+ .base = S3C64XX_GPE(0),
+ .ngpio = S3C64XX_GPIO_E_NR,
+ .label = "GPE",
+ },
+ }, {
+ .base = S3C64XX_GPG_BASE,
+ .chip = {
+ .base = S3C64XX_GPG(0),
+ .ngpio = S3C64XX_GPIO_G_NR,
+ .label = "GPG",
+ },
+ }, {
+ .base = S3C64XX_GPM_BASE,
+ .config = &samsung_gpio_cfgs[1],
+ .chip = {
+ .base = S3C64XX_GPM(0),
+ .ngpio = S3C64XX_GPIO_M_NR,
+ .label = "GPM",
+ .to_irq = s3c64xx_gpiolib_mbank_to_irq,
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s3c64xx_gpios_4bit2[] = {
+#ifdef CONFIG_ARCH_S3C64XX
+ {
+ .base = S3C64XX_GPH_BASE + 0x4,
+ .chip = {
+ .base = S3C64XX_GPH(0),
+ .ngpio = S3C64XX_GPIO_H_NR,
+ .label = "GPH",
+ },
+ }, {
+ .base = S3C64XX_GPK_BASE + 0x4,
+ .config = &samsung_gpio_cfgs[0],
+ .chip = {
+ .base = S3C64XX_GPK(0),
+ .ngpio = S3C64XX_GPIO_K_NR,
+ .label = "GPK",
+ },
+ }, {
+ .base = S3C64XX_GPL_BASE + 0x4,
+ .config = &samsung_gpio_cfgs[1],
+ .chip = {
+ .base = S3C64XX_GPL(0),
+ .ngpio = S3C64XX_GPIO_L_NR,
+ .label = "GPL",
+ .to_irq = s3c64xx_gpiolib_lbank_to_irq,
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip s3c64xx_gpios_2bit[] = {
+#ifdef CONFIG_ARCH_S3C64XX
+ {
+ .base = S3C64XX_GPF_BASE,
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S3C64XX_GPF(0),
+ .ngpio = S3C64XX_GPIO_F_NR,
+ .label = "GPF",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[7],
+ .chip = {
+ .base = S3C64XX_GPI(0),
+ .ngpio = S3C64XX_GPIO_I_NR,
+ .label = "GPI",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[7],
+ .chip = {
+ .base = S3C64XX_GPJ(0),
+ .ngpio = S3C64XX_GPIO_J_NR,
+ .label = "GPJ",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S3C64XX_GPO(0),
+ .ngpio = S3C64XX_GPIO_O_NR,
+ .label = "GPO",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S3C64XX_GPP(0),
+ .ngpio = S3C64XX_GPIO_P_NR,
+ .label = "GPP",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[6],
+ .chip = {
+ .base = S3C64XX_GPQ(0),
+ .ngpio = S3C64XX_GPIO_Q_NR,
+ .label = "GPQ",
+ },
+ }, {
+ .base = S3C64XX_GPN_BASE,
+ .irq_base = IRQ_EINT(0),
+ .config = &samsung_gpio_cfgs[5],
+ .chip = {
+ .base = S3C64XX_GPN(0),
+ .ngpio = S3C64XX_GPIO_N_NR,
+ .label = "GPN",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ },
+#endif
+};
+
+/* TODO: cleanup soc_is_* */
+static __init int samsung_gpiolib_init(void)
+{
+ /*
+ * Currently there are two drivers that can provide GPIO support for
+ * Samsung SoCs. For device tree enabled platforms, the new
+ * pinctrl-samsung driver is used, providing both GPIO and pin control
+ * interfaces. For legacy (non-DT) platforms this driver is used.
+ */
+ if (of_have_populated_dt())
+ return -ENODEV;
+
+ samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs));
+
+ if (soc_is_s3c24xx()) {
+ s3c24xx_gpiolib_add_chips(s3c24xx_gpios,
+ ARRAY_SIZE(s3c24xx_gpios), S3C24XX_VA_GPIO);
+ } else if (soc_is_s3c64xx()) {
+ samsung_gpiolib_add_2bit_chips(s3c64xx_gpios_2bit,
+ ARRAY_SIZE(s3c64xx_gpios_2bit),
+ S3C64XX_VA_GPIO + 0xE0, 0x20);
+ samsung_gpiolib_add_4bit_chips(s3c64xx_gpios_4bit,
+ ARRAY_SIZE(s3c64xx_gpios_4bit),
+ S3C64XX_VA_GPIO);
+ samsung_gpiolib_add_4bit2_chips(s3c64xx_gpios_4bit2,
+ ARRAY_SIZE(s3c64xx_gpios_4bit2));
+ } else {
+ WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+core_initcall(samsung_gpiolib_init);
+
+int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long flags;
+ int offset;
+ int ret;
+
+ if (!chip)
+ return -EINVAL;
+
+ offset = pin - chip->chip.base;
+
+ samsung_gpio_lock(chip, flags);
+ ret = samsung_gpio_do_setcfg(chip, offset, config);
+ samsung_gpio_unlock(chip, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(s3c_gpio_cfgpin);
+
+int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr,
+ unsigned int cfg)
+{
+ int ret;
+
+ for (; nr > 0; nr--, start++) {
+ ret = s3c_gpio_cfgpin(start, cfg);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(s3c_gpio_cfgpin_range);
+
+int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr,
+ unsigned int cfg, samsung_gpio_pull_t pull)
+{
+ int ret;
+
+ for (; nr > 0; nr--, start++) {
+ s3c_gpio_setpull(start, pull);
+ ret = s3c_gpio_cfgpin(start, cfg);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(s3c_gpio_cfgall_range);
+
+unsigned s3c_gpio_getcfg(unsigned int pin)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long flags;
+ unsigned ret = 0;
+ int offset;
+
+ if (chip) {
+ offset = pin - chip->chip.base;
+
+ samsung_gpio_lock(chip, flags);
+ ret = samsung_gpio_do_getcfg(chip, offset);
+ samsung_gpio_unlock(chip, flags);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(s3c_gpio_getcfg);
+
+int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long flags;
+ int offset, ret;
+
+ if (!chip)
+ return -EINVAL;
+
+ offset = pin - chip->chip.base;
+
+ samsung_gpio_lock(chip, flags);
+ ret = samsung_gpio_do_setpull(chip, offset, pull);
+ samsung_gpio_unlock(chip, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(s3c_gpio_setpull);
+
+samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin)
+{
+ struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
+ unsigned long flags;
+ int offset;
+ u32 pup = 0;
+
+ if (chip) {
+ offset = pin - chip->chip.base;
+
+ samsung_gpio_lock(chip, flags);
+ pup = samsung_gpio_do_getpull(chip, offset);
+ samsung_gpio_unlock(chip, flags);
+ }
+
+ return (__force samsung_gpio_pull_t)pup;
+}
+EXPORT_SYMBOL(s3c_gpio_getpull);
+
+#ifdef CONFIG_PLAT_S3C24XX
+unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change)
+{
+ unsigned long flags;
+ unsigned long misccr;
+
+ local_irq_save(flags);
+ misccr = __raw_readl(S3C24XX_MISCCR);
+ misccr &= ~clear;
+ misccr ^= change;
+ __raw_writel(misccr, S3C24XX_MISCCR);
+ local_irq_restore(flags);
+
+ return misccr;
+}
+EXPORT_SYMBOL(s3c2410_modify_misccr);
+#endif
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
new file mode 100644
index 000000000..b72906f5b
--- /dev/null
+++ b/drivers/gpio/gpio-sch.c
@@ -0,0 +1,244 @@
+/*
+ * GPIO interface for Intel Poulsbo SCH
+ *
+ * Copyright (c) 2010 CompuLab Ltd
+ * Author: Denis Turischev <denis@compulab.co.il>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/pci_ids.h>
+
+#include <linux/gpio.h>
+
+#define GEN 0x00
+#define GIO 0x04
+#define GLV 0x08
+
+struct sch_gpio {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ unsigned short iobase;
+ unsigned short core_base;
+ unsigned short resume_base;
+};
+
+#define to_sch_gpio(gc) container_of(gc, struct sch_gpio, chip)
+
+static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio,
+ unsigned reg)
+{
+ unsigned base = 0;
+
+ if (gpio >= sch->resume_base) {
+ gpio -= sch->resume_base;
+ base += 0x20;
+ }
+
+ return base + reg + gpio / 8;
+}
+
+static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
+{
+ if (gpio >= sch->resume_base)
+ gpio -= sch->resume_base;
+ return gpio % 8;
+}
+
+static int sch_gpio_reg_get(struct gpio_chip *gc, unsigned gpio, unsigned reg)
+{
+ struct sch_gpio *sch = to_sch_gpio(gc);
+ unsigned short offset, bit;
+ u8 reg_val;
+
+ offset = sch_gpio_offset(sch, gpio, reg);
+ bit = sch_gpio_bit(sch, gpio);
+
+ reg_val = !!(inb(sch->iobase + offset) & BIT(bit));
+
+ return reg_val;
+}
+
+static void sch_gpio_reg_set(struct gpio_chip *gc, unsigned gpio, unsigned reg,
+ int val)
+{
+ struct sch_gpio *sch = to_sch_gpio(gc);
+ unsigned short offset, bit;
+ u8 reg_val;
+
+ offset = sch_gpio_offset(sch, gpio, reg);
+ bit = sch_gpio_bit(sch, gpio);
+
+ reg_val = inb(sch->iobase + offset);
+
+ if (val)
+ outb(reg_val | BIT(bit), sch->iobase + offset);
+ else
+ outb((reg_val & ~BIT(bit)), sch->iobase + offset);
+}
+
+static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
+{
+ struct sch_gpio *sch = to_sch_gpio(gc);
+
+ spin_lock(&sch->lock);
+ sch_gpio_reg_set(gc, gpio_num, GIO, 1);
+ spin_unlock(&sch->lock);
+ return 0;
+}
+
+static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+ return sch_gpio_reg_get(gc, gpio_num, GLV);
+}
+
+static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
+{
+ struct sch_gpio *sch = to_sch_gpio(gc);
+
+ spin_lock(&sch->lock);
+ sch_gpio_reg_set(gc, gpio_num, GLV, val);
+ spin_unlock(&sch->lock);
+}
+
+static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
+ int val)
+{
+ struct sch_gpio *sch = to_sch_gpio(gc);
+
+ spin_lock(&sch->lock);
+ sch_gpio_reg_set(gc, gpio_num, GIO, 0);
+ spin_unlock(&sch->lock);
+
+ /*
+ * 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.
+ * 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
+ * and an external pull-up is connected.
+ */
+ sch_gpio_set(gc, gpio_num, val);
+ return 0;
+}
+
+static struct gpio_chip sch_gpio_chip = {
+ .label = "sch_gpio",
+ .owner = THIS_MODULE,
+ .direction_input = sch_gpio_direction_in,
+ .get = sch_gpio_get,
+ .direction_output = sch_gpio_direction_out,
+ .set = sch_gpio_set,
+};
+
+static int sch_gpio_probe(struct platform_device *pdev)
+{
+ struct sch_gpio *sch;
+ struct resource *res;
+
+ sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL);
+ if (!sch)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EBUSY;
+
+ if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name))
+ return -EBUSY;
+
+ spin_lock_init(&sch->lock);
+ sch->iobase = res->start;
+ sch->chip = sch_gpio_chip;
+ sch->chip.label = dev_name(&pdev->dev);
+ sch->chip.dev = &pdev->dev;
+
+ switch (pdev->id) {
+ case PCI_DEVICE_ID_INTEL_SCH_LPC:
+ sch->core_base = 0;
+ sch->resume_base = 10;
+ sch->chip.ngpio = 14;
+
+ /*
+ * GPIO[6:0] enabled by default
+ * GPIO7 is configured by the CMC as SLPIOVR
+ * Enable GPIO[9:8] core powered gpios explicitly
+ */
+ sch_gpio_reg_set(&sch->chip, 8, GEN, 1);
+ sch_gpio_reg_set(&sch->chip, 9, GEN, 1);
+ /*
+ * SUS_GPIO[2:0] enabled by default
+ * Enable SUS_GPIO3 resume powered gpio explicitly
+ */
+ sch_gpio_reg_set(&sch->chip, 13, GEN, 1);
+ break;
+
+ case PCI_DEVICE_ID_INTEL_ITC_LPC:
+ sch->core_base = 0;
+ sch->resume_base = 5;
+ sch->chip.ngpio = 14;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
+ sch->core_base = 0;
+ sch->resume_base = 21;
+ sch->chip.ngpio = 30;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB:
+ sch->core_base = 0;
+ sch->resume_base = 2;
+ sch->chip.ngpio = 8;
+ break;
+
+ default:
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, sch);
+
+ return gpiochip_add(&sch->chip);
+}
+
+static int sch_gpio_remove(struct platform_device *pdev)
+{
+ struct sch_gpio *sch = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&sch->chip);
+ return 0;
+}
+
+static struct platform_driver sch_gpio_driver = {
+ .driver = {
+ .name = "sch_gpio",
+ },
+ .probe = sch_gpio_probe,
+ .remove = sch_gpio_remove,
+};
+
+module_platform_driver(sch_gpio_driver);
+
+MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
+MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sch_gpio");
diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c
new file mode 100644
index 000000000..0cb11413e
--- /dev/null
+++ b/drivers/gpio/gpio-sch311x.c
@@ -0,0 +1,438 @@
+/*
+ * GPIO driver for the SMSC SCH311x Super-I/O chips
+ *
+ * Copyright (C) 2013 Bruno Randolf <br1@einfach.org>
+ *
+ * SuperIO functions and chip detection:
+ * (c) Copyright 2008 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#define DRV_NAME "gpio-sch311x"
+
+#define SCH311X_GPIO_CONF_OUT 0x00
+#define SCH311X_GPIO_CONF_IN 0x01
+#define SCH311X_GPIO_CONF_INVERT 0x02
+#define SCH311X_GPIO_CONF_OPEN_DRAIN 0x80
+
+#define SIO_CONFIG_KEY_ENTER 0x55
+#define SIO_CONFIG_KEY_EXIT 0xaa
+
+#define GP1 0x4b
+
+static int sch311x_ioports[] = { 0x2e, 0x4e, 0x162e, 0x164e };
+
+static struct platform_device *sch311x_gpio_pdev;
+
+struct sch311x_pdev_data { /* platform device data */
+ unsigned short runtime_reg; /* runtime register base address */
+};
+
+struct sch311x_gpio_block { /* one GPIO block runtime data */
+ struct gpio_chip chip;
+ unsigned short data_reg; /* from definition below */
+ unsigned short *config_regs; /* pointer to definition below */
+ unsigned short runtime_reg; /* runtime register */
+ spinlock_t lock; /* lock for this GPIO block */
+};
+
+struct sch311x_gpio_priv { /* driver private data */
+ struct sch311x_gpio_block blocks[6];
+};
+
+struct sch311x_gpio_block_def { /* register address definitions */
+ unsigned short data_reg;
+ unsigned short config_regs[8];
+ unsigned short base;
+};
+
+/* Note: some GPIOs are not available, these are marked with 0x00 */
+
+static struct sch311x_gpio_block_def sch311x_gpio_blocks[] = {
+ {
+ .data_reg = 0x4b, /* GP1 */
+ .config_regs = {0x23, 0x24, 0x25, 0x26, 0x27, 0x29, 0x2a, 0x2b},
+ .base = 10,
+ },
+ {
+ .data_reg = 0x4c, /* GP2 */
+ .config_regs = {0x00, 0x2c, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x32},
+ .base = 20,
+ },
+ {
+ .data_reg = 0x4d, /* GP3 */
+ .config_regs = {0x33, 0x34, 0x35, 0x36, 0x37, 0x00, 0x39, 0x3a},
+ .base = 30,
+ },
+ {
+ .data_reg = 0x4e, /* GP4 */
+ .config_regs = {0x3b, 0x00, 0x3d, 0x00, 0x6e, 0x6f, 0x72, 0x73},
+ .base = 40,
+ },
+ {
+ .data_reg = 0x4f, /* GP5 */
+ .config_regs = {0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46},
+ .base = 50,
+ },
+ {
+ .data_reg = 0x50, /* GP6 */
+ .config_regs = {0x47, 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59},
+ .base = 60,
+ },
+};
+
+static inline struct sch311x_gpio_block *
+to_sch311x_gpio_block(struct gpio_chip *chip)
+{
+ return container_of(chip, struct sch311x_gpio_block, chip);
+}
+
+
+/*
+ * Super-IO functions
+ */
+
+static inline int sch311x_sio_enter(int sio_config_port)
+{
+ /* Don't step on other drivers' I/O space by accident. */
+ if (!request_muxed_region(sio_config_port, 2, DRV_NAME)) {
+ pr_err(DRV_NAME "I/O address 0x%04x already in use\n",
+ sio_config_port);
+ return -EBUSY;
+ }
+
+ outb(SIO_CONFIG_KEY_ENTER, sio_config_port);
+ return 0;
+}
+
+static inline void sch311x_sio_exit(int sio_config_port)
+{
+ outb(SIO_CONFIG_KEY_EXIT, sio_config_port);
+ release_region(sio_config_port, 2);
+}
+
+static inline int sch311x_sio_inb(int sio_config_port, int reg)
+{
+ outb(reg, sio_config_port);
+ return inb(sio_config_port + 1);
+}
+
+static inline void sch311x_sio_outb(int sio_config_port, int reg, int val)
+{
+ outb(reg, sio_config_port);
+ outb(val, sio_config_port + 1);
+}
+
+
+/*
+ * GPIO functions
+ */
+
+static int sch311x_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct sch311x_gpio_block *block = to_sch311x_gpio_block(chip);
+
+ if (block->config_regs[offset] == 0) /* GPIO is not available */
+ return -ENODEV;
+
+ if (!request_region(block->runtime_reg + block->config_regs[offset],
+ 1, DRV_NAME)) {
+ dev_err(chip->dev, "Failed to request region 0x%04x.\n",
+ block->runtime_reg + block->config_regs[offset]);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static void sch311x_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct sch311x_gpio_block *block = to_sch311x_gpio_block(chip);
+
+ if (block->config_regs[offset] == 0) /* GPIO is not available */
+ return;
+
+ release_region(block->runtime_reg + block->config_regs[offset], 1);
+}
+
+static int sch311x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct sch311x_gpio_block *block = to_sch311x_gpio_block(chip);
+ unsigned char data;
+
+ spin_lock(&block->lock);
+ data = inb(block->runtime_reg + block->data_reg);
+ spin_unlock(&block->lock);
+
+ return !!(data & BIT(offset));
+}
+
+static void __sch311x_gpio_set(struct sch311x_gpio_block *block,
+ unsigned offset, int value)
+{
+ unsigned char data = inb(block->runtime_reg + block->data_reg);
+ if (value)
+ data |= BIT(offset);
+ else
+ data &= ~BIT(offset);
+ outb(data, block->runtime_reg + block->data_reg);
+}
+
+static void sch311x_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct sch311x_gpio_block *block = to_sch311x_gpio_block(chip);
+
+ spin_lock(&block->lock);
+ __sch311x_gpio_set(block, offset, value);
+ spin_unlock(&block->lock);
+}
+
+static int sch311x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct sch311x_gpio_block *block = to_sch311x_gpio_block(chip);
+
+ spin_lock(&block->lock);
+ outb(SCH311X_GPIO_CONF_IN, block->runtime_reg +
+ block->config_regs[offset]);
+ spin_unlock(&block->lock);
+
+ return 0;
+}
+
+static int sch311x_gpio_direction_out(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct sch311x_gpio_block *block = to_sch311x_gpio_block(chip);
+
+ spin_lock(&block->lock);
+
+ outb(SCH311X_GPIO_CONF_OUT, block->runtime_reg +
+ block->config_regs[offset]);
+
+ __sch311x_gpio_set(block, offset, value);
+
+ spin_unlock(&block->lock);
+ return 0;
+}
+
+static int sch311x_gpio_probe(struct platform_device *pdev)
+{
+ struct sch311x_pdev_data *pdata = pdev->dev.platform_data;
+ struct sch311x_gpio_priv *priv;
+ struct sch311x_gpio_block *block;
+ int err, i;
+
+ /* we can register all GPIO data registers at once */
+ if (!request_region(pdata->runtime_reg + GP1, 6, DRV_NAME)) {
+ dev_err(&pdev->dev, "Failed to request region 0x%04x-0x%04x.\n",
+ pdata->runtime_reg + GP1, pdata->runtime_reg + GP1 + 5);
+ return -EBUSY;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ for (i = 0; i < ARRAY_SIZE(priv->blocks); i++) {
+ block = &priv->blocks[i];
+
+ spin_lock_init(&block->lock);
+
+ block->chip.label = DRV_NAME;
+ block->chip.owner = THIS_MODULE;
+ block->chip.request = sch311x_gpio_request;
+ block->chip.free = sch311x_gpio_free;
+ block->chip.direction_input = sch311x_gpio_direction_in;
+ block->chip.direction_output = sch311x_gpio_direction_out;
+ block->chip.get = sch311x_gpio_get;
+ block->chip.set = sch311x_gpio_set;
+ block->chip.ngpio = 8;
+ block->chip.dev = &pdev->dev;
+ block->chip.base = sch311x_gpio_blocks[i].base;
+ block->config_regs = sch311x_gpio_blocks[i].config_regs;
+ block->data_reg = sch311x_gpio_blocks[i].data_reg;
+ block->runtime_reg = pdata->runtime_reg;
+
+ err = gpiochip_add(&block->chip);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "Could not register gpiochip, %d\n", err);
+ goto exit_err;
+ }
+ dev_info(&pdev->dev,
+ "SMSC SCH311x GPIO block %d registered.\n", i);
+ }
+
+ return 0;
+
+exit_err:
+ release_region(pdata->runtime_reg + GP1, 6);
+ /* release already registered chips */
+ for (--i; i >= 0; i--)
+ gpiochip_remove(&priv->blocks[i].chip);
+ return err;
+}
+
+static int sch311x_gpio_remove(struct platform_device *pdev)
+{
+ struct sch311x_pdev_data *pdata = pdev->dev.platform_data;
+ struct sch311x_gpio_priv *priv = platform_get_drvdata(pdev);
+ int i;
+
+ release_region(pdata->runtime_reg + GP1, 6);
+
+ for (i = 0; i < ARRAY_SIZE(priv->blocks); i++) {
+ gpiochip_remove(&priv->blocks[i].chip);
+ dev_info(&pdev->dev,
+ "SMSC SCH311x GPIO block %d unregistered.\n", i);
+ }
+ return 0;
+}
+
+static struct platform_driver sch311x_gpio_driver = {
+ .driver.name = DRV_NAME,
+ .driver.owner = THIS_MODULE,
+ .probe = sch311x_gpio_probe,
+ .remove = sch311x_gpio_remove,
+};
+
+
+/*
+ * Init & exit routines
+ */
+
+static int __init sch311x_detect(int sio_config_port, unsigned short *addr)
+{
+ int err = 0, reg;
+ unsigned short base_addr;
+ unsigned char dev_id;
+
+ err = sch311x_sio_enter(sio_config_port);
+ if (err)
+ return err;
+
+ /* Check device ID. */
+ reg = sch311x_sio_inb(sio_config_port, 0x20);
+ switch (reg) {
+ case 0x7c: /* SCH3112 */
+ dev_id = 2;
+ break;
+ case 0x7d: /* SCH3114 */
+ dev_id = 4;
+ break;
+ case 0x7f: /* SCH3116 */
+ dev_id = 6;
+ break;
+ default:
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /* Select logical device A (runtime registers) */
+ sch311x_sio_outb(sio_config_port, 0x07, 0x0a);
+
+ /* Check if Logical Device Register is currently active */
+ if ((sch311x_sio_inb(sio_config_port, 0x30) & 0x01) == 0)
+ pr_info("Seems that LDN 0x0a is not active...\n");
+
+ /* Get the base address of the runtime registers */
+ base_addr = (sch311x_sio_inb(sio_config_port, 0x60) << 8) |
+ sch311x_sio_inb(sio_config_port, 0x61);
+ if (!base_addr) {
+ pr_err("Base address not set\n");
+ err = -ENODEV;
+ goto exit;
+ }
+ *addr = base_addr;
+
+ pr_info("Found an SMSC SCH311%d chip at 0x%04x\n", dev_id, base_addr);
+
+exit:
+ sch311x_sio_exit(sio_config_port);
+ return err;
+}
+
+static int __init sch311x_gpio_pdev_add(const unsigned short addr)
+{
+ struct sch311x_pdev_data pdata;
+ int err;
+
+ pdata.runtime_reg = addr;
+
+ sch311x_gpio_pdev = platform_device_alloc(DRV_NAME, -1);
+ if (!sch311x_gpio_pdev)
+ return -ENOMEM;
+
+ err = platform_device_add_data(sch311x_gpio_pdev,
+ &pdata, sizeof(pdata));
+ if (err) {
+ pr_err(DRV_NAME "Platform data allocation failed\n");
+ goto err;
+ }
+
+ err = platform_device_add(sch311x_gpio_pdev);
+ if (err) {
+ pr_err(DRV_NAME "Device addition failed\n");
+ goto err;
+ }
+ return 0;
+
+err:
+ platform_device_put(sch311x_gpio_pdev);
+ return err;
+}
+
+static int __init sch311x_gpio_init(void)
+{
+ int err, i;
+ unsigned short addr = 0;
+
+ for (i = 0; i < ARRAY_SIZE(sch311x_ioports); i++)
+ if (sch311x_detect(sch311x_ioports[i], &addr) == 0)
+ break;
+
+ if (!addr)
+ return -ENODEV;
+
+ err = platform_driver_register(&sch311x_gpio_driver);
+ if (err)
+ return err;
+
+ err = sch311x_gpio_pdev_add(addr);
+ if (err)
+ goto unreg_platform_driver;
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&sch311x_gpio_driver);
+ return err;
+}
+
+static void __exit sch311x_gpio_exit(void)
+{
+ platform_device_unregister(sch311x_gpio_pdev);
+ platform_driver_unregister(&sch311x_gpio_driver);
+}
+
+module_init(sch311x_gpio_init);
+module_exit(sch311x_gpio_exit);
+
+MODULE_AUTHOR("Bruno Randolf <br1@einfach.org>");
+MODULE_DESCRIPTION("SMSC SCH311x GPIO Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-sch311x");
diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c
new file mode 100644
index 000000000..d8da36cd8
--- /dev/null
+++ b/drivers/gpio/gpio-sodaville.c
@@ -0,0 +1,291 @@
+/*
+ * GPIO interface for Intel Sodaville SoCs.
+ *
+ * Copyright (c) 2010, 2011 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define DRV_NAME "sdv_gpio"
+#define SDV_NUM_PUB_GPIOS 12
+#define PCI_DEVICE_ID_SDV_GPIO 0x2e67
+#define GPIO_BAR 0
+
+#define GPOUTR 0x00
+#define GPOER 0x04
+#define GPINR 0x08
+
+#define GPSTR 0x0c
+#define GPIT1R0 0x10
+#define GPIO_INT 0x14
+#define GPIT1R1 0x18
+
+#define GPMUXCTL 0x1c
+
+struct sdv_gpio_chip_data {
+ int irq_base;
+ void __iomem *gpio_pub_base;
+ struct irq_domain *id;
+ struct irq_chip_generic *gc;
+ struct bgpio_chip bgpio;
+};
+
+static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct sdv_gpio_chip_data *sd = gc->private;
+ void __iomem *type_reg;
+ u32 reg;
+
+ if (d->hwirq < 8)
+ type_reg = sd->gpio_pub_base + GPIT1R0;
+ else
+ type_reg = sd->gpio_pub_base + GPIT1R1;
+
+ reg = readl(type_reg);
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ reg &= ~BIT(4 * (d->hwirq % 8));
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ reg |= BIT(4 * (d->hwirq % 8));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ writel(reg, type_reg);
+ return 0;
+}
+
+static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data)
+{
+ struct sdv_gpio_chip_data *sd = data;
+ u32 irq_stat = readl(sd->gpio_pub_base + GPSTR);
+
+ irq_stat &= readl(sd->gpio_pub_base + GPIO_INT);
+ if (!irq_stat)
+ return IRQ_NONE;
+
+ while (irq_stat) {
+ u32 irq_bit = __fls(irq_stat);
+
+ irq_stat &= ~BIT(irq_bit);
+ generic_handle_irq(irq_find_mapping(sd->id, irq_bit));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int sdv_xlate(struct irq_domain *h, struct device_node *node,
+ const u32 *intspec, u32 intsize, irq_hw_number_t *out_hwirq,
+ u32 *out_type)
+{
+ u32 line, type;
+
+ if (node != h->of_node)
+ return -EINVAL;
+
+ if (intsize < 2)
+ return -EINVAL;
+
+ line = *intspec;
+ *out_hwirq = line;
+
+ intspec++;
+ type = *intspec;
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_LOW:
+ case IRQ_TYPE_LEVEL_HIGH:
+ *out_type = type;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct irq_domain_ops irq_domain_sdv_ops = {
+ .xlate = sdv_xlate,
+};
+
+static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,
+ struct pci_dev *pdev)
+{
+ struct irq_chip_type *ct;
+ int ret;
+
+ sd->irq_base = irq_alloc_descs(-1, 0, SDV_NUM_PUB_GPIOS, -1);
+ if (sd->irq_base < 0)
+ return sd->irq_base;
+
+ /* mask + ACK all interrupt sources */
+ writel(0, sd->gpio_pub_base + GPIO_INT);
+ writel((1 << 11) - 1, sd->gpio_pub_base + GPSTR);
+
+ ret = request_irq(pdev->irq, sdv_gpio_pub_irq_handler, IRQF_SHARED,
+ "sdv_gpio", sd);
+ if (ret)
+ goto out_free_desc;
+
+ /*
+ * This gpio irq controller latches level irqs. Testing shows that if
+ * we unmask & ACK the IRQ before the source of the interrupt is gone
+ * then the interrupt is active again.
+ */
+ sd->gc = irq_alloc_generic_chip("sdv-gpio", 1, sd->irq_base,
+ sd->gpio_pub_base, handle_fasteoi_irq);
+ if (!sd->gc) {
+ ret = -ENOMEM;
+ goto out_free_irq;
+ }
+
+ sd->gc->private = sd;
+ ct = sd->gc->chip_types;
+ ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
+ ct->regs.eoi = GPSTR;
+ ct->regs.mask = GPIO_INT;
+ ct->chip.irq_mask = irq_gc_mask_clr_bit;
+ ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->chip.irq_eoi = irq_gc_eoi;
+ ct->chip.irq_set_type = sdv_gpio_pub_set_type;
+
+ irq_setup_generic_chip(sd->gc, IRQ_MSK(SDV_NUM_PUB_GPIOS),
+ IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST,
+ IRQ_LEVEL | IRQ_NOPROBE);
+
+ sd->id = irq_domain_add_legacy(pdev->dev.of_node, SDV_NUM_PUB_GPIOS,
+ sd->irq_base, 0, &irq_domain_sdv_ops, sd);
+ if (!sd->id) {
+ ret = -ENODEV;
+ goto out_free_irq;
+ }
+ return 0;
+out_free_irq:
+ free_irq(pdev->irq, sd);
+out_free_desc:
+ irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS);
+ return ret;
+}
+
+static int sdv_gpio_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ struct sdv_gpio_chip_data *sd;
+ unsigned long addr;
+ const void *prop;
+ int len;
+ int ret;
+ u32 mux_val;
+
+ sd = kzalloc(sizeof(struct sdv_gpio_chip_data), GFP_KERNEL);
+ if (!sd)
+ return -ENOMEM;
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't enable device.\n");
+ goto done;
+ }
+
+ ret = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
+ if (ret) {
+ dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
+ goto disable_pci;
+ }
+
+ addr = pci_resource_start(pdev, GPIO_BAR);
+ if (!addr) {
+ ret = -ENODEV;
+ goto release_reg;
+ }
+ sd->gpio_pub_base = ioremap(addr, pci_resource_len(pdev, GPIO_BAR));
+
+ prop = of_get_property(pdev->dev.of_node, "intel,muxctl", &len);
+ if (prop && len == 4) {
+ mux_val = of_read_number(prop, 1);
+ writel(mux_val, sd->gpio_pub_base + GPMUXCTL);
+ }
+
+ ret = bgpio_init(&sd->bgpio, &pdev->dev, 4,
+ sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR,
+ NULL, sd->gpio_pub_base + GPOER, NULL, 0);
+ if (ret)
+ goto unmap;
+ sd->bgpio.gc.ngpio = SDV_NUM_PUB_GPIOS;
+
+ ret = gpiochip_add(&sd->bgpio.gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpiochip_add() failed.\n");
+ goto unmap;
+ }
+
+ ret = sdv_register_irqsupport(sd, pdev);
+ if (ret)
+ goto unmap;
+
+ pci_set_drvdata(pdev, sd);
+ dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n");
+ return 0;
+
+unmap:
+ iounmap(sd->gpio_pub_base);
+release_reg:
+ pci_release_region(pdev, GPIO_BAR);
+disable_pci:
+ pci_disable_device(pdev);
+done:
+ kfree(sd);
+ return ret;
+}
+
+static void sdv_gpio_remove(struct pci_dev *pdev)
+{
+ struct sdv_gpio_chip_data *sd = pci_get_drvdata(pdev);
+
+ free_irq(pdev->irq, sd);
+ irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS);
+
+ gpiochip_remove(&sd->bgpio.gc);
+ pci_release_region(pdev, GPIO_BAR);
+ iounmap(sd->gpio_pub_base);
+ pci_disable_device(pdev);
+ kfree(sd);
+}
+
+static const struct pci_device_id sdv_gpio_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDV_GPIO) },
+ { 0, },
+};
+
+static struct pci_driver sdv_gpio_driver = {
+ .name = DRV_NAME,
+ .id_table = sdv_gpio_pci_ids,
+ .probe = sdv_gpio_probe,
+ .remove = sdv_gpio_remove,
+};
+
+module_pci_driver(sdv_gpio_driver);
+
+MODULE_AUTHOR("Hans J. Koch <hjk@linutronix.de>");
+MODULE_DESCRIPTION("GPIO interface for Intel Sodaville SoCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c
new file mode 100644
index 000000000..69ffca5b0
--- /dev/null
+++ b/drivers/gpio/gpio-spear-spics.c
@@ -0,0 +1,207 @@
+/*
+ * SPEAr platform SPI chipselect abstraction over gpiolib
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Shiraz Hashim <shiraz.linux.kernel@gmail.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/err.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+/* maximum chipselects */
+#define NUM_OF_GPIO 4
+
+/*
+ * Provision is available on some SPEAr SoCs to control ARM PL022 spi cs
+ * through system registers. This register lies outside spi (pl022)
+ * address space into system registers.
+ *
+ * It provides control for spi chip select lines so that any chipselect
+ * (out of 4 possible chipselects in pl022) can be made low to select
+ * the particular slave.
+ */
+
+/**
+ * struct spear_spics - represents spi chip select control
+ * @base: base address
+ * @perip_cfg: configuration register
+ * @sw_enable_bit: bit to enable s/w control over chipselects
+ * @cs_value_bit: bit to program high or low chipselect
+ * @cs_enable_mask: mask to select bits required to select chipselect
+ * @cs_enable_shift: bit pos of cs_enable_mask
+ * @use_count: use count of a spi controller cs lines
+ * @last_off: stores last offset caller of set_value()
+ * @chip: gpio_chip abstraction
+ */
+struct spear_spics {
+ void __iomem *base;
+ u32 perip_cfg;
+ u32 sw_enable_bit;
+ u32 cs_value_bit;
+ u32 cs_enable_mask;
+ u32 cs_enable_shift;
+ unsigned long use_count;
+ int last_off;
+ struct gpio_chip chip;
+};
+
+/* gpio framework specific routines */
+static int spics_get_value(struct gpio_chip *chip, unsigned offset)
+{
+ return -ENXIO;
+}
+
+static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct spear_spics *spics = container_of(chip, struct spear_spics,
+ chip);
+ u32 tmp;
+
+ /* select chip select from register */
+ tmp = readl_relaxed(spics->base + spics->perip_cfg);
+ if (spics->last_off != offset) {
+ spics->last_off = offset;
+ tmp &= ~(spics->cs_enable_mask << spics->cs_enable_shift);
+ tmp |= offset << spics->cs_enable_shift;
+ }
+
+ /* toggle chip select line */
+ tmp &= ~(0x1 << spics->cs_value_bit);
+ tmp |= value << spics->cs_value_bit;
+ writel_relaxed(tmp, spics->base + spics->perip_cfg);
+}
+
+static int spics_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ return -ENXIO;
+}
+
+static int spics_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ spics_set_value(chip, offset, value);
+ return 0;
+}
+
+static int spics_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct spear_spics *spics = container_of(chip, struct spear_spics,
+ chip);
+ u32 tmp;
+
+ if (!spics->use_count++) {
+ tmp = readl_relaxed(spics->base + spics->perip_cfg);
+ tmp |= 0x1 << spics->sw_enable_bit;
+ tmp |= 0x1 << spics->cs_value_bit;
+ writel_relaxed(tmp, spics->base + spics->perip_cfg);
+ }
+
+ return 0;
+}
+
+static void spics_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct spear_spics *spics = container_of(chip, struct spear_spics,
+ chip);
+ u32 tmp;
+
+ if (!--spics->use_count) {
+ tmp = readl_relaxed(spics->base + spics->perip_cfg);
+ tmp &= ~(0x1 << spics->sw_enable_bit);
+ writel_relaxed(tmp, spics->base + spics->perip_cfg);
+ }
+}
+
+static int spics_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct spear_spics *spics;
+ struct resource *res;
+ int ret;
+
+ spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL);
+ if (!spics)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ spics->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(spics->base))
+ return PTR_ERR(spics->base);
+
+ if (of_property_read_u32(np, "st-spics,peripcfg-reg",
+ &spics->perip_cfg))
+ goto err_dt_data;
+ if (of_property_read_u32(np, "st-spics,sw-enable-bit",
+ &spics->sw_enable_bit))
+ goto err_dt_data;
+ if (of_property_read_u32(np, "st-spics,cs-value-bit",
+ &spics->cs_value_bit))
+ goto err_dt_data;
+ if (of_property_read_u32(np, "st-spics,cs-enable-mask",
+ &spics->cs_enable_mask))
+ goto err_dt_data;
+ if (of_property_read_u32(np, "st-spics,cs-enable-shift",
+ &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;
+ spics->chip.free = spics_free;
+ spics->chip.direction_input = spics_direction_input;
+ spics->chip.direction_output = spics_direction_output;
+ spics->chip.get = spics_get_value;
+ spics->chip.set = spics_set_value;
+ spics->chip.label = dev_name(&pdev->dev);
+ spics->chip.dev = &pdev->dev;
+ spics->chip.owner = THIS_MODULE;
+ spics->last_off = -1;
+
+ ret = gpiochip_add(&spics->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpio chip\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "spear spics registered\n");
+ return 0;
+
+err_dt_data:
+ dev_err(&pdev->dev, "DT probe failed\n");
+ return -EINVAL;
+}
+
+static const struct of_device_id spics_gpio_of_match[] = {
+ { .compatible = "st,spear-spics-gpio" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, spics_gpio_of_match);
+
+static struct platform_driver spics_gpio_driver = {
+ .probe = spics_gpio_probe,
+ .driver = {
+ .name = "spear-spics-gpio",
+ .of_match_table = spics_gpio_of_match,
+ },
+};
+
+static int __init spics_gpio_init(void)
+{
+ return platform_driver_register(&spics_gpio_driver);
+}
+subsys_initcall(spics_gpio_init);
+
+MODULE_AUTHOR("Shiraz Hashim <shiraz.linux.kernel@gmail.com>");
+MODULE_DESCRIPTION("STMicroelectronics SPEAr SPI Chip Select Abstraction");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c
new file mode 100644
index 000000000..18579ac65
--- /dev/null
+++ b/drivers/gpio/gpio-sta2x11.c
@@ -0,0 +1,439 @@
+/*
+ * STMicroelectronics ConneXt (STA2X11) GPIO driver
+ *
+ * Copyright 2012 ST Microelectronics (Alessandro Rubini)
+ * Based on gpio-ml-ioh.c, Copyright 2010 OKI Semiconductors Ltd.
+ * Also based on previous sta2x11 work, Copyright 2011 Wind River Systems, Inc.
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/sta2x11-mfd.h>
+
+struct gsta_regs {
+ u32 dat; /* 0x00 */
+ u32 dats;
+ u32 datc;
+ u32 pdis;
+ u32 dir; /* 0x10 */
+ u32 dirs;
+ u32 dirc;
+ u32 unused_1c;
+ u32 afsela; /* 0x20 */
+ u32 unused_24[7];
+ u32 rimsc; /* 0x40 */
+ u32 fimsc;
+ u32 is;
+ u32 ic;
+};
+
+struct gsta_gpio {
+ spinlock_t lock;
+ struct device *dev;
+ void __iomem *reg_base;
+ struct gsta_regs __iomem *regs[GSTA_NR_BLOCKS];
+ struct gpio_chip gpio;
+ int irq_base;
+ /* FIXME: save the whole config here (AF, ...) */
+ unsigned irq_type[GSTA_NR_GPIO];
+};
+
+static inline struct gsta_regs __iomem *__regs(struct gsta_gpio *chip, int nr)
+{
+ return chip->regs[nr / GSTA_GPIO_PER_BLOCK];
+}
+
+static inline u32 __bit(int nr)
+{
+ return 1U << (nr % GSTA_GPIO_PER_BLOCK);
+}
+
+/*
+ * gpio methods
+ */
+
+static void gsta_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
+{
+ struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+ struct gsta_regs __iomem *regs = __regs(chip, nr);
+ u32 bit = __bit(nr);
+
+ if (val)
+ writel(bit, &regs->dats);
+ else
+ writel(bit, &regs->datc);
+}
+
+static int gsta_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+ struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+ struct gsta_regs __iomem *regs = __regs(chip, nr);
+ u32 bit = __bit(nr);
+
+ return readl(&regs->dat) & bit;
+}
+
+static int gsta_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+ int val)
+{
+ struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+ struct gsta_regs __iomem *regs = __regs(chip, nr);
+ u32 bit = __bit(nr);
+
+ writel(bit, &regs->dirs);
+ /* Data register after direction, otherwise pullup/down is selected */
+ if (val)
+ writel(bit, &regs->dats);
+ else
+ writel(bit, &regs->datc);
+ return 0;
+}
+
+static int gsta_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+ struct gsta_regs __iomem *regs = __regs(chip, nr);
+ u32 bit = __bit(nr);
+
+ writel(bit, &regs->dirc);
+ return 0;
+}
+
+static int gsta_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+ return chip->irq_base + offset;
+}
+
+static void gsta_gpio_setup(struct gsta_gpio *chip) /* called from probe */
+{
+ struct gpio_chip *gpio = &chip->gpio;
+
+ /*
+ * ARCH_NR_GPIOS is currently 256 and dynamic allocation starts
+ * from the end. However, for compatibility, we need the first
+ * ConneXt device to start from gpio 0: it's the main chipset
+ * on most boards so documents and drivers assume gpio0..gpio127
+ */
+ static int gpio_base;
+
+ gpio->label = dev_name(chip->dev);
+ gpio->owner = THIS_MODULE;
+ gpio->direction_input = gsta_gpio_direction_input;
+ gpio->get = gsta_gpio_get;
+ gpio->direction_output = gsta_gpio_direction_output;
+ gpio->set = gsta_gpio_set;
+ gpio->dbg_show = NULL;
+ gpio->base = gpio_base;
+ gpio->ngpio = GSTA_NR_GPIO;
+ gpio->can_sleep = false;
+ gpio->to_irq = gsta_gpio_to_irq;
+
+ /*
+ * After the first device, turn to dynamic gpio numbers.
+ * For example, with ARCH_NR_GPIOS = 256 we can fit two cards
+ */
+ if (!gpio_base)
+ gpio_base = -1;
+}
+
+/*
+ * Special method: alternate functions and pullup/pulldown. This is only
+ * invoked on startup to configure gpio's according to platform data.
+ * FIXME : this functionality shall be managed (and exported to other drivers)
+ * via the pin control subsystem.
+ */
+static void gsta_set_config(struct gsta_gpio *chip, int nr, unsigned cfg)
+{
+ struct gsta_regs __iomem *regs = __regs(chip, nr);
+ unsigned long flags;
+ u32 bit = __bit(nr);
+ u32 val;
+ int err = 0;
+
+ pr_info("%s: %p %i %i\n", __func__, chip, nr, cfg);
+
+ if (cfg == PINMUX_TYPE_NONE)
+ return;
+
+ /* Alternate function or not? */
+ spin_lock_irqsave(&chip->lock, flags);
+ val = readl(&regs->afsela);
+ if (cfg == PINMUX_TYPE_FUNCTION)
+ val |= bit;
+ else
+ val &= ~bit;
+ writel(val | bit, &regs->afsela);
+ if (cfg == PINMUX_TYPE_FUNCTION) {
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return;
+ }
+
+ /* not alternate function: set details */
+ switch (cfg) {
+ case PINMUX_TYPE_OUTPUT_LOW:
+ writel(bit, &regs->dirs);
+ writel(bit, &regs->datc);
+ break;
+ case PINMUX_TYPE_OUTPUT_HIGH:
+ writel(bit, &regs->dirs);
+ writel(bit, &regs->dats);
+ break;
+ case PINMUX_TYPE_INPUT:
+ writel(bit, &regs->dirc);
+ val = readl(&regs->pdis) | bit;
+ writel(val, &regs->pdis);
+ break;
+ case PINMUX_TYPE_INPUT_PULLUP:
+ writel(bit, &regs->dirc);
+ val = readl(&regs->pdis) & ~bit;
+ writel(val, &regs->pdis);
+ writel(bit, &regs->dats);
+ break;
+ case PINMUX_TYPE_INPUT_PULLDOWN:
+ writel(bit, &regs->dirc);
+ val = readl(&regs->pdis) & ~bit;
+ writel(val, &regs->pdis);
+ writel(bit, &regs->datc);
+ break;
+ default:
+ err = 1;
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+ if (err)
+ pr_err("%s: chip %p, pin %i, cfg %i is invalid\n",
+ __func__, chip, nr, cfg);
+}
+
+/*
+ * Irq methods
+ */
+
+static void gsta_irq_disable(struct irq_data *data)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ struct gsta_gpio *chip = gc->private;
+ int nr = data->irq - chip->irq_base;
+ struct gsta_regs __iomem *regs = __regs(chip, nr);
+ u32 bit = __bit(nr);
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ if (chip->irq_type[nr] & IRQ_TYPE_EDGE_RISING) {
+ val = readl(&regs->rimsc) & ~bit;
+ writel(val, &regs->rimsc);
+ }
+ if (chip->irq_type[nr] & IRQ_TYPE_EDGE_FALLING) {
+ val = readl(&regs->fimsc) & ~bit;
+ writel(val, &regs->fimsc);
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return;
+}
+
+static void gsta_irq_enable(struct irq_data *data)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ struct gsta_gpio *chip = gc->private;
+ int nr = data->irq - chip->irq_base;
+ struct gsta_regs __iomem *regs = __regs(chip, nr);
+ u32 bit = __bit(nr);
+ u32 val;
+ int type;
+ unsigned long flags;
+
+ type = chip->irq_type[nr];
+
+ spin_lock_irqsave(&chip->lock, flags);
+ val = readl(&regs->rimsc);
+ if (type & IRQ_TYPE_EDGE_RISING)
+ writel(val | bit, &regs->rimsc);
+ else
+ writel(val & ~bit, &regs->rimsc);
+ val = readl(&regs->rimsc);
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ writel(val | bit, &regs->fimsc);
+ else
+ writel(val & ~bit, &regs->fimsc);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return;
+}
+
+static int gsta_irq_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct gsta_gpio *chip = gc->private;
+ int nr = d->irq - chip->irq_base;
+
+ /* We only support edge interrupts */
+ if (!(type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))) {
+ pr_debug("%s: unsupported type 0x%x\n", __func__, type);
+ return -EINVAL;
+ }
+
+ chip->irq_type[nr] = type; /* used for enable/disable */
+
+ gsta_irq_enable(d);
+ return 0;
+}
+
+static irqreturn_t gsta_gpio_handler(int irq, void *dev_id)
+{
+ struct gsta_gpio *chip = dev_id;
+ struct gsta_regs __iomem *regs;
+ u32 is;
+ int i, nr, base;
+ irqreturn_t ret = IRQ_NONE;
+
+ for (i = 0; i < GSTA_NR_BLOCKS; i++) {
+ regs = chip->regs[i];
+ base = chip->irq_base + i * GSTA_GPIO_PER_BLOCK;
+ while ((is = readl(&regs->is))) {
+ nr = __ffs(is);
+ irq = base + nr;
+ generic_handle_irq(irq);
+ writel(1 << nr, &regs->ic);
+ ret = IRQ_HANDLED;
+ }
+ }
+ return ret;
+}
+
+static void gsta_alloc_irq_chip(struct gsta_gpio *chip)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base,
+ chip->reg_base, handle_simple_irq);
+ gc->private = chip;
+ ct = gc->chip_types;
+
+ ct->chip.irq_set_type = gsta_irq_type;
+ ct->chip.irq_disable = gsta_irq_disable;
+ ct->chip.irq_enable = gsta_irq_enable;
+
+ /* FIXME: this makes at most 32 interrupts. Request 0 by now */
+ irq_setup_generic_chip(gc, 0 /* IRQ_MSK(GSTA_GPIO_PER_BLOCK) */, 0,
+ IRQ_NOREQUEST | IRQ_NOPROBE, 0);
+
+ /* Set up all all 128 interrupts: code from setup_generic_chip */
+ {
+ struct irq_chip_type *ct = gc->chip_types;
+ int i, j;
+ for (j = 0; j < GSTA_NR_GPIO; j++) {
+ i = chip->irq_base + j;
+ irq_set_chip_and_handler(i, &ct->chip, ct->handler);
+ irq_set_chip_data(i, gc);
+ irq_modify_status(i, IRQ_NOREQUEST | IRQ_NOPROBE, 0);
+ }
+ gc->irq_cnt = i - gc->irq_base;
+ }
+}
+
+/* The platform device used here is instantiated by the MFD device */
+static int gsta_probe(struct platform_device *dev)
+{
+ int i, err;
+ struct pci_dev *pdev;
+ struct sta2x11_gpio_pdata *gpio_pdata;
+ struct gsta_gpio *chip;
+ struct resource *res;
+
+ pdev = *(struct pci_dev **)dev_get_platdata(&dev->dev);
+ gpio_pdata = dev_get_platdata(&pdev->dev);
+
+ if (gpio_pdata == NULL)
+ dev_err(&dev->dev, "no gpio config\n");
+ pr_debug("gpio config: %p\n", gpio_pdata);
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+
+ chip = devm_kzalloc(&dev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ chip->dev = &dev->dev;
+ chip->reg_base = devm_ioremap_resource(&dev->dev, res);
+ if (IS_ERR(chip->reg_base))
+ return PTR_ERR(chip->reg_base);
+
+ for (i = 0; i < GSTA_NR_BLOCKS; i++) {
+ chip->regs[i] = chip->reg_base + i * 4096;
+ /* disable all irqs */
+ writel(0, &chip->regs[i]->rimsc);
+ writel(0, &chip->regs[i]->fimsc);
+ writel(~0, &chip->regs[i]->ic);
+ }
+ spin_lock_init(&chip->lock);
+ gsta_gpio_setup(chip);
+ if (gpio_pdata)
+ for (i = 0; i < GSTA_NR_GPIO; i++)
+ gsta_set_config(chip, i, gpio_pdata->pinconfig[i]);
+
+ /* 384 was used in previous code: be compatible for other drivers */
+ err = irq_alloc_descs(-1, 384, GSTA_NR_GPIO, NUMA_NO_NODE);
+ if (err < 0) {
+ dev_warn(&dev->dev, "sta2x11 gpio: Can't get irq base (%i)\n",
+ -err);
+ return err;
+ }
+ chip->irq_base = err;
+ gsta_alloc_irq_chip(chip);
+
+ err = request_irq(pdev->irq, gsta_gpio_handler,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err < 0) {
+ dev_err(&dev->dev, "sta2x11 gpio: Can't request irq (%i)\n",
+ -err);
+ goto err_free_descs;
+ }
+
+ err = gpiochip_add(&chip->gpio);
+ if (err < 0) {
+ dev_err(&dev->dev, "sta2x11 gpio: Can't register (%i)\n",
+ -err);
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(dev, chip);
+ return 0;
+
+err_free_irq:
+ free_irq(pdev->irq, chip);
+err_free_descs:
+ irq_free_descs(chip->irq_base, GSTA_NR_GPIO);
+ return err;
+}
+
+static struct platform_driver sta2x11_gpio_platform_driver = {
+ .driver = {
+ .name = "sta2x11-gpio",
+ },
+ .probe = gsta_probe,
+};
+
+module_platform_driver(sta2x11_gpio_platform_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("sta2x11_gpio GPIO driver");
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
new file mode 100644
index 000000000..dabfb99dd
--- /dev/null
+++ b/drivers/gpio/gpio-stmpe.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/seq_file.h>
+
+/*
+ * These registers are modified under the irq bus lock and cached to avoid
+ * unnecessary writes in bus_sync_unlock.
+ */
+enum { REG_RE, REG_FE, REG_IE };
+
+#define CACHE_NR_REGS 3
+/* No variant has more than 24 GPIOs */
+#define CACHE_NR_BANKS (24 / 8)
+
+struct stmpe_gpio {
+ struct gpio_chip chip;
+ struct stmpe *stmpe;
+ struct device *dev;
+ struct mutex irq_lock;
+ u32 norequest_mask;
+ /* Caches of interrupt control registers for bus_lock */
+ u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
+ u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
+};
+
+static inline struct stmpe_gpio *to_stmpe_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct stmpe_gpio, chip);
+}
+
+static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+ int ret;
+
+ ret = stmpe_reg_read(stmpe, reg);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & mask);
+}
+
+static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB;
+ u8 reg = stmpe->regs[which] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ /*
+ * Some variants have single register for gpio set/clear functionality.
+ * For them we need to write 0 to clear and 1 to set.
+ */
+ if (stmpe->regs[STMPE_IDX_GPSR_LSB] == stmpe->regs[STMPE_IDX_GPCR_LSB])
+ stmpe_set_bits(stmpe, reg, mask, val ? mask : 0);
+ else
+ stmpe_reg_write(stmpe, reg, mask);
+}
+
+static int stmpe_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int val)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ stmpe_gpio_set(chip, offset, val);
+
+ return stmpe_set_bits(stmpe, reg, mask, mask);
+}
+
+static int stmpe_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ return stmpe_set_bits(stmpe, reg, mask, 0);
+}
+
+static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+
+ if (stmpe_gpio->norequest_mask & (1 << offset))
+ return -EINVAL;
+
+ return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "stmpe",
+ .owner = THIS_MODULE,
+ .direction_input = stmpe_gpio_direction_input,
+ .get = stmpe_gpio_get,
+ .direction_output = stmpe_gpio_direction_output,
+ .set = stmpe_gpio_set,
+ .request = stmpe_gpio_request,
+ .can_sleep = true,
+};
+
+static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(gc);
+ int offset = d->hwirq;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH)
+ return -EINVAL;
+
+ /* STMPE801 doesn't have RE and FE registers */
+ if (stmpe_gpio->stmpe->partnum == STMPE801)
+ return 0;
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ stmpe_gpio->regs[REG_RE][regoffset] |= mask;
+ else
+ stmpe_gpio->regs[REG_RE][regoffset] &= ~mask;
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ stmpe_gpio->regs[REG_FE][regoffset] |= mask;
+ else
+ stmpe_gpio->regs[REG_FE][regoffset] &= ~mask;
+
+ return 0;
+}
+
+static void stmpe_gpio_irq_lock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(gc);
+
+ mutex_lock(&stmpe_gpio->irq_lock);
+}
+
+static void stmpe_gpio_irq_sync_unlock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(gc);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ static const u8 regmap[] = {
+ [REG_RE] = STMPE_IDX_GPRER_LSB,
+ [REG_FE] = STMPE_IDX_GPFER_LSB,
+ [REG_IE] = STMPE_IDX_IEGPIOR_LSB,
+ };
+ int i, j;
+
+ for (i = 0; i < CACHE_NR_REGS; i++) {
+ /* STMPE801 doesn't have RE and FE registers */
+ if ((stmpe->partnum == STMPE801) &&
+ (i != REG_IE))
+ continue;
+
+ for (j = 0; j < num_banks; j++) {
+ u8 old = stmpe_gpio->oldregs[i][j];
+ u8 new = stmpe_gpio->regs[i][j];
+
+ if (new == old)
+ continue;
+
+ stmpe_gpio->oldregs[i][j] = new;
+ stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new);
+ }
+ }
+
+ mutex_unlock(&stmpe_gpio->irq_lock);
+}
+
+static void stmpe_gpio_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(gc);
+ int offset = d->hwirq;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe_gpio->regs[REG_IE][regoffset] &= ~mask;
+}
+
+static void stmpe_gpio_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(gc);
+ int offset = d->hwirq;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe_gpio->regs[REG_IE][regoffset] |= mask;
+}
+
+static void stmpe_dbg_show_one(struct seq_file *s,
+ struct gpio_chip *gc,
+ unsigned offset, unsigned gpio)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(gc);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ const char *label = gpiochip_is_requested(gc, offset);
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ bool val = !!stmpe_gpio_get(gc, offset);
+ u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+ int ret;
+ u8 dir;
+
+ ret = stmpe_reg_read(stmpe, dir_reg);
+ if (ret < 0)
+ return;
+ dir = !!(ret & mask);
+
+ if (dir) {
+ seq_printf(s, " gpio-%-3d (%-20.20s) out %s",
+ gpio, label ?: "(none)",
+ val ? "hi" : "lo");
+ } else {
+ u8 edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_MSB] + num_banks - 1 - (offset / 8);
+ u8 rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB] - (offset / 8);
+ u8 fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB] - (offset / 8);
+ u8 irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB] - (offset / 8);
+ bool edge_det;
+ bool rise;
+ bool fall;
+ bool irqen;
+
+ ret = stmpe_reg_read(stmpe, edge_det_reg);
+ if (ret < 0)
+ return;
+ edge_det = !!(ret & mask);
+ ret = stmpe_reg_read(stmpe, rise_reg);
+ if (ret < 0)
+ return;
+ rise = !!(ret & mask);
+ ret = stmpe_reg_read(stmpe, fall_reg);
+ if (ret < 0)
+ return;
+ fall = !!(ret & mask);
+ ret = stmpe_reg_read(stmpe, irqen_reg);
+ if (ret < 0)
+ return;
+ irqen = !!(ret & mask);
+
+ seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s %s%s%s",
+ gpio, label ?: "(none)",
+ val ? "hi" : "lo",
+ edge_det ? "edge-asserted" : "edge-inactive",
+ irqen ? "IRQ-enabled" : "",
+ rise ? " rising-edge-detection" : "",
+ fall ? " falling-edge-detection" : "");
+ }
+}
+
+static void stmpe_dbg_show(struct seq_file *s, struct gpio_chip *gc)
+{
+ unsigned i;
+ unsigned gpio = gc->base;
+
+ for (i = 0; i < gc->ngpio; i++, gpio++) {
+ stmpe_dbg_show_one(s, gc, i, gpio);
+ seq_printf(s, "\n");
+ }
+}
+
+static struct irq_chip stmpe_gpio_irq_chip = {
+ .name = "stmpe-gpio",
+ .irq_bus_lock = stmpe_gpio_irq_lock,
+ .irq_bus_sync_unlock = stmpe_gpio_irq_sync_unlock,
+ .irq_mask = stmpe_gpio_irq_mask,
+ .irq_unmask = stmpe_gpio_irq_unmask,
+ .irq_set_type = stmpe_gpio_irq_set_type,
+};
+
+static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
+{
+ struct stmpe_gpio *stmpe_gpio = dev;
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB];
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ u8 status[num_banks];
+ int ret;
+ int i;
+
+ ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < num_banks; i++) {
+ int bank = num_banks - i - 1;
+ unsigned int enabled = stmpe_gpio->regs[REG_IE][bank];
+ unsigned int stat = status[i];
+
+ stat &= enabled;
+ if (!stat)
+ continue;
+
+ while (stat) {
+ int bit = __ffs(stat);
+ int line = bank * 8 + bit;
+ int child_irq = irq_find_mapping(stmpe_gpio->chip.irqdomain,
+ line);
+
+ handle_nested_irq(child_irq);
+ stat &= ~(1 << bit);
+ }
+
+ stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
+
+ /* Edge detect register is not present on 801 */
+ if (stmpe->partnum != STMPE801)
+ stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB]
+ + i, status[i]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int stmpe_gpio_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct device_node *np = pdev->dev.of_node;
+ struct stmpe_gpio *stmpe_gpio;
+ int ret;
+ int irq = 0;
+
+ irq = platform_get_irq(pdev, 0);
+
+ stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL);
+ if (!stmpe_gpio)
+ return -ENOMEM;
+
+ mutex_init(&stmpe_gpio->irq_lock);
+
+ stmpe_gpio->dev = &pdev->dev;
+ stmpe_gpio->stmpe = stmpe;
+ stmpe_gpio->chip = template_chip;
+ stmpe_gpio->chip.ngpio = stmpe->num_gpios;
+ stmpe_gpio->chip.dev = &pdev->dev;
+ stmpe_gpio->chip.of_node = np;
+ stmpe_gpio->chip.base = -1;
+
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ stmpe_gpio->chip.dbg_show = stmpe_dbg_show;
+
+ of_property_read_u32(np, "st,norequest-mask",
+ &stmpe_gpio->norequest_mask);
+
+ if (irq < 0)
+ dev_info(&pdev->dev,
+ "device configured in no-irq mode: "
+ "irqs are not available\n");
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
+ if (ret)
+ goto out_free;
+
+ ret = gpiochip_add(&stmpe_gpio->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+ goto out_disable;
+ }
+
+ if (irq > 0) {
+ 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;
+ }
+ ret = gpiochip_irqchip_add(&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_chained_irqchip(&stmpe_gpio->chip,
+ &stmpe_gpio_irq_chip,
+ irq,
+ NULL);
+ }
+
+ 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;
+}
+
+static int stmpe_gpio_remove(struct platform_device *pdev)
+{
+ struct stmpe_gpio *stmpe_gpio = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+
+ gpiochip_remove(&stmpe_gpio->chip);
+ stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
+ kfree(stmpe_gpio);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_gpio_driver = {
+ .driver.name = "stmpe-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = stmpe_gpio_probe,
+ .remove = stmpe_gpio_remove,
+};
+
+static int __init stmpe_gpio_init(void)
+{
+ return platform_driver_register(&stmpe_gpio_driver);
+}
+subsys_initcall(stmpe_gpio_init);
+
+static void __exit stmpe_gpio_exit(void)
+{
+ platform_driver_unregister(&stmpe_gpio_driver);
+}
+module_exit(stmpe_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx GPIO driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
new file mode 100644
index 000000000..202361eb7
--- /dev/null
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/of_platform.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <lantiq_soc.h>
+
+/*
+ * The Serial To Parallel (STP) is found on MIPS based Lantiq socs. It is a
+ * peripheral controller used to drive external shift register cascades. At most
+ * 3 groups of 8 bits can be driven. The hardware is able to allow the DSL modem
+ * to drive the 2 LSBs of the cascade automatically.
+ */
+
+/* control register 0 */
+#define XWAY_STP_CON0 0x00
+/* control register 1 */
+#define XWAY_STP_CON1 0x04
+/* data register 0 */
+#define XWAY_STP_CPU0 0x08
+/* data register 1 */
+#define XWAY_STP_CPU1 0x0C
+/* access register */
+#define XWAY_STP_AR 0x10
+
+/* software or hardware update select bit */
+#define XWAY_STP_CON_SWU BIT(31)
+
+/* automatic update rates */
+#define XWAY_STP_2HZ 0
+#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)
+
+/* clock source for automatic update */
+#define XWAY_STP_UPD_FPI BIT(31)
+#define XWAY_STP_UPD_MASK (BIT(31) | BIT(30))
+
+/* let the adsl core drive the 2 LSBs */
+#define XWAY_STP_ADSL_SHIFT 24
+#define XWAY_STP_ADSL_MASK 0x3
+
+/* 2 groups of 3 bits can be driven by the phys */
+#define XWAY_STP_PHY_MASK 0x3
+#define XWAY_STP_PHY1_SHIFT 27
+#define XWAY_STP_PHY2_SHIFT 15
+
+/* STP has 3 groups of 8 bits */
+#define XWAY_STP_GROUP0 BIT(0)
+#define XWAY_STP_GROUP1 BIT(1)
+#define XWAY_STP_GROUP2 BIT(2)
+#define XWAY_STP_GROUP_MASK (0x7)
+
+/* Edge configuration bits */
+#define XWAY_STP_FALLING BIT(26)
+#define XWAY_STP_EDGE_MASK BIT(26)
+
+#define xway_stp_r32(m, reg) __raw_readl(m + reg)
+#define xway_stp_w32(m, val, reg) __raw_writel(val, m + reg)
+#define xway_stp_w32_mask(m, clear, set, reg) \
+ ltq_w32((ltq_r32(m + reg) & ~(clear)) | (set), \
+ m + reg)
+
+struct xway_stp {
+ struct gpio_chip gc;
+ void __iomem *virt;
+ u32 edge; /* rising or falling edge triggered shift register */
+ u32 shadow; /* shadow the shift registers state */
+ u8 groups; /* we can drive 1-3 groups of 8bit each */
+ 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 reserved; /* mask out the hw driven bits in gpio_request */
+};
+
+/**
+ * xway_stp_set() - gpio_chip->set - set gpios.
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ * @val: Value to be written to specified signal.
+ *
+ * Set the shadow value and call ltq_ebu_apply.
+ */
+static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val)
+{
+ struct xway_stp *chip =
+ container_of(gc, struct xway_stp, gc);
+
+ if (val)
+ chip->shadow |= BIT(gpio);
+ 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);
+}
+
+/**
+ * xway_stp_dir_out() - gpio_chip->dir_out - set gpio direction.
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ * @val: Value to be written to specified signal.
+ *
+ * Same as xway_stp_set, always returns 0.
+ */
+static int xway_stp_dir_out(struct gpio_chip *gc, unsigned gpio, int val)
+{
+ xway_stp_set(gc, gpio, val);
+
+ return 0;
+}
+
+/**
+ * xway_stp_request() - gpio_chip->request
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ *
+ * We mask out the HW driven pins
+ */
+static int xway_stp_request(struct gpio_chip *gc, unsigned gpio)
+{
+ struct xway_stp *chip =
+ container_of(gc, struct xway_stp, gc);
+
+ if ((gpio < 8) && (chip->reserved & BIT(gpio))) {
+ dev_err(gc->dev, "GPIO %d is driven by hardware\n", gpio);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * xway_stp_hw_init() - Configure the STP unit and enable the clock gate
+ * @virt: pointer to the remapped register range
+ */
+static int xway_stp_hw_init(struct xway_stp *chip)
+{
+ /* sane defaults */
+ xway_stp_w32(chip->virt, 0, XWAY_STP_AR);
+ xway_stp_w32(chip->virt, 0, XWAY_STP_CPU0);
+ xway_stp_w32(chip->virt, 0, XWAY_STP_CPU1);
+ xway_stp_w32(chip->virt, XWAY_STP_CON_SWU, XWAY_STP_CON0);
+ xway_stp_w32(chip->virt, 0, XWAY_STP_CON1);
+
+ /* apply edge trigger settings for the shift register */
+ xway_stp_w32_mask(chip->virt, XWAY_STP_EDGE_MASK,
+ chip->edge, XWAY_STP_CON0);
+
+ /* apply led group settings */
+ xway_stp_w32_mask(chip->virt, XWAY_STP_GROUP_MASK,
+ chip->groups, XWAY_STP_CON1);
+
+ /* tell the hardware which pins are controlled by the dsl modem */
+ xway_stp_w32_mask(chip->virt,
+ XWAY_STP_ADSL_MASK << XWAY_STP_ADSL_SHIFT,
+ chip->dsl << XWAY_STP_ADSL_SHIFT,
+ XWAY_STP_CON0);
+
+ /* tell the hardware which pins are controlled by the phys */
+ xway_stp_w32_mask(chip->virt,
+ XWAY_STP_PHY_MASK << XWAY_STP_PHY1_SHIFT,
+ chip->phy1 << XWAY_STP_PHY1_SHIFT,
+ XWAY_STP_CON0);
+ xway_stp_w32_mask(chip->virt,
+ XWAY_STP_PHY_MASK << XWAY_STP_PHY2_SHIFT,
+ chip->phy2 << XWAY_STP_PHY2_SHIFT,
+ XWAY_STP_CON1);
+
+ /* mask out the hw driven bits in gpio_request */
+ chip->reserved = (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)
+ xway_stp_w32_mask(chip->virt, XWAY_STP_UPD_MASK,
+ XWAY_STP_UPD_FPI, XWAY_STP_CON1);
+
+ return 0;
+}
+
+static int xway_stp_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ const __be32 *shadow, *groups, *dsl, *phy;
+ struct xway_stp *chip;
+ struct clk *clk;
+ int ret = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->virt = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(chip->virt))
+ return PTR_ERR(chip->virt);
+
+ chip->gc.dev = &pdev->dev;
+ chip->gc.label = "stp-xway";
+ chip->gc.direction_output = xway_stp_dir_out;
+ chip->gc.set = xway_stp_set;
+ chip->gc.request = xway_stp_request;
+ chip->gc.base = -1;
+ chip->gc.owner = THIS_MODULE;
+
+ /* store the shadow value if one was passed by the devicetree */
+ shadow = of_get_property(pdev->dev.of_node, "lantiq,shadow", NULL);
+ if (shadow)
+ chip->shadow = be32_to_cpu(*shadow);
+
+ /* find out which gpio groups should be enabled */
+ groups = of_get_property(pdev->dev.of_node, "lantiq,groups", NULL);
+ if (groups)
+ chip->groups = be32_to_cpu(*groups) & XWAY_STP_GROUP_MASK;
+ else
+ chip->groups = XWAY_STP_GROUP0;
+ chip->gc.ngpio = fls(chip->groups) * 8;
+
+ /* find out which gpios are controlled by the dsl core */
+ dsl = of_get_property(pdev->dev.of_node, "lantiq,dsl", NULL);
+ if (dsl)
+ chip->dsl = be32_to_cpu(*dsl) & XWAY_STP_ADSL_MASK;
+
+ /* 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")) {
+ phy = of_get_property(pdev->dev.of_node, "lantiq,phy1", NULL);
+ if (phy)
+ chip->phy1 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK;
+ phy = of_get_property(pdev->dev.of_node, "lantiq,phy2", NULL);
+ if (phy)
+ chip->phy2 = be32_to_cpu(*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;
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+ clk_enable(clk);
+
+ ret = xway_stp_hw_init(chip);
+ if (!ret)
+ ret = gpiochip_add(&chip->gc);
+
+ if (!ret)
+ dev_info(&pdev->dev, "Init done\n");
+
+ return ret;
+}
+
+static const struct of_device_id xway_stp_match[] = {
+ { .compatible = "lantiq,gpio-stp-xway" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xway_stp_match);
+
+static struct platform_driver xway_stp_driver = {
+ .probe = xway_stp_probe,
+ .driver = {
+ .name = "gpio-stp-xway",
+ .of_match_table = xway_stp_match,
+ },
+};
+
+static int __init xway_stp_init(void)
+{
+ return platform_driver_register(&xway_stp_driver);
+}
+
+subsys_initcall(xway_stp_init);
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
new file mode 100644
index 000000000..458d9d795
--- /dev/null
+++ b/drivers/gpio/gpio-sx150x.c
@@ -0,0 +1,709 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/i2c/sx150x.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+
+#define NO_UPDATE_PENDING -1
+
+/* The chip models of sx150x */
+#define SX150X_456 0
+#define SX150X_789 1
+
+struct sx150x_456_pri {
+ u8 reg_pld_mode;
+ u8 reg_pld_table0;
+ u8 reg_pld_table1;
+ u8 reg_pld_table2;
+ u8 reg_pld_table3;
+ u8 reg_pld_table4;
+ u8 reg_advance;
+};
+
+struct sx150x_789_pri {
+ u8 reg_drain;
+ u8 reg_polarity;
+ u8 reg_clock;
+ u8 reg_misc;
+ u8 reg_reset;
+ u8 ngpios;
+};
+
+struct sx150x_device_data {
+ u8 model;
+ u8 reg_pullup;
+ u8 reg_pulldn;
+ u8 reg_dir;
+ u8 reg_data;
+ u8 reg_irq_mask;
+ u8 reg_irq_src;
+ u8 reg_sense;
+ u8 ngpios;
+ union {
+ struct sx150x_456_pri x456;
+ struct sx150x_789_pri x789;
+ } pri;
+};
+
+struct sx150x_chip {
+ struct gpio_chip gpio_chip;
+ struct i2c_client *client;
+ const struct sx150x_device_data *dev_cfg;
+ int irq_summary;
+ int irq_base;
+ int irq_update;
+ u32 irq_sense;
+ u32 irq_masked;
+ u32 dev_sense;
+ u32 dev_masked;
+ struct irq_chip irq_chip;
+ struct mutex lock;
+};
+
+static const struct sx150x_device_data sx150x_devices[] = {
+ [0] = { /* sx1508q */
+ .model = SX150X_789,
+ .reg_pullup = 0x03,
+ .reg_pulldn = 0x04,
+ .reg_dir = 0x07,
+ .reg_data = 0x08,
+ .reg_irq_mask = 0x09,
+ .reg_irq_src = 0x0c,
+ .reg_sense = 0x0b,
+ .pri.x789 = {
+ .reg_drain = 0x05,
+ .reg_polarity = 0x06,
+ .reg_clock = 0x0f,
+ .reg_misc = 0x10,
+ .reg_reset = 0x7d,
+ },
+ .ngpios = 8,
+ },
+ [1] = { /* sx1509q */
+ .model = SX150X_789,
+ .reg_pullup = 0x07,
+ .reg_pulldn = 0x09,
+ .reg_dir = 0x0f,
+ .reg_data = 0x11,
+ .reg_irq_mask = 0x13,
+ .reg_irq_src = 0x19,
+ .reg_sense = 0x17,
+ .pri.x789 = {
+ .reg_drain = 0x0b,
+ .reg_polarity = 0x0d,
+ .reg_clock = 0x1e,
+ .reg_misc = 0x1f,
+ .reg_reset = 0x7d,
+ },
+ .ngpios = 16
+ },
+ [2] = { /* sx1506q */
+ .model = SX150X_456,
+ .reg_pullup = 0x05,
+ .reg_pulldn = 0x07,
+ .reg_dir = 0x03,
+ .reg_data = 0x01,
+ .reg_irq_mask = 0x09,
+ .reg_irq_src = 0x0f,
+ .reg_sense = 0x0d,
+ .pri.x456 = {
+ .reg_pld_mode = 0x21,
+ .reg_pld_table0 = 0x23,
+ .reg_pld_table1 = 0x25,
+ .reg_pld_table2 = 0x27,
+ .reg_pld_table3 = 0x29,
+ .reg_pld_table4 = 0x2b,
+ .reg_advance = 0xad,
+ },
+ .ngpios = 16
+ },
+};
+
+static const struct i2c_device_id sx150x_id[] = {
+ {"sx1508q", 0},
+ {"sx1509q", 1},
+ {"sx1506q", 2},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sx150x_id);
+
+static const struct of_device_id sx150x_of_match[] = {
+ { .compatible = "semtech,sx1508q" },
+ { .compatible = "semtech,sx1509q" },
+ { .compatible = "semtech,sx1506q" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sx150x_of_match);
+
+static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ s32 err = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (err < 0)
+ dev_warn(&client->dev,
+ "i2c write fail: can't write %02x to %02x: %d\n",
+ val, reg, err);
+ return err;
+}
+
+static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ s32 err = i2c_smbus_read_byte_data(client, reg);
+
+ if (err >= 0)
+ *val = err;
+ else
+ dev_warn(&client->dev,
+ "i2c read fail: can't read from %02x: %d\n",
+ reg, err);
+ return err;
+}
+
+static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
+{
+ return (chip->dev_cfg->ngpios == offset);
+}
+
+/*
+ * These utility functions solve the common problem of locating and setting
+ * configuration bits. Configuration bits are grouped into registers
+ * whose indexes increase downwards. For example, with eight-bit registers,
+ * sixteen gpios would have their config bits grouped in the following order:
+ * REGISTER N-1 [ f e d c b a 9 8 ]
+ * N [ 7 6 5 4 3 2 1 0 ]
+ *
+ * For multi-bit configurations, the pattern gets wider:
+ * REGISTER N-3 [ f f e e d d c c ]
+ * N-2 [ b b a a 9 9 8 8 ]
+ * N-1 [ 7 7 6 6 5 5 4 4 ]
+ * N [ 3 3 2 2 1 1 0 0 ]
+ *
+ * Given the address of the starting register 'N', the index of the gpio
+ * whose configuration we seek to change, and the width in bits of that
+ * configuration, these functions allow us to locate the correct
+ * register and mask the correct bits.
+ */
+static inline void sx150x_find_cfg(u8 offset, u8 width,
+ u8 *reg, u8 *mask, u8 *shift)
+{
+ *reg -= offset * width / 8;
+ *mask = (1 << width) - 1;
+ *shift = (offset * width) % 8;
+ *mask <<= *shift;
+}
+
+static s32 sx150x_write_cfg(struct sx150x_chip *chip,
+ u8 offset, u8 width, u8 reg, u8 val)
+{
+ u8 mask;
+ u8 data;
+ u8 shift;
+ s32 err;
+
+ sx150x_find_cfg(offset, width, &reg, &mask, &shift);
+ err = sx150x_i2c_read(chip->client, reg, &data);
+ if (err < 0)
+ return err;
+
+ data &= ~mask;
+ data |= (val << shift) & mask;
+ return sx150x_i2c_write(chip->client, reg, data);
+}
+
+static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset)
+{
+ u8 reg = chip->dev_cfg->reg_data;
+ u8 mask;
+ u8 data;
+ u8 shift;
+ s32 err;
+
+ sx150x_find_cfg(offset, 1, &reg, &mask, &shift);
+ err = sx150x_i2c_read(chip->client, reg, &data);
+ if (err >= 0)
+ err = (data & mask) != 0 ? 1 : 0;
+
+ return err;
+}
+
+static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
+{
+ sx150x_i2c_write(chip->client,
+ chip->dev_cfg->pri.x789.reg_clock,
+ (val ? 0x1f : 0x10));
+}
+
+static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val)
+{
+ sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_data,
+ (val ? 1 : 0));
+}
+
+static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset)
+{
+ return sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_dir,
+ 1);
+}
+
+static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
+{
+ int err;
+
+ err = sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_data,
+ (val ? 1 : 0));
+ if (err >= 0)
+ err = sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_dir,
+ 0);
+ return err;
+}
+
+static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+ int status = -EINVAL;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_get_io(chip, offset);
+ mutex_unlock(&chip->lock);
+ }
+
+ return status;
+}
+
+static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct sx150x_chip *chip;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ mutex_lock(&chip->lock);
+ if (offset_is_oscio(chip, offset))
+ sx150x_set_oscio(chip, val);
+ else
+ sx150x_set_io(chip, offset, val);
+ mutex_unlock(&chip->lock);
+}
+
+static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+ int status = -EINVAL;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_io_input(chip, offset);
+ mutex_unlock(&chip->lock);
+ }
+ return status;
+}
+
+static int sx150x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset,
+ int val)
+{
+ struct sx150x_chip *chip;
+ int status = 0;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_io_output(chip, offset, val);
+ mutex_unlock(&chip->lock);
+ }
+ return status;
+}
+
+static void sx150x_irq_mask(struct irq_data *d)
+{
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned n = d->hwirq;
+
+ chip->irq_masked |= (1 << n);
+ chip->irq_update = n;
+}
+
+static void sx150x_irq_unmask(struct irq_data *d)
+{
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned n = d->hwirq;
+
+ chip->irq_masked &= ~(1 << n);
+ chip->irq_update = n;
+}
+
+static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned n, val = 0;
+
+ if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+ return -EINVAL;
+
+ n = d->hwirq;
+
+ if (flow_type & IRQ_TYPE_EDGE_RISING)
+ val |= 0x1;
+ if (flow_type & IRQ_TYPE_EDGE_FALLING)
+ val |= 0x2;
+
+ chip->irq_sense &= ~(3UL << (n * 2));
+ chip->irq_sense |= val << (n * 2);
+ chip->irq_update = n;
+ return 0;
+}
+
+static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
+{
+ struct sx150x_chip *chip = (struct sx150x_chip *)dev_id;
+ unsigned nhandled = 0;
+ unsigned sub_irq;
+ unsigned n;
+ s32 err;
+ u8 val;
+ int i;
+
+ for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) {
+ err = sx150x_i2c_read(chip->client,
+ chip->dev_cfg->reg_irq_src - i,
+ &val);
+ if (err < 0)
+ continue;
+
+ sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_irq_src - i,
+ val);
+ for (n = 0; n < 8; ++n) {
+ if (val & (1 << n)) {
+ sub_irq = irq_find_mapping(
+ chip->gpio_chip.irqdomain,
+ (i * 8) + n);
+ handle_nested_irq(sub_irq);
+ ++nhandled;
+ }
+ }
+ }
+
+ return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
+}
+
+static void sx150x_irq_bus_lock(struct irq_data *d)
+{
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+
+ mutex_lock(&chip->lock);
+}
+
+static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
+{
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned n;
+
+ if (chip->irq_update == NO_UPDATE_PENDING)
+ goto out;
+
+ n = chip->irq_update;
+ chip->irq_update = NO_UPDATE_PENDING;
+
+ /* Avoid updates if nothing changed */
+ if (chip->dev_sense == chip->irq_sense &&
+ chip->dev_masked == chip->irq_masked)
+ goto out;
+
+ chip->dev_sense = chip->irq_sense;
+ chip->dev_masked = chip->irq_masked;
+
+ if (chip->irq_masked & (1 << n)) {
+ sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
+ sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
+ } else {
+ sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
+ sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
+ chip->irq_sense >> (n * 2));
+ }
+out:
+ mutex_unlock(&chip->lock);
+}
+
+static void sx150x_init_chip(struct sx150x_chip *chip,
+ struct i2c_client *client,
+ kernel_ulong_t driver_data,
+ struct sx150x_platform_data *pdata)
+{
+ mutex_init(&chip->lock);
+
+ chip->client = client;
+ chip->dev_cfg = &sx150x_devices[driver_data];
+ chip->gpio_chip.dev = &client->dev;
+ chip->gpio_chip.label = client->name;
+ chip->gpio_chip.direction_input = sx150x_gpio_direction_input;
+ chip->gpio_chip.direction_output = sx150x_gpio_direction_output;
+ chip->gpio_chip.get = sx150x_gpio_get;
+ chip->gpio_chip.set = sx150x_gpio_set;
+ chip->gpio_chip.base = pdata->gpio_base;
+ chip->gpio_chip.can_sleep = true;
+ chip->gpio_chip.ngpio = chip->dev_cfg->ngpios;
+#ifdef CONFIG_OF_GPIO
+ chip->gpio_chip.of_node = client->dev.of_node;
+ chip->gpio_chip.of_gpio_n_cells = 2;
+#endif
+ if (pdata->oscio_is_gpo)
+ ++chip->gpio_chip.ngpio;
+
+ chip->irq_chip.name = client->name;
+ chip->irq_chip.irq_mask = sx150x_irq_mask;
+ chip->irq_chip.irq_unmask = sx150x_irq_unmask;
+ chip->irq_chip.irq_set_type = sx150x_irq_set_type;
+ chip->irq_chip.irq_bus_lock = sx150x_irq_bus_lock;
+ chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ chip->irq_masked = ~0;
+ chip->irq_sense = 0;
+ chip->dev_masked = ~0;
+ chip->dev_sense = 0;
+ chip->irq_update = NO_UPDATE_PENDING;
+}
+
+static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
+{
+ int err = 0;
+ unsigned n;
+
+ for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n)
+ err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8));
+ return err;
+}
+
+static int sx150x_reset(struct sx150x_chip *chip)
+{
+ int err;
+
+ err = i2c_smbus_write_byte_data(chip->client,
+ chip->dev_cfg->pri.x789.reg_reset,
+ 0x12);
+ if (err < 0)
+ return err;
+
+ err = i2c_smbus_write_byte_data(chip->client,
+ chip->dev_cfg->pri.x789.reg_reset,
+ 0x34);
+ return err;
+}
+
+static int sx150x_init_hw(struct sx150x_chip *chip,
+ struct sx150x_platform_data *pdata)
+{
+ int err = 0;
+
+ if (pdata->reset_during_probe) {
+ err = sx150x_reset(chip);
+ if (err < 0)
+ return err;
+ }
+
+ if (chip->dev_cfg->model == SX150X_789)
+ err = sx150x_i2c_write(chip->client,
+ chip->dev_cfg->pri.x789.reg_misc,
+ 0x01);
+ else
+ err = sx150x_i2c_write(chip->client,
+ chip->dev_cfg->pri.x456.reg_advance,
+ 0x04);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup,
+ pdata->io_pullup_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn,
+ pdata->io_pulldn_ena);
+ if (err < 0)
+ return err;
+
+ if (chip->dev_cfg->model == SX150X_789) {
+ err = sx150x_init_io(chip,
+ chip->dev_cfg->pri.x789.reg_drain,
+ pdata->io_open_drain_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip,
+ chip->dev_cfg->pri.x789.reg_polarity,
+ pdata->io_polarity);
+ if (err < 0)
+ return err;
+ } else {
+ /* Set all pins to work in normal mode */
+ err = sx150x_init_io(chip,
+ chip->dev_cfg->pri.x456.reg_pld_mode,
+ 0);
+ if (err < 0)
+ return err;
+ }
+
+
+ if (pdata->oscio_is_gpo)
+ sx150x_set_oscio(chip, 0);
+
+ return err;
+}
+
+static int sx150x_install_irq_chip(struct sx150x_chip *chip,
+ int irq_summary,
+ int irq_base)
+{
+ int err;
+
+ chip->irq_summary = irq_summary;
+ chip->irq_base = irq_base;
+
+ /* Add gpio chip to irq subsystem */
+ err = gpiochip_irqchip_add(&chip->gpio_chip,
+ &chip->irq_chip, chip->irq_base,
+ handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
+ if (err) {
+ dev_err(&chip->client->dev,
+ "could not connect irqchip to gpiochip\n");
+ return err;
+ }
+
+ err = devm_request_threaded_irq(&chip->client->dev,
+ irq_summary, NULL, sx150x_irq_thread_fn,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING,
+ chip->irq_chip.name, chip);
+ if (err < 0) {
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ }
+
+ return err;
+}
+
+static int sx150x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA;
+ struct sx150x_platform_data *pdata;
+ struct sx150x_chip *chip;
+ int rc;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(client->adapter, i2c_funcs))
+ return -ENOSYS;
+
+ chip = devm_kzalloc(&client->dev,
+ sizeof(struct sx150x_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ sx150x_init_chip(chip, client, id->driver_data, pdata);
+ rc = sx150x_init_hw(chip, pdata);
+ if (rc < 0)
+ return rc;
+
+ rc = gpiochip_add(&chip->gpio_chip);
+ if (rc)
+ return rc;
+
+ if (pdata->irq_summary >= 0) {
+ rc = sx150x_install_irq_chip(chip,
+ pdata->irq_summary,
+ pdata->irq_base);
+ if (rc < 0)
+ goto probe_fail_post_gpiochip_add;
+ }
+
+ i2c_set_clientdata(client, chip);
+
+ return 0;
+probe_fail_post_gpiochip_add:
+ gpiochip_remove(&chip->gpio_chip);
+ return rc;
+}
+
+static int sx150x_remove(struct i2c_client *client)
+{
+ struct sx150x_chip *chip;
+
+ chip = i2c_get_clientdata(client);
+ gpiochip_remove(&chip->gpio_chip);
+
+ return 0;
+}
+
+static struct i2c_driver sx150x_driver = {
+ .driver = {
+ .name = "sx150x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sx150x_of_match),
+ },
+ .probe = sx150x_probe,
+ .remove = sx150x_remove,
+ .id_table = sx150x_id,
+};
+
+static int __init sx150x_init(void)
+{
+ return i2c_add_driver(&sx150x_driver);
+}
+subsys_initcall(sx150x_init);
+
+static void __exit sx150x_exit(void)
+{
+ return i2c_del_driver(&sx150x_driver);
+}
+module_exit(sx150x_exit);
+
+MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:sx150x");
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
new file mode 100644
index 000000000..045a95257
--- /dev/null
+++ b/drivers/gpio/gpio-syscon.c
@@ -0,0 +1,265 @@
+/*
+ * SYSCON GPIO driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define GPIO_SYSCON_FEAT_IN BIT(0)
+#define GPIO_SYSCON_FEAT_OUT BIT(1)
+#define GPIO_SYSCON_FEAT_DIR BIT(2)
+
+/* SYSCON driver is designed to use 32-bit wide registers */
+#define SYSCON_REG_SIZE (4)
+#define SYSCON_REG_BITS (SYSCON_REG_SIZE * 8)
+
+/**
+ * struct syscon_gpio_data - Configuration for the device.
+ * 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
+ * GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
+ * 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;
+ unsigned int dir_bit_offset;
+ void (*set)(struct gpio_chip *chip,
+ unsigned offset, int value);
+};
+
+struct syscon_gpio_priv {
+ struct gpio_chip chip;
+ struct regmap *syscon;
+ const struct syscon_gpio_data *data;
+ u32 dreg_offset;
+ u32 dir_reg_offset;
+};
+
+static inline struct syscon_gpio_priv *to_syscon_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct syscon_gpio_priv, chip);
+}
+
+static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+ unsigned int val, offs;
+ int ret;
+
+ offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
+
+ ret = regmap_read(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(offs % SYSCON_REG_BITS));
+}
+
+static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+ unsigned int offs;
+
+ offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS),
+ val ? BIT(offs % SYSCON_REG_BITS) : 0);
+}
+
+static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+
+ if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
+ unsigned int offs;
+
+ offs = priv->dir_reg_offset +
+ priv->data->dir_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS), 0);
+ }
+
+ return 0;
+}
+
+static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+
+ if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
+ unsigned int offs;
+
+ offs = priv->dir_reg_offset +
+ priv->data->dir_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS),
+ BIT(offs % SYSCON_REG_BITS));
+ }
+
+ priv->data->set(chip, offset, val);
+
+ return 0;
+}
+
+static const struct syscon_gpio_data clps711x_mctrl_gpio = {
+ /* ARM CLPS711X SYSFLG1 Bits 8-10 */
+ .compatible = "cirrus,clps711x-syscon1",
+ .flags = GPIO_SYSCON_FEAT_IN,
+ .bit_count = 3,
+ .dat_bit_offset = 0x40 * 8 + 8,
+};
+
+#define KEYSTONE_LOCK_BIT BIT(0)
+
+static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+ unsigned int offs;
+ int ret;
+
+ offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
+
+ if (!val)
+ return;
+
+ ret = regmap_update_bits(
+ priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT,
+ BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT);
+ if (ret < 0)
+ dev_err(chip->dev, "gpio write failed ret(%d)\n", ret);
+}
+
+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,
+ .set = keystone_gpio_set,
+};
+
+static const struct of_device_id syscon_gpio_ids[] = {
+ {
+ .compatible = "cirrus,clps711x-mctrl-gpio",
+ .data = &clps711x_mctrl_gpio,
+ },
+ {
+ .compatible = "ti,keystone-dsp-gpio",
+ .data = &keystone_dsp_gpio,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, syscon_gpio_ids);
+
+static int syscon_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id = of_match_device(syscon_gpio_ids, dev);
+ struct syscon_gpio_priv *priv;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = of_id->data;
+
+ 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))
+ 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.dev = dev;
+ priv->chip.owner = THIS_MODULE;
+ priv->chip.label = dev_name(dev);
+ priv->chip.base = -1;
+ priv->chip.ngpio = priv->data->bit_count;
+ priv->chip.get = syscon_gpio_get;
+ if (priv->data->flags & GPIO_SYSCON_FEAT_IN)
+ priv->chip.direction_input = syscon_gpio_dir_in;
+ if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) {
+ priv->chip.set = priv->data->set ? : syscon_gpio_set;
+ priv->chip.direction_output = syscon_gpio_dir_out;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return gpiochip_add(&priv->chip);
+}
+
+static int syscon_gpio_remove(struct platform_device *pdev)
+{
+ struct syscon_gpio_priv *priv = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&priv->chip);
+ return 0;
+}
+
+static struct platform_driver syscon_gpio_driver = {
+ .driver = {
+ .name = "gpio-syscon",
+ .of_match_table = syscon_gpio_ids,
+ },
+ .probe = syscon_gpio_probe,
+ .remove = syscon_gpio_remove,
+};
+module_platform_driver(syscon_gpio_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("SYSCON GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
new file mode 100644
index 000000000..46b89614a
--- /dev/null
+++ b/drivers/gpio/gpio-tb10x.c
@@ -0,0 +1,320 @@
+/* Abilis Systems MODULE DESCRIPTION
+ *
+ * Copyright (C) Abilis Systems 2013
+ *
+ * Authors: Sascha Leuenberger <sascha.leuenberger@abilis.com>
+ * Christian Ruppert <christian.ruppert@abilis.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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/pinctrl/consumer.h>
+
+#define TB10X_GPIO_DIR_IN (0x00000000)
+#define TB10X_GPIO_DIR_OUT (0x00000001)
+#define OFFSET_TO_REG_DDR (0x00)
+#define OFFSET_TO_REG_DATA (0x04)
+#define OFFSET_TO_REG_INT_EN (0x08)
+#define OFFSET_TO_REG_CHANGE (0x0C)
+#define OFFSET_TO_REG_WRMASK (0x10)
+#define OFFSET_TO_REG_INT_TYPE (0x14)
+
+
+/**
+ * @spinlock: used for atomic read/modify/write of registers
+ * @base: register base address
+ * @domain: IRQ domain of GPIO generated interrupts managed by this controller
+ * @irq: Interrupt line of parent interrupt controller
+ * @gc: gpio_chip structure associated to this GPIO controller
+ */
+struct tb10x_gpio {
+ spinlock_t spinlock;
+ void __iomem *base;
+ struct irq_domain *domain;
+ int irq;
+ struct gpio_chip gc;
+};
+
+static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
+{
+ return ioread32(gpio->base + offs);
+}
+
+static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 val)
+{
+ iowrite32(val, gpio->base + offs);
+}
+
+static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 mask, u32 val)
+{
+ u32 r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio->spinlock, flags);
+
+ r = tb10x_reg_read(gpio, offs);
+ r = (r & ~mask) | (val & mask);
+
+ tb10x_reg_write(gpio, offs, r);
+
+ spin_unlock_irqrestore(&gpio->spinlock, flags);
+}
+
+static inline struct tb10x_gpio *to_tb10x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tb10x_gpio, gc);
+}
+
+static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = TB10X_GPIO_DIR_IN << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int val;
+
+ val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA);
+
+ if (val & BIT(offset))
+ return 1;
+ else
+ return 0;
+}
+
+static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = value << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val);
+}
+
+static int tb10x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = TB10X_GPIO_DIR_OUT << offset;
+
+ tb10x_gpio_set(chip, offset, value);
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+
+ return irq_create_mapping(tb10x_gpio->domain, offset);
+}
+
+static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) {
+ pr_err("Only (both) edge triggered interrupts supported.\n");
+ return -EINVAL;
+ }
+
+ irqd_set_trigger_type(data, type);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
+{
+ struct tb10x_gpio *tb10x_gpio = data;
+ u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
+ u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
+ const unsigned long bits = r & m;
+ int i;
+
+ for_each_set_bit(i, &bits, 32)
+ generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i));
+
+ return IRQ_HANDLED;
+}
+
+static int tb10x_gpio_probe(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio;
+ struct resource *mem;
+ struct device_node *dn = pdev->dev.of_node;
+ int ret = -EBUSY;
+ u32 ngpio;
+
+ if (!dn)
+ return -EINVAL;
+
+ if (of_property_read_u32(dn, "abilis,ngpio", &ngpio))
+ return -EINVAL;
+
+ tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL);
+ if (tb10x_gpio == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&tb10x_gpio->spinlock);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(tb10x_gpio->base))
+ return PTR_ERR(tb10x_gpio->base);
+
+ tb10x_gpio->gc.label = of_node_full_name(dn);
+ tb10x_gpio->gc.dev = &pdev->dev;
+ tb10x_gpio->gc.owner = THIS_MODULE;
+ tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
+ tb10x_gpio->gc.get = tb10x_gpio_get;
+ tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
+ tb10x_gpio->gc.set = tb10x_gpio_set;
+ tb10x_gpio->gc.request = tb10x_gpio_request;
+ tb10x_gpio->gc.free = tb10x_gpio_free;
+ tb10x_gpio->gc.base = -1;
+ tb10x_gpio->gc.ngpio = ngpio;
+ tb10x_gpio->gc.can_sleep = false;
+
+
+ ret = gpiochip_add(&tb10x_gpio->gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not add gpiochip.\n");
+ goto fail_gpiochip_registration;
+ }
+
+ platform_set_drvdata(pdev, tb10x_gpio);
+
+ if (of_find_property(dn, "interrupt-controller", NULL)) {
+ struct irq_chip_generic *gc;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "No interrupt specified.\n");
+ goto fail_get_irq;
+ }
+
+ tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
+ tb10x_gpio->irq = ret;
+
+ ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
+ IRQF_TRIGGER_NONE | IRQF_SHARED,
+ dev_name(&pdev->dev), tb10x_gpio);
+ if (ret != 0)
+ goto fail_request_irq;
+
+ tb10x_gpio->domain = irq_domain_add_linear(dn,
+ tb10x_gpio->gc.ngpio,
+ &irq_generic_chip_ops, NULL);
+ if (!tb10x_gpio->domain) {
+ ret = -ENOMEM;
+ goto fail_irq_domain;
+ }
+
+ ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
+ tb10x_gpio->gc.ngpio, 1, tb10x_gpio->gc.label,
+ handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE,
+ IRQ_GC_INIT_MASK_CACHE);
+ if (ret)
+ goto fail_irq_domain;
+
+ gc = tb10x_gpio->domain->gc->gc[0];
+ gc->reg_base = tb10x_gpio->base;
+ gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_type = tb10x_gpio_irq_set_type;
+ gc->chip_types[0].regs.ack = OFFSET_TO_REG_CHANGE;
+ gc->chip_types[0].regs.mask = OFFSET_TO_REG_INT_EN;
+ }
+
+ return 0;
+
+fail_irq_domain:
+fail_request_irq:
+fail_get_irq:
+ gpiochip_remove(&tb10x_gpio->gc);
+fail_gpiochip_registration:
+fail_ioremap:
+ return ret;
+}
+
+static int tb10x_gpio_remove(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
+
+ if (tb10x_gpio->gc.to_irq) {
+ irq_remove_generic_chip(tb10x_gpio->domain->gc->gc[0],
+ BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0);
+ kfree(tb10x_gpio->domain->gc);
+ irq_domain_remove(tb10x_gpio->domain);
+ free_irq(tb10x_gpio->irq, tb10x_gpio);
+ }
+ gpiochip_remove(&tb10x_gpio->gc);
+
+ return 0;
+}
+
+static const struct of_device_id tb10x_gpio_dt_ids[] = {
+ { .compatible = "abilis,tb10x-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids);
+
+static struct platform_driver tb10x_gpio_driver = {
+ .probe = tb10x_gpio_probe,
+ .remove = tb10x_gpio_remove,
+ .driver = {
+ .name = "tb10x-gpio",
+ .of_match_table = tb10x_gpio_dt_ids,
+ }
+};
+
+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
new file mode 100644
index 000000000..31b244cff
--- /dev/null
+++ b/drivers/gpio/gpio-tc3589x.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/tc3589x.h>
+
+/*
+ * 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 };
+
+#define CACHE_NR_REGS 4
+#define CACHE_NR_BANKS 3
+
+struct tc3589x_gpio {
+ struct gpio_chip chip;
+ struct tc3589x *tc3589x;
+ struct device *dev;
+ struct mutex irq_lock;
+ /* Caches of interrupt control registers for bus_lock */
+ u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
+ u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
+};
+
+static inline struct tc3589x_gpio *to_tc3589x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tc3589x_gpio, chip);
+}
+
+static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
+ struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
+ u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;
+ u8 mask = 1 << (offset % 8);
+ int ret;
+
+ ret = tc3589x_reg_read(tc3589x, reg);
+ if (ret < 0)
+ return ret;
+
+ return ret & mask;
+}
+
+static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
+ struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
+ u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;
+ unsigned pos = offset % 8;
+ u8 data[] = {!!val << pos, 1 << pos};
+
+ tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data);
+}
+
+static int tc3589x_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int val)
+{
+ struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
+ struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
+ u8 reg = TC3589x_GPIODIR0 + offset / 8;
+ unsigned pos = offset % 8;
+
+ tc3589x_gpio_set(chip, offset, val);
+
+ return tc3589x_set_bits(tc3589x, reg, 1 << pos, 1 << pos);
+}
+
+static int tc3589x_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
+ struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
+ u8 reg = TC3589x_GPIODIR0 + offset / 8;
+ unsigned pos = offset % 8;
+
+ return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "tc3589x",
+ .owner = THIS_MODULE,
+ .direction_input = tc3589x_gpio_direction_input,
+ .get = tc3589x_gpio_get,
+ .direction_output = tc3589x_gpio_direction_output,
+ .set = tc3589x_gpio_set,
+ .can_sleep = true,
+};
+
+static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct tc3589x_gpio *tc3589x_gpio = container_of(gc, struct tc3589x_gpio, chip);
+ int offset = d->hwirq;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ if (type == IRQ_TYPE_EDGE_BOTH) {
+ tc3589x_gpio->regs[REG_IBE][regoffset] |= mask;
+ return 0;
+ }
+
+ tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask;
+
+ if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
+ tc3589x_gpio->regs[REG_IS][regoffset] |= mask;
+ else
+ tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask;
+
+ if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
+ tc3589x_gpio->regs[REG_IEV][regoffset] |= mask;
+ else
+ tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask;
+
+ return 0;
+}
+
+static void tc3589x_gpio_irq_lock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct tc3589x_gpio *tc3589x_gpio = container_of(gc, struct tc3589x_gpio, chip);
+
+ mutex_lock(&tc3589x_gpio->irq_lock);
+}
+
+static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct tc3589x_gpio *tc3589x_gpio = container_of(gc, struct tc3589x_gpio, chip);
+ struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
+ static const u8 regmap[] = {
+ [REG_IBE] = TC3589x_GPIOIBE0,
+ [REG_IEV] = TC3589x_GPIOIEV0,
+ [REG_IS] = TC3589x_GPIOIS0,
+ [REG_IE] = TC3589x_GPIOIE0,
+ };
+ int i, j;
+
+ for (i = 0; i < CACHE_NR_REGS; i++) {
+ for (j = 0; j < CACHE_NR_BANKS; j++) {
+ u8 old = tc3589x_gpio->oldregs[i][j];
+ u8 new = tc3589x_gpio->regs[i][j];
+
+ if (new == old)
+ continue;
+
+ tc3589x_gpio->oldregs[i][j] = new;
+ tc3589x_reg_write(tc3589x, regmap[i] + j * 8, new);
+ }
+ }
+
+ mutex_unlock(&tc3589x_gpio->irq_lock);
+}
+
+static void tc3589x_gpio_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct tc3589x_gpio *tc3589x_gpio = container_of(gc, struct tc3589x_gpio, chip);
+ int offset = d->hwirq;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask;
+}
+
+static void tc3589x_gpio_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct tc3589x_gpio *tc3589x_gpio = container_of(gc, struct tc3589x_gpio, chip);
+ int offset = d->hwirq;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ tc3589x_gpio->regs[REG_IE][regoffset] |= mask;
+}
+
+static struct irq_chip tc3589x_gpio_irq_chip = {
+ .name = "tc3589x-gpio",
+ .irq_bus_lock = tc3589x_gpio_irq_lock,
+ .irq_bus_sync_unlock = tc3589x_gpio_irq_sync_unlock,
+ .irq_mask = tc3589x_gpio_irq_mask,
+ .irq_unmask = tc3589x_gpio_irq_unmask,
+ .irq_set_type = tc3589x_gpio_irq_set_type,
+};
+
+static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
+{
+ struct tc3589x_gpio *tc3589x_gpio = dev;
+ struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
+ u8 status[CACHE_NR_BANKS];
+ int ret;
+ int i;
+
+ ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0,
+ ARRAY_SIZE(status), status);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(status); i++) {
+ unsigned int stat = status[i];
+ if (!stat)
+ continue;
+
+ while (stat) {
+ int bit = __ffs(stat);
+ int line = i * 8 + bit;
+ int irq = irq_find_mapping(tc3589x_gpio->chip.irqdomain,
+ line);
+
+ handle_nested_irq(irq);
+ stat &= ~(1 << bit);
+ }
+
+ tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+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;
+ int ret;
+ int irq;
+
+ if (!np) {
+ dev_err(&pdev->dev, "No Device Tree node found\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ tc3589x_gpio = devm_kzalloc(&pdev->dev, sizeof(struct tc3589x_gpio),
+ GFP_KERNEL);
+ if (!tc3589x_gpio)
+ return -ENOMEM;
+
+ mutex_init(&tc3589x_gpio->irq_lock);
+
+ tc3589x_gpio->dev = &pdev->dev;
+ tc3589x_gpio->tc3589x = tc3589x;
+
+ tc3589x_gpio->chip = template_chip;
+ tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
+ tc3589x_gpio->chip.dev = &pdev->dev;
+ tc3589x_gpio->chip.base = -1;
+ tc3589x_gpio->chip.of_node = np;
+
+ /* Bring the GPIO module out of reset */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
+ TC3589x_RSTCTRL_GPIRST, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_threaded_irq(&pdev->dev,
+ irq, NULL, tc3589x_gpio_irq,
+ IRQF_ONESHOT, "tc3589x-gpio",
+ tc3589x_gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+ return ret;
+ }
+
+ ret = gpiochip_add(&tc3589x_gpio->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+ return ret;
+ }
+
+ ret = gpiochip_irqchip_add(&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_chained_irqchip(&tc3589x_gpio->chip,
+ &tc3589x_gpio_irq_chip,
+ irq,
+ NULL);
+
+ platform_set_drvdata(pdev, tc3589x_gpio);
+
+ return 0;
+}
+
+static int tc3589x_gpio_remove(struct platform_device *pdev)
+{
+ struct tc3589x_gpio *tc3589x_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&tc3589x_gpio->chip);
+
+ return 0;
+}
+
+static struct platform_driver tc3589x_gpio_driver = {
+ .driver.name = "tc3589x-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = tc3589x_gpio_probe,
+ .remove = tc3589x_gpio_remove,
+};
+
+static int __init tc3589x_gpio_init(void)
+{
+ return platform_driver_register(&tc3589x_gpio_driver);
+}
+subsys_initcall(tc3589x_gpio_init);
+
+static void __exit tc3589x_gpio_exit(void)
+{
+ platform_driver_unregister(&tc3589x_gpio_driver);
+}
+module_exit(tc3589x_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TC3589x GPIO driver");
+MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
new file mode 100644
index 000000000..1741981d5
--- /dev/null
+++ b/drivers/gpio/gpio-tegra.c
@@ -0,0 +1,590 @@
+/*
+ * arch/arm/mach-tegra/gpio.c
+ *
+ * Copyright (c) 2010 Google, Inc
+ *
+ * Author:
+ * Erik Gilling <konkers@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm.h>
+
+#define GPIO_BANK(x) ((x) >> 5)
+#define GPIO_PORT(x) (((x) >> 3) & 0x3)
+#define GPIO_BIT(x) ((x) & 0x7)
+
+#define GPIO_REG(x) (GPIO_BANK(x) * tegra_gpio_bank_stride + \
+ GPIO_PORT(x) * 4)
+
+#define GPIO_CNF(x) (GPIO_REG(x) + 0x00)
+#define GPIO_OE(x) (GPIO_REG(x) + 0x10)
+#define GPIO_OUT(x) (GPIO_REG(x) + 0X20)
+#define GPIO_IN(x) (GPIO_REG(x) + 0x30)
+#define GPIO_INT_STA(x) (GPIO_REG(x) + 0x40)
+#define GPIO_INT_ENB(x) (GPIO_REG(x) + 0x50)
+#define GPIO_INT_LVL(x) (GPIO_REG(x) + 0x60)
+#define GPIO_INT_CLR(x) (GPIO_REG(x) + 0x70)
+
+#define GPIO_MSK_CNF(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x00)
+#define GPIO_MSK_OE(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x10)
+#define GPIO_MSK_OUT(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0X20)
+#define GPIO_MSK_INT_STA(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x40)
+#define GPIO_MSK_INT_ENB(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x50)
+#define GPIO_MSK_INT_LVL(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x60)
+
+#define GPIO_INT_LVL_MASK 0x010101
+#define GPIO_INT_LVL_EDGE_RISING 0x000101
+#define GPIO_INT_LVL_EDGE_FALLING 0x000100
+#define GPIO_INT_LVL_EDGE_BOTH 0x010100
+#define GPIO_INT_LVL_LEVEL_HIGH 0x000001
+#define GPIO_INT_LVL_LEVEL_LOW 0x000000
+
+struct tegra_gpio_bank {
+ int bank;
+ int irq;
+ spinlock_t lvl_lock[4];
+#ifdef CONFIG_PM_SLEEP
+ u32 cnf[4];
+ u32 out[4];
+ u32 oe[4];
+ u32 int_enb[4];
+ u32 int_lvl[4];
+ u32 wake_enb[4];
+#endif
+};
+
+static struct device *dev;
+static struct irq_domain *irq_domain;
+static void __iomem *regs;
+static u32 tegra_gpio_bank_count;
+static u32 tegra_gpio_bank_stride;
+static u32 tegra_gpio_upper_offset;
+static struct tegra_gpio_bank *tegra_gpio_banks;
+
+static inline void tegra_gpio_writel(u32 val, u32 reg)
+{
+ __raw_writel(val, regs + reg);
+}
+
+static inline u32 tegra_gpio_readl(u32 reg)
+{
+ return __raw_readl(regs + reg);
+}
+
+static int tegra_gpio_compose(int bank, int port, int bit)
+{
+ return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7);
+}
+
+static void tegra_gpio_mask_write(u32 reg, int gpio, int value)
+{
+ u32 val;
+
+ val = 0x100 << GPIO_BIT(gpio);
+ if (value)
+ val |= 1 << GPIO_BIT(gpio);
+ tegra_gpio_writel(val, reg);
+}
+
+static void tegra_gpio_enable(int gpio)
+{
+ tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1);
+}
+
+static void tegra_gpio_disable(int gpio)
+{
+ tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0);
+}
+
+static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(offset);
+}
+
+static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(offset);
+ tegra_gpio_disable(offset);
+}
+
+static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ tegra_gpio_mask_write(GPIO_MSK_OUT(offset), offset, value);
+}
+
+static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ /* If gpio is in output mode then read from the out value */
+ if ((tegra_gpio_readl(GPIO_OE(offset)) >> GPIO_BIT(offset)) & 1)
+ return (tegra_gpio_readl(GPIO_OUT(offset)) >>
+ GPIO_BIT(offset)) & 0x1;
+
+ return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1;
+}
+
+static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 0);
+ tegra_gpio_enable(offset);
+ return 0;
+}
+
+static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ tegra_gpio_set(chip, offset, value);
+ tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 1);
+ tegra_gpio_enable(offset);
+ return 0;
+}
+
+static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return irq_find_mapping(irq_domain, offset);
+}
+
+static struct gpio_chip tegra_gpio_chip = {
+ .label = "tegra-gpio",
+ .request = tegra_gpio_request,
+ .free = tegra_gpio_free,
+ .direction_input = tegra_gpio_direction_input,
+ .get = tegra_gpio_get,
+ .direction_output = tegra_gpio_direction_output,
+ .set = tegra_gpio_set,
+ .to_irq = tegra_gpio_to_irq,
+ .base = 0,
+};
+
+static void tegra_gpio_irq_ack(struct irq_data *d)
+{
+ int gpio = d->hwirq;
+
+ tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio));
+}
+
+static void tegra_gpio_irq_mask(struct irq_data *d)
+{
+ int gpio = d->hwirq;
+
+ tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0);
+}
+
+static void tegra_gpio_irq_unmask(struct irq_data *d)
+{
+ int gpio = d->hwirq;
+
+ tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 1);
+}
+
+static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ int gpio = d->hwirq;
+ struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ int port = GPIO_PORT(gpio);
+ int lvl_type;
+ int val;
+ unsigned long flags;
+ int ret;
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ lvl_type = GPIO_INT_LVL_EDGE_RISING;
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ lvl_type = GPIO_INT_LVL_EDGE_FALLING;
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ lvl_type = GPIO_INT_LVL_EDGE_BOTH;
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ lvl_type = GPIO_INT_LVL_LEVEL_HIGH;
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ lvl_type = GPIO_INT_LVL_LEVEL_LOW;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = gpiochip_lock_as_irq(&tegra_gpio_chip, gpio);
+ if (ret) {
+ dev_err(dev, "unable to lock Tegra GPIO %d as IRQ\n", gpio);
+ return ret;
+ }
+
+ spin_lock_irqsave(&bank->lvl_lock[port], flags);
+
+ val = tegra_gpio_readl(GPIO_INT_LVL(gpio));
+ val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio));
+ val |= lvl_type << GPIO_BIT(gpio);
+ tegra_gpio_writel(val, GPIO_INT_LVL(gpio));
+
+ spin_unlock_irqrestore(&bank->lvl_lock[port], flags);
+
+ tegra_gpio_mask_write(GPIO_MSK_OE(gpio), gpio, 0);
+ tegra_gpio_enable(gpio);
+
+ if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+
+ return 0;
+}
+
+static void tegra_gpio_irq_shutdown(struct irq_data *d)
+{
+ int gpio = d->hwirq;
+
+ gpiochip_unlock_as_irq(&tegra_gpio_chip, gpio);
+}
+
+static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct tegra_gpio_bank *bank;
+ int port;
+ int pin;
+ int unmasked = 0;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+
+ bank = irq_get_handler_data(irq);
+
+ for (port = 0; port < 4; port++) {
+ int gpio = tegra_gpio_compose(bank->bank, port, 0);
+ unsigned long sta = tegra_gpio_readl(GPIO_INT_STA(gpio)) &
+ tegra_gpio_readl(GPIO_INT_ENB(gpio));
+ u32 lvl = tegra_gpio_readl(GPIO_INT_LVL(gpio));
+
+ for_each_set_bit(pin, &sta, 8) {
+ tegra_gpio_writel(1 << pin, GPIO_INT_CLR(gpio));
+
+ /* if gpio is edge triggered, clear condition
+ * before executing the hander so that we don't
+ * miss edges
+ */
+ if (lvl & (0x100 << pin)) {
+ unmasked = 1;
+ chained_irq_exit(chip, desc);
+ }
+
+ generic_handle_irq(gpio_to_irq(gpio + pin));
+ }
+ }
+
+ if (!unmasked)
+ chained_irq_exit(chip, desc);
+
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_gpio_resume(struct device *dev)
+{
+ unsigned long flags;
+ int b;
+ int p;
+
+ local_irq_save(flags);
+
+ for (b = 0; b < tegra_gpio_bank_count; b++) {
+ struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
+
+ for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
+ unsigned int gpio = (b<<5) | (p<<3);
+ tegra_gpio_writel(bank->cnf[p], GPIO_CNF(gpio));
+ tegra_gpio_writel(bank->out[p], GPIO_OUT(gpio));
+ tegra_gpio_writel(bank->oe[p], GPIO_OE(gpio));
+ tegra_gpio_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio));
+ tegra_gpio_writel(bank->int_enb[p], GPIO_INT_ENB(gpio));
+ }
+ }
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int tegra_gpio_suspend(struct device *dev)
+{
+ unsigned long flags;
+ int b;
+ int p;
+
+ local_irq_save(flags);
+ for (b = 0; b < tegra_gpio_bank_count; b++) {
+ struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
+
+ for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
+ unsigned int gpio = (b<<5) | (p<<3);
+ bank->cnf[p] = tegra_gpio_readl(GPIO_CNF(gpio));
+ bank->out[p] = tegra_gpio_readl(GPIO_OUT(gpio));
+ bank->oe[p] = tegra_gpio_readl(GPIO_OE(gpio));
+ bank->int_enb[p] = tegra_gpio_readl(GPIO_INT_ENB(gpio));
+ bank->int_lvl[p] = tegra_gpio_readl(GPIO_INT_LVL(gpio));
+
+ /* Enable gpio irq for wake up source */
+ tegra_gpio_writel(bank->wake_enb[p],
+ GPIO_INT_ENB(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);
+ int gpio = d->hwirq;
+ u32 port, bit, mask;
+
+ port = GPIO_PORT(gpio);
+ bit = GPIO_BIT(gpio);
+ mask = BIT(bit);
+
+ if (enable)
+ bank->wake_enb[port] |= mask;
+ else
+ bank->wake_enb[port] &= ~mask;
+
+ return irq_set_irq_wake(bank->irq, enable);
+}
+#endif
+
+static struct irq_chip tegra_gpio_irq_chip = {
+ .name = "GPIO",
+ .irq_ack = tegra_gpio_irq_ack,
+ .irq_mask = tegra_gpio_irq_mask,
+ .irq_unmask = tegra_gpio_irq_unmask,
+ .irq_set_type = tegra_gpio_irq_set_type,
+ .irq_shutdown = tegra_gpio_irq_shutdown,
+#ifdef CONFIG_PM_SLEEP
+ .irq_set_wake = tegra_gpio_irq_set_wake,
+#endif
+};
+
+static const struct dev_pm_ops tegra_gpio_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
+};
+
+struct tegra_gpio_soc_config {
+ u32 bank_stride;
+ u32 upper_offset;
+};
+
+static struct tegra_gpio_soc_config tegra20_gpio_config = {
+ .bank_stride = 0x80,
+ .upper_offset = 0x800,
+};
+
+static struct tegra_gpio_soc_config tegra30_gpio_config = {
+ .bank_stride = 0x100,
+ .upper_offset = 0x80,
+};
+
+static const struct of_device_id tegra_gpio_of_match[] = {
+ { .compatible = "nvidia,tegra30-gpio", .data = &tegra30_gpio_config },
+ { .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config },
+ { },
+};
+
+/* This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
+static int tegra_gpio_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct tegra_gpio_soc_config *config;
+ struct resource *res;
+ struct tegra_gpio_bank *bank;
+ int ret;
+ int gpio;
+ int i;
+ int j;
+
+ dev = &pdev->dev;
+
+ match = of_match_device(tegra_gpio_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ config = (struct tegra_gpio_soc_config *)match->data;
+
+ tegra_gpio_bank_stride = config->bank_stride;
+ tegra_gpio_upper_offset = config->upper_offset;
+
+ for (;;) {
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, tegra_gpio_bank_count);
+ if (!res)
+ break;
+ tegra_gpio_bank_count++;
+ }
+ if (!tegra_gpio_bank_count) {
+ dev_err(&pdev->dev, "Missing IRQ resource\n");
+ return -ENODEV;
+ }
+
+ tegra_gpio_chip.ngpio = tegra_gpio_bank_count * 32;
+
+ tegra_gpio_banks = devm_kzalloc(&pdev->dev,
+ tegra_gpio_bank_count * sizeof(*tegra_gpio_banks),
+ GFP_KERNEL);
+ if (!tegra_gpio_banks)
+ return -ENODEV;
+
+ irq_domain = irq_domain_add_linear(pdev->dev.of_node,
+ tegra_gpio_chip.ngpio,
+ &irq_domain_simple_ops, NULL);
+ if (!irq_domain)
+ return -ENODEV;
+
+ for (i = 0; i < tegra_gpio_bank_count; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+ if (!res) {
+ dev_err(&pdev->dev, "Missing IRQ resource\n");
+ return -ENODEV;
+ }
+
+ bank = &tegra_gpio_banks[i];
+ bank->bank = i;
+ bank->irq = res->start;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ for (i = 0; i < tegra_gpio_bank_count; i++) {
+ for (j = 0; j < 4; j++) {
+ int gpio = tegra_gpio_compose(i, j, 0);
+ tegra_gpio_writel(0x00, GPIO_INT_ENB(gpio));
+ }
+ }
+
+ tegra_gpio_chip.of_node = pdev->dev.of_node;
+
+ ret = gpiochip_add(&tegra_gpio_chip);
+ if (ret < 0) {
+ irq_domain_remove(irq_domain);
+ return ret;
+ }
+
+ for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) {
+ int irq = irq_create_mapping(irq_domain, gpio);
+ /* No validity check; all Tegra GPIOs are valid IRQs */
+
+ bank = &tegra_gpio_banks[GPIO_BANK(gpio)];
+
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_data(irq, bank);
+ irq_set_chip_and_handler(irq, &tegra_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ for (i = 0; i < tegra_gpio_bank_count; i++) {
+ bank = &tegra_gpio_banks[i];
+
+ irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler);
+ irq_set_handler_data(bank->irq, bank);
+
+ for (j = 0; j < 4; j++)
+ spin_lock_init(&bank->lvl_lock[j]);
+ }
+
+ return 0;
+}
+
+static struct platform_driver tegra_gpio_driver = {
+ .driver = {
+ .name = "tegra-gpio",
+ .pm = &tegra_gpio_pm_ops,
+ .of_match_table = tegra_gpio_of_match,
+ },
+ .probe = tegra_gpio_probe,
+};
+
+static int __init tegra_gpio_init(void)
+{
+ return platform_driver_register(&tegra_gpio_driver);
+}
+postcore_initcall(tegra_gpio_init);
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static int dbg_gpio_show(struct seq_file *s, void *unused)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < tegra_gpio_bank_count; i++) {
+ for (j = 0; j < 4; j++) {
+ int gpio = tegra_gpio_compose(i, j, 0);
+ seq_printf(s,
+ "%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
+ i, j,
+ tegra_gpio_readl(GPIO_CNF(gpio)),
+ tegra_gpio_readl(GPIO_OE(gpio)),
+ tegra_gpio_readl(GPIO_OUT(gpio)),
+ tegra_gpio_readl(GPIO_IN(gpio)),
+ tegra_gpio_readl(GPIO_INT_STA(gpio)),
+ tegra_gpio_readl(GPIO_INT_ENB(gpio)),
+ tegra_gpio_readl(GPIO_INT_LVL(gpio)));
+ }
+ }
+ return 0;
+}
+
+static int dbg_gpio_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_gpio_show, &inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+ .open = dbg_gpio_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_gpio_debuginit(void)
+{
+ (void) debugfs_create_file("tegra_gpio", S_IRUGO,
+ NULL, NULL, &debug_fops);
+ return 0;
+}
+late_initcall(tegra_gpio_debuginit);
+#endif
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c
new file mode 100644
index 000000000..e8f97e03c
--- /dev/null
+++ b/drivers/gpio/gpio-timberdale.c
@@ -0,0 +1,346 @@
+/*
+ * Timberdale FPGA GPIO driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA GPIO
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/timb_gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#define DRIVER_NAME "timb-gpio"
+
+#define TGPIOVAL 0x00
+#define TGPIODIR 0x04
+#define TGPIO_IER 0x08
+#define TGPIO_ISR 0x0c
+#define TGPIO_IPR 0x10
+#define TGPIO_ICR 0x14
+#define TGPIO_FLR 0x18
+#define TGPIO_LVR 0x1c
+#define TGPIO_VER 0x20
+#define TGPIO_BFLR 0x24
+
+struct timbgpio {
+ void __iomem *membase;
+ spinlock_t lock; /* mutual exclusion */
+ struct gpio_chip gpio;
+ int irq_base;
+ unsigned long last_ier;
+};
+
+static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index,
+ unsigned offset, bool enabled)
+{
+ struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
+ u32 reg;
+
+ spin_lock(&tgpio->lock);
+ reg = ioread32(tgpio->membase + offset);
+
+ if (enabled)
+ reg |= (1 << index);
+ else
+ reg &= ~(1 << index);
+
+ iowrite32(reg, tgpio->membase + offset);
+ spin_unlock(&tgpio->lock);
+
+ return 0;
+}
+
+static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ return timbgpio_update_bit(gpio, nr, TGPIODIR, true);
+}
+
+static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+ struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
+ u32 value;
+
+ value = ioread32(tgpio->membase + TGPIOVAL);
+ return (value & (1 << nr)) ? 1 : 0;
+}
+
+static int timbgpio_gpio_direction_output(struct gpio_chip *gpio,
+ unsigned nr, int val)
+{
+ return timbgpio_update_bit(gpio, nr, TGPIODIR, false);
+}
+
+static void timbgpio_gpio_set(struct gpio_chip *gpio,
+ unsigned nr, int val)
+{
+ timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0);
+}
+
+static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
+
+ if (tgpio->irq_base <= 0)
+ return -EINVAL;
+
+ return tgpio->irq_base + offset;
+}
+
+/*
+ * GPIO IRQ
+ */
+static void timbgpio_irq_disable(struct irq_data *d)
+{
+ struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tgpio->irq_base;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tgpio->lock, flags);
+ tgpio->last_ier &= ~(1UL << offset);
+ iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
+ spin_unlock_irqrestore(&tgpio->lock, flags);
+}
+
+static void timbgpio_irq_enable(struct irq_data *d)
+{
+ struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tgpio->irq_base;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tgpio->lock, flags);
+ tgpio->last_ier |= 1UL << offset;
+ iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
+ spin_unlock_irqrestore(&tgpio->lock, flags);
+}
+
+static int timbgpio_irq_type(struct irq_data *d, unsigned trigger)
+{
+ struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tgpio->irq_base;
+ unsigned long flags;
+ u32 lvr, flr, bflr = 0;
+ u32 ver;
+ int ret = 0;
+
+ if (offset < 0 || offset > tgpio->gpio.ngpio)
+ return -EINVAL;
+
+ ver = ioread32(tgpio->membase + TGPIO_VER);
+
+ spin_lock_irqsave(&tgpio->lock, flags);
+
+ lvr = ioread32(tgpio->membase + TGPIO_LVR);
+ flr = ioread32(tgpio->membase + TGPIO_FLR);
+ if (ver > 2)
+ bflr = ioread32(tgpio->membase + TGPIO_BFLR);
+
+ if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+ bflr &= ~(1 << offset);
+ flr &= ~(1 << offset);
+ if (trigger & IRQ_TYPE_LEVEL_HIGH)
+ lvr |= 1 << offset;
+ else
+ lvr &= ~(1 << offset);
+ }
+
+ if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
+ if (ver < 3) {
+ ret = -EINVAL;
+ goto out;
+ } else {
+ flr |= 1 << offset;
+ bflr |= 1 << offset;
+ }
+ } else {
+ bflr &= ~(1 << offset);
+ flr |= 1 << offset;
+ if (trigger & IRQ_TYPE_EDGE_FALLING)
+ lvr &= ~(1 << offset);
+ else
+ lvr |= 1 << offset;
+ }
+
+ iowrite32(lvr, tgpio->membase + TGPIO_LVR);
+ iowrite32(flr, tgpio->membase + TGPIO_FLR);
+ if (ver > 2)
+ iowrite32(bflr, tgpio->membase + TGPIO_BFLR);
+
+ iowrite32(1 << offset, tgpio->membase + TGPIO_ICR);
+
+out:
+ spin_unlock_irqrestore(&tgpio->lock, flags);
+ return ret;
+}
+
+static void timbgpio_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct timbgpio *tgpio = irq_get_handler_data(irq);
+ unsigned long ipr;
+ int offset;
+
+ desc->irq_data.chip->irq_ack(irq_get_irq_data(irq));
+ ipr = ioread32(tgpio->membase + TGPIO_IPR);
+ iowrite32(ipr, tgpio->membase + TGPIO_ICR);
+
+ /*
+ * Some versions of the hardware trash the IER register if more than
+ * one interrupt is received simultaneously.
+ */
+ iowrite32(0, tgpio->membase + TGPIO_IER);
+
+ for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio)
+ generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset));
+
+ iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
+}
+
+static struct irq_chip timbgpio_irqchip = {
+ .name = "GPIO",
+ .irq_enable = timbgpio_irq_enable,
+ .irq_disable = timbgpio_irq_disable,
+ .irq_set_type = timbgpio_irq_type,
+};
+
+static int timbgpio_probe(struct platform_device *pdev)
+{
+ int err, i;
+ struct device *dev = &pdev->dev;
+ struct gpio_chip *gc;
+ struct timbgpio *tgpio;
+ struct resource *iomem;
+ struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (!pdata || pdata->nr_pins > 32) {
+ dev_err(dev, "Invalid platform data\n");
+ return -EINVAL;
+ }
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iomem) {
+ dev_err(dev, "Unable to get resource\n");
+ return -EINVAL;
+ }
+
+ tgpio = devm_kzalloc(dev, sizeof(struct timbgpio), GFP_KERNEL);
+ if (!tgpio) {
+ dev_err(dev, "Memory alloc failed\n");
+ return -EINVAL;
+ }
+ tgpio->irq_base = pdata->irq_base;
+
+ spin_lock_init(&tgpio->lock);
+
+ if (!devm_request_mem_region(dev, iomem->start, resource_size(iomem),
+ DRIVER_NAME)) {
+ dev_err(dev, "Region already claimed\n");
+ return -EBUSY;
+ }
+
+ tgpio->membase = devm_ioremap(dev, iomem->start, resource_size(iomem));
+ if (!tgpio->membase) {
+ dev_err(dev, "Cannot ioremap\n");
+ return -ENOMEM;
+ }
+
+ gc = &tgpio->gpio;
+
+ gc->label = dev_name(&pdev->dev);
+ gc->owner = THIS_MODULE;
+ gc->dev = &pdev->dev;
+ gc->direction_input = timbgpio_gpio_direction_input;
+ gc->get = timbgpio_gpio_get;
+ gc->direction_output = timbgpio_gpio_direction_output;
+ gc->set = timbgpio_gpio_set;
+ gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL;
+ gc->dbg_show = NULL;
+ gc->base = pdata->gpio_base;
+ gc->ngpio = pdata->nr_pins;
+ gc->can_sleep = false;
+
+ err = gpiochip_add(gc);
+ if (err)
+ return err;
+
+ platform_set_drvdata(pdev, tgpio);
+
+ /* make sure to disable interrupts */
+ iowrite32(0x0, tgpio->membase + TGPIO_IER);
+
+ if (irq < 0 || tgpio->irq_base <= 0)
+ return 0;
+
+ for (i = 0; i < pdata->nr_pins; i++) {
+ irq_set_chip_and_handler(tgpio->irq_base + i,
+ &timbgpio_irqchip, handle_simple_irq);
+ irq_set_chip_data(tgpio->irq_base + i, tgpio);
+#ifdef CONFIG_ARM
+ set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE);
+#endif
+ }
+
+ irq_set_handler_data(irq, tgpio);
+ irq_set_chained_handler(irq, timbgpio_irq);
+
+ return 0;
+}
+
+static int timbgpio_remove(struct platform_device *pdev)
+{
+ struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct timbgpio *tgpio = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (irq >= 0 && tgpio->irq_base > 0) {
+ int i;
+ for (i = 0; i < pdata->nr_pins; i++) {
+ irq_set_chip(tgpio->irq_base + i, NULL);
+ irq_set_chip_data(tgpio->irq_base + i, NULL);
+ }
+
+ irq_set_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ }
+
+ gpiochip_remove(&tgpio->gpio);
+
+ return 0;
+}
+
+static struct platform_driver timbgpio_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = timbgpio_probe,
+ .remove = timbgpio_remove,
+};
+
+/*--------------------------------------------------------------------------*/
+
+module_platform_driver(timbgpio_platform_driver);
+
+MODULE_DESCRIPTION("Timberdale GPIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Mocean Laboratories");
+MODULE_ALIAS("platform:"DRIVER_NAME);
+
diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c
new file mode 100644
index 000000000..9c9238e83
--- /dev/null
+++ b/drivers/gpio/gpio-tps6586x.c
@@ -0,0 +1,166 @@
+/*
+ * TI TPS6586x GPIO driver
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * Based on tps6586x.c
+ * Copyright (c) 2010 CompuLab Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/tps6586x.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+/* GPIO control registers */
+#define TPS6586X_GPIOSET1 0x5d
+#define TPS6586X_GPIOSET2 0x5e
+
+struct tps6586x_gpio {
+ struct gpio_chip gpio_chip;
+ struct device *parent;
+};
+
+static inline struct tps6586x_gpio *to_tps6586x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tps6586x_gpio, gpio_chip);
+}
+
+static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+ uint8_t val;
+ int ret;
+
+ ret = tps6586x_read(tps6586x_gpio->parent, TPS6586X_GPIOSET2, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & (1 << offset));
+}
+
+static void tps6586x_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+
+ tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2,
+ value << offset, 1 << offset);
+}
+
+static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+ uint8_t val, mask;
+
+ tps6586x_gpio_set(gc, offset, value);
+
+ val = 0x1 << (offset * 2);
+ mask = 0x3 << (offset * 2);
+
+ return tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET1,
+ val, mask);
+}
+
+static int tps6586x_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+
+ return tps6586x_irq_get_virq(tps6586x_gpio->parent,
+ TPS6586X_INT_PLDO_0 + offset);
+}
+
+static int tps6586x_gpio_probe(struct platform_device *pdev)
+{
+ struct tps6586x_platform_data *pdata;
+ struct tps6586x_gpio *tps6586x_gpio;
+ int ret;
+
+ pdata = dev_get_platdata(pdev->dev.parent);
+ tps6586x_gpio = devm_kzalloc(&pdev->dev,
+ sizeof(*tps6586x_gpio), GFP_KERNEL);
+ if (!tps6586x_gpio)
+ return -ENOMEM;
+
+ tps6586x_gpio->parent = pdev->dev.parent;
+
+ tps6586x_gpio->gpio_chip.owner = THIS_MODULE;
+ tps6586x_gpio->gpio_chip.label = pdev->name;
+ tps6586x_gpio->gpio_chip.dev = &pdev->dev;
+ tps6586x_gpio->gpio_chip.ngpio = 4;
+ tps6586x_gpio->gpio_chip.can_sleep = true;
+
+ /* FIXME: add handling of GPIOs as dedicated inputs */
+ tps6586x_gpio->gpio_chip.direction_output = tps6586x_gpio_output;
+ tps6586x_gpio->gpio_chip.set = tps6586x_gpio_set;
+ 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 = gpiochip_add(&tps6586x_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, tps6586x_gpio);
+
+ return ret;
+}
+
+static int tps6586x_gpio_remove(struct platform_device *pdev)
+{
+ struct tps6586x_gpio *tps6586x_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&tps6586x_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver tps6586x_gpio_driver = {
+ .driver.name = "tps6586x-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = tps6586x_gpio_probe,
+ .remove = tps6586x_gpio_remove,
+};
+
+static int __init tps6586x_gpio_init(void)
+{
+ return platform_driver_register(&tps6586x_gpio_driver);
+}
+subsys_initcall(tps6586x_gpio_init);
+
+static void __exit tps6586x_gpio_exit(void)
+{
+ platform_driver_unregister(&tps6586x_gpio_driver);
+}
+module_exit(tps6586x_gpio_exit);
+
+MODULE_ALIAS("platform:tps6586x-gpio");
+MODULE_DESCRIPTION("GPIO interface for TPS6586X PMIC");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c
new file mode 100644
index 000000000..88f1f5ff4
--- /dev/null
+++ b/drivers/gpio/gpio-tps65910.c
@@ -0,0 +1,220 @@
+/*
+ * TI TPS6591x GPIO driver
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria jedu@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/tps65910.h>
+#include <linux/of_device.h>
+
+struct tps65910_gpio {
+ struct gpio_chip gpio_chip;
+ struct tps65910 *tps65910;
+};
+
+static inline struct tps65910_gpio *to_tps65910_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tps65910_gpio, gpio_chip);
+}
+
+static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
+ struct tps65910 *tps65910 = tps65910_gpio->tps65910;
+ unsigned int val;
+
+ tps65910_reg_read(tps65910, TPS65910_GPIO0 + offset, &val);
+
+ if (val & GPIO_STS_MASK)
+ return 1;
+
+ return 0;
+}
+
+static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
+ struct tps65910 *tps65910 = tps65910_gpio->tps65910;
+
+ if (value)
+ tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
+ GPIO_SET_MASK);
+ else
+ tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
+ GPIO_SET_MASK);
+}
+
+static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
+ struct tps65910 *tps65910 = tps65910_gpio->tps65910;
+
+ /* Set the initial value */
+ tps65910_gpio_set(gc, offset, value);
+
+ return tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
+ GPIO_CFG_MASK);
+}
+
+static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
+ struct tps65910 *tps65910 = tps65910_gpio->tps65910;
+
+ return tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
+ GPIO_CFG_MASK);
+}
+
+#ifdef CONFIG_OF
+static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
+ struct tps65910 *tps65910, int chip_ngpio)
+{
+ struct tps65910_board *tps65910_board = tps65910->of_plat_data;
+ unsigned int prop_array[TPS6591X_MAX_NUM_GPIO];
+ int ngpio = min(chip_ngpio, TPS6591X_MAX_NUM_GPIO);
+ int ret;
+ int idx;
+
+ tps65910_board->gpio_base = -1;
+ ret = of_property_read_u32_array(tps65910->dev->of_node,
+ "ti,en-gpio-sleep", prop_array, ngpio);
+ if (ret < 0) {
+ dev_dbg(dev, "ti,en-gpio-sleep not specified\n");
+ return tps65910_board;
+ }
+
+ for (idx = 0; idx < ngpio; idx++)
+ tps65910_board->en_gpio_sleep[idx] = (prop_array[idx] != 0);
+
+ return tps65910_board;
+}
+#else
+static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
+ struct tps65910 *tps65910, int chip_ngpio)
+{
+ return NULL;
+}
+#endif
+
+static int tps65910_gpio_probe(struct platform_device *pdev)
+{
+ struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
+ struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
+ struct tps65910_gpio *tps65910_gpio;
+ int ret;
+ int i;
+
+ tps65910_gpio = devm_kzalloc(&pdev->dev,
+ sizeof(*tps65910_gpio), GFP_KERNEL);
+ if (!tps65910_gpio)
+ return -ENOMEM;
+
+ tps65910_gpio->tps65910 = tps65910;
+
+ tps65910_gpio->gpio_chip.owner = THIS_MODULE;
+ tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name;
+
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65910:
+ tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO;
+ break;
+ case TPS65911:
+ tps65910_gpio->gpio_chip.ngpio = TPS65911_NUM_GPIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+ tps65910_gpio->gpio_chip.can_sleep = true;
+ tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input;
+ tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output;
+ tps65910_gpio->gpio_chip.set = tps65910_gpio_set;
+ tps65910_gpio->gpio_chip.get = tps65910_gpio_get;
+ tps65910_gpio->gpio_chip.dev = &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
+ tps65910_gpio->gpio_chip.base = -1;
+
+ if (!pdata && tps65910->dev->of_node)
+ pdata = tps65910_parse_dt_for_gpio(&pdev->dev, tps65910,
+ tps65910_gpio->gpio_chip.ngpio);
+
+ if (!pdata)
+ goto skip_init;
+
+ /* Configure sleep control for gpios if provided */
+ for (i = 0; i < tps65910_gpio->gpio_chip.ngpio; ++i) {
+ if (!pdata->en_gpio_sleep[i])
+ continue;
+
+ ret = tps65910_reg_set_bits(tps65910,
+ TPS65910_GPIO0 + i, GPIO_SLEEP_MASK);
+ if (ret < 0)
+ dev_warn(tps65910->dev,
+ "GPIO Sleep setting failed with err %d\n", ret);
+ }
+
+skip_init:
+ ret = gpiochip_add(&tps65910_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, tps65910_gpio);
+
+ return ret;
+}
+
+static int tps65910_gpio_remove(struct platform_device *pdev)
+{
+ struct tps65910_gpio *tps65910_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&tps65910_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver tps65910_gpio_driver = {
+ .driver.name = "tps65910-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = tps65910_gpio_probe,
+ .remove = tps65910_gpio_remove,
+};
+
+static int __init tps65910_gpio_init(void)
+{
+ return platform_driver_register(&tps65910_gpio_driver);
+}
+subsys_initcall(tps65910_gpio_init);
+
+static void __exit tps65910_gpio_exit(void)
+{
+ platform_driver_unregister(&tps65910_gpio_driver);
+}
+module_exit(tps65910_gpio_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_AUTHOR("Jorge Eduardo Candelaria jedu@slimlogic.co.uk>");
+MODULE_DESCRIPTION("GPIO interface for TPS65910/TPS6511 PMICs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65910-gpio");
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
new file mode 100644
index 000000000..9cdbc0c9c
--- /dev/null
+++ b/drivers/gpio/gpio-tps65912.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/mfd/tps65912.h>
+
+struct tps65912_gpio_data {
+ struct tps65912 *tps65912;
+ struct gpio_chip gpio_chip;
+};
+
+#define to_tgd(gc) container_of(gc, struct tps65912_gpio_data, gpio_chip)
+
+static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc);
+ struct tps65912 *tps65912 = tps65912_gpio->tps65912;
+ int val;
+
+ val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset);
+
+ if (val & GPIO_STS_MASK)
+ return 1;
+
+ return 0;
+}
+
+static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc);
+ struct tps65912 *tps65912 = tps65912_gpio->tps65912;
+
+ if (value)
+ tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_SET_MASK);
+ else
+ tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_SET_MASK);
+}
+
+static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc);
+ struct tps65912 *tps65912 = tps65912_gpio->tps65912;
+
+ /* Set the initial value */
+ tps65912_gpio_set(gc, offset, value);
+
+ return tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_CFG_MASK);
+}
+
+static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc);
+ struct tps65912 *tps65912 = tps65912_gpio->tps65912;
+
+ return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_CFG_MASK);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "tps65912",
+ .owner = THIS_MODULE,
+ .direction_input = tps65912_gpio_input,
+ .direction_output = tps65912_gpio_output,
+ .get = tps65912_gpio_get,
+ .set = tps65912_gpio_set,
+ .can_sleep = true,
+ .ngpio = 5,
+ .base = -1,
+};
+
+static int tps65912_gpio_probe(struct platform_device *pdev)
+{
+ struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
+ struct tps65912_board *pdata = dev_get_platdata(tps65912->dev);
+ struct tps65912_gpio_data *tps65912_gpio;
+ int ret;
+
+ tps65912_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65912_gpio),
+ GFP_KERNEL);
+ if (tps65912_gpio == NULL)
+ return -ENOMEM;
+
+ tps65912_gpio->tps65912 = tps65912;
+ tps65912_gpio->gpio_chip = template_chip;
+ tps65912_gpio->gpio_chip.dev = &pdev->dev;
+ if (pdata && pdata->gpio_base)
+ tps65912_gpio->gpio_chip.base = pdata->gpio_base;
+
+ ret = gpiochip_add(&tps65912_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, tps65912_gpio);
+
+ return ret;
+}
+
+static int tps65912_gpio_remove(struct platform_device *pdev)
+{
+ struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&tps65912_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver tps65912_gpio_driver = {
+ .driver = {
+ .name = "tps65912-gpio",
+ },
+ .probe = tps65912_gpio_probe,
+ .remove = tps65912_gpio_remove,
+};
+
+static int __init tps65912_gpio_init(void)
+{
+ return platform_driver_register(&tps65912_gpio_driver);
+}
+subsys_initcall(tps65912_gpio_init);
+
+static void __exit tps65912_gpio_exit(void)
+{
+ platform_driver_unregister(&tps65912_gpio_driver);
+}
+module_exit(tps65912_gpio_exit);
+
+MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65912-gpio");
diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c
new file mode 100644
index 000000000..92fbabd82
--- /dev/null
+++ b/drivers/gpio/gpio-ts5500.c
@@ -0,0 +1,465 @@
+/*
+ * Digital I/O driver for Technologic Systems TS-5500
+ *
+ * Copyright (c) 2012 Savoir-faire Linux Inc.
+ * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *
+ * Technologic Systems platforms have pin blocks, exposing several Digital
+ * Input/Output lines (DIO). This driver aims to support single pin blocks.
+ * In that sense, the support is not limited to the TS-5500 blocks.
+ * Actually, the following platforms have DIO support:
+ *
+ * TS-5500:
+ * Documentation: http://wiki.embeddedarm.com/wiki/TS-5500
+ * Blocks: DIO1, DIO2 and LCD port.
+ *
+ * TS-5600:
+ * Documentation: http://wiki.embeddedarm.com/wiki/TS-5600
+ * Blocks: LCD port (identical to TS-5500 LCD).
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_data/gpio-ts5500.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* List of supported Technologic Systems platforms DIO blocks */
+enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
+
+struct ts5500_priv {
+ const struct ts5500_dio *pinout;
+ struct gpio_chip gpio_chip;
+ spinlock_t lock;
+ bool strap;
+ u8 hwirq;
+};
+
+/*
+ * Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
+ * This flag ensures that the region has been requested by this driver.
+ */
+static bool hex7d_reserved;
+
+/*
+ * This structure is used to describe capabilities of DIO lines,
+ * such as available directions and connected interrupt (if any).
+ */
+struct ts5500_dio {
+ const u8 value_addr;
+ const u8 value_mask;
+ const u8 control_addr;
+ const u8 control_mask;
+ const bool no_input;
+ const bool no_output;
+ const u8 irq;
+};
+
+#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \
+ { \
+ .value_addr = vaddr, \
+ .value_mask = BIT(vbit), \
+ .control_addr = caddr, \
+ .control_mask = BIT(cbit), \
+ }
+
+#define TS5500_DIO_IN(addr, bit) \
+ { \
+ .value_addr = addr, \
+ .value_mask = BIT(bit), \
+ .no_output = true, \
+ }
+
+#define TS5500_DIO_IN_IRQ(addr, bit, _irq) \
+ { \
+ .value_addr = addr, \
+ .value_mask = BIT(bit), \
+ .no_output = true, \
+ .irq = _irq, \
+ }
+
+#define TS5500_DIO_OUT(addr, bit) \
+ { \
+ .value_addr = addr, \
+ .value_mask = BIT(bit), \
+ .no_input = true, \
+ }
+
+/*
+ * Input/Output DIO lines are programmed in groups of 4. Their values are
+ * available through 4 consecutive bits in a value port, whereas the direction
+ * of these 4 lines is driven by only 1 bit in a control port.
+ */
+#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit) \
+ TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit), \
+ TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit), \
+ TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit), \
+ TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
+
+/*
+ * TS-5500 DIO1 block
+ *
+ * value control dir hw
+ * addr bit addr bit in out irq name pin offset
+ *
+ * 0x7b 0 0x7a 0 x x DIO1_0 1 0
+ * 0x7b 1 0x7a 0 x x DIO1_1 3 1
+ * 0x7b 2 0x7a 0 x x DIO1_2 5 2
+ * 0x7b 3 0x7a 0 x x DIO1_3 7 3
+ * 0x7b 4 0x7a 1 x x DIO1_4 9 4
+ * 0x7b 5 0x7a 1 x x DIO1_5 11 5
+ * 0x7b 6 0x7a 1 x x DIO1_6 13 6
+ * 0x7b 7 0x7a 1 x x DIO1_7 15 7
+ * 0x7c 0 0x7a 5 x x DIO1_8 4 8
+ * 0x7c 1 0x7a 5 x x DIO1_9 6 9
+ * 0x7c 2 0x7a 5 x x DIO1_10 8 10
+ * 0x7c 3 0x7a 5 x x DIO1_11 10 11
+ * 0x7c 4 x DIO1_12 12 12
+ * 0x7c 5 x 7 DIO1_13 14 13
+ */
+static const struct ts5500_dio ts5500_dio1[] = {
+ TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
+ TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
+ TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
+ TS5500_DIO_IN(0x7c, 4),
+ TS5500_DIO_IN_IRQ(0x7c, 5, 7),
+};
+
+/*
+ * TS-5500 DIO2 block
+ *
+ * value control dir hw
+ * addr bit addr bit in out irq name pin offset
+ *
+ * 0x7e 0 0x7d 0 x x DIO2_0 1 0
+ * 0x7e 1 0x7d 0 x x DIO2_1 3 1
+ * 0x7e 2 0x7d 0 x x DIO2_2 5 2
+ * 0x7e 3 0x7d 0 x x DIO2_3 7 3
+ * 0x7e 4 0x7d 1 x x DIO2_4 9 4
+ * 0x7e 5 0x7d 1 x x DIO2_5 11 5
+ * 0x7e 6 0x7d 1 x x DIO2_6 13 6
+ * 0x7e 7 0x7d 1 x x DIO2_7 15 7
+ * 0x7f 0 0x7d 5 x x DIO2_8 4 8
+ * 0x7f 1 0x7d 5 x x DIO2_9 6 9
+ * 0x7f 2 0x7d 5 x x DIO2_10 8 10
+ * 0x7f 3 0x7d 5 x x DIO2_11 10 11
+ * 0x7f 4 x 6 DIO2_13 14 12
+ */
+static const struct ts5500_dio ts5500_dio2[] = {
+ TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
+ TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
+ TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
+ TS5500_DIO_IN_IRQ(0x7f, 4, 6),
+};
+
+/*
+ * TS-5500 LCD port used as DIO block
+ * TS-5600 LCD port is identical
+ *
+ * value control dir hw
+ * addr bit addr bit in out irq name pin offset
+ *
+ * 0x72 0 0x7d 2 x x LCD_0 8 0
+ * 0x72 1 0x7d 2 x x LCD_1 7 1
+ * 0x72 2 0x7d 2 x x LCD_2 10 2
+ * 0x72 3 0x7d 2 x x LCD_3 9 3
+ * 0x72 4 0x7d 3 x x LCD_4 12 4
+ * 0x72 5 0x7d 3 x x LCD_5 11 5
+ * 0x72 6 0x7d 3 x x LCD_6 14 6
+ * 0x72 7 0x7d 3 x x LCD_7 13 7
+ * 0x73 0 x LCD_EN 5 8
+ * 0x73 6 x LCD_WR 6 9
+ * 0x73 7 x 1 LCD_RS 3 10
+ */
+static const struct ts5500_dio ts5500_lcd[] = {
+ TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
+ TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
+ TS5500_DIO_OUT(0x73, 0),
+ TS5500_DIO_IN(0x73, 6),
+ TS5500_DIO_IN_IRQ(0x73, 7, 1),
+};
+
+static inline struct ts5500_priv *ts5500_gc_to_priv(struct gpio_chip *chip)
+{
+ return container_of(chip, struct ts5500_priv, gpio_chip);
+}
+
+static inline void ts5500_set_mask(u8 mask, u8 addr)
+{
+ u8 val = inb(addr);
+ val |= mask;
+ outb(val, addr);
+}
+
+static inline void ts5500_clear_mask(u8 mask, u8 addr)
+{
+ u8 val = inb(addr);
+ val &= ~mask;
+ outb(val, addr);
+}
+
+static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+ const struct ts5500_dio line = priv->pinout[offset];
+ unsigned long flags;
+
+ if (line.no_input)
+ return -ENXIO;
+
+ if (line.no_output)
+ return 0;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ ts5500_clear_mask(line.control_mask, line.control_addr);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+ const struct ts5500_dio line = priv->pinout[offset];
+
+ return !!(inb(line.value_addr) & line.value_mask);
+}
+
+static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+ const struct ts5500_dio line = priv->pinout[offset];
+ unsigned long flags;
+
+ if (line.no_output)
+ return -ENXIO;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (!line.no_input)
+ ts5500_set_mask(line.control_mask, line.control_addr);
+
+ if (val)
+ ts5500_set_mask(line.value_mask, line.value_addr);
+ else
+ ts5500_clear_mask(line.value_mask, line.value_addr);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+ const struct ts5500_dio line = priv->pinout[offset];
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (val)
+ ts5500_set_mask(line.value_mask, line.value_addr);
+ else
+ ts5500_clear_mask(line.value_mask, line.value_addr);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+ const struct ts5500_dio *block = priv->pinout;
+ const struct ts5500_dio line = block[offset];
+
+ /* Only one pin is connected to an interrupt */
+ if (line.irq)
+ return line.irq;
+
+ /* As this pin is input-only, we may strap it to another in/out pin */
+ if (priv->strap)
+ return priv->hwirq;
+
+ return -ENXIO;
+}
+
+static int ts5500_enable_irq(struct ts5500_priv *priv)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->hwirq == 7)
+ ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
+ else if (priv->hwirq == 6)
+ ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
+ else if (priv->hwirq == 1)
+ ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
+ else
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static void ts5500_disable_irq(struct ts5500_priv *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->hwirq == 7)
+ ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
+ else if (priv->hwirq == 6)
+ ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
+ else if (priv->hwirq == 1)
+ ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
+ else
+ dev_err(priv->gpio_chip.dev, "invalid hwirq %d\n", priv->hwirq);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int ts5500_dio_probe(struct platform_device *pdev)
+{
+ enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
+ struct ts5500_dio_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ 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;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+ priv->hwirq = res->start;
+ spin_lock_init(&priv->lock);
+
+ priv->gpio_chip.owner = THIS_MODULE;
+ priv->gpio_chip.label = name;
+ priv->gpio_chip.dev = dev;
+ priv->gpio_chip.direction_input = ts5500_gpio_input;
+ priv->gpio_chip.direction_output = ts5500_gpio_output;
+ priv->gpio_chip.get = ts5500_gpio_get;
+ priv->gpio_chip.set = ts5500_gpio_set;
+ priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
+ priv->gpio_chip.base = -1;
+ if (pdata) {
+ priv->gpio_chip.base = pdata->base;
+ priv->strap = pdata->strap;
+ }
+
+ switch (block) {
+ case TS5500_DIO1:
+ priv->pinout = ts5500_dio1;
+ priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
+
+ if (!devm_request_region(dev, 0x7a, 3, name)) {
+ dev_err(dev, "failed to request %s ports\n", name);
+ return -EBUSY;
+ }
+ break;
+ case TS5500_DIO2:
+ priv->pinout = ts5500_dio2;
+ priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
+
+ if (!devm_request_region(dev, 0x7e, 2, name)) {
+ dev_err(dev, "failed to request %s ports\n", name);
+ return -EBUSY;
+ }
+
+ if (hex7d_reserved)
+ break;
+
+ if (!devm_request_region(dev, 0x7d, 1, name)) {
+ dev_err(dev, "failed to request %s 7D\n", name);
+ return -EBUSY;
+ }
+
+ hex7d_reserved = true;
+ break;
+ case TS5500_LCD:
+ case TS5600_LCD:
+ priv->pinout = ts5500_lcd;
+ priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
+
+ if (!devm_request_region(dev, 0x72, 2, name)) {
+ dev_err(dev, "failed to request %s ports\n", name);
+ return -EBUSY;
+ }
+
+ if (!hex7d_reserved) {
+ if (!devm_request_region(dev, 0x7d, 1, name)) {
+ dev_err(dev, "failed to request %s 7D\n", name);
+ return -EBUSY;
+ }
+
+ hex7d_reserved = true;
+ }
+
+ /* Ensure usage of LCD port as DIO */
+ spin_lock_irqsave(&priv->lock, flags);
+ ts5500_clear_mask(BIT(4), 0x7d);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ break;
+ }
+
+ ret = gpiochip_add(&priv->gpio_chip);
+ if (ret) {
+ dev_err(dev, "failed to register the gpio chip\n");
+ return ret;
+ }
+
+ ret = ts5500_enable_irq(priv);
+ if (ret) {
+ dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
+ goto cleanup;
+ }
+
+ return 0;
+cleanup:
+ gpiochip_remove(&priv->gpio_chip);
+ return ret;
+}
+
+static int ts5500_dio_remove(struct platform_device *pdev)
+{
+ struct ts5500_priv *priv = platform_get_drvdata(pdev);
+
+ ts5500_disable_irq(priv);
+ gpiochip_remove(&priv->gpio_chip);
+ return 0;
+}
+
+static struct platform_device_id ts5500_dio_ids[] = {
+ { "ts5500-dio1", TS5500_DIO1 },
+ { "ts5500-dio2", TS5500_DIO2 },
+ { "ts5500-dio-lcd", TS5500_LCD },
+ { "ts5600-dio-lcd", TS5600_LCD },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
+
+static struct platform_driver ts5500_dio_driver = {
+ .driver = {
+ .name = "ts5500-dio",
+ },
+ .probe = ts5500_dio_probe,
+ .remove = ts5500_dio_remove,
+ .id_table = ts5500_dio_ids,
+};
+
+module_platform_driver(ts5500_dio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
+MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
new file mode 100644
index 000000000..9e1dbb987
--- /dev/null
+++ b/drivers/gpio/gpio-twl4030.c
@@ -0,0 +1,628 @@
+/*
+ * Access to GPIOs on TWL4030/TPS659x0 chips
+ *
+ * Copyright (C) 2006-2007 Texas Instruments, Inc.
+ * Copyright (C) 2006 MontaVista Software, Inc.
+ *
+ * Code re-arranged and cleaned up by:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ *
+ * Initial Code:
+ * Andy Lowe / Nishanth Menon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
+
+#include <linux/i2c/twl.h>
+
+/*
+ * The GPIO "subchip" supports 18 GPIOs which can be configured as
+ * inputs or outputs, with pullups or pulldowns on each pin. Each
+ * GPIO can trigger interrupts on either or both edges.
+ *
+ * GPIO interrupts can be fed to either of two IRQ lines; this is
+ * intended to support multiple hosts.
+ *
+ * There are also two LED pins used sometimes as output-only GPIOs.
+ */
+
+/* genirq interfaces are not available to modules */
+#ifdef MODULE
+#define is_module() true
+#else
+#define is_module() false
+#endif
+
+/* GPIO_CTRL Fields */
+#define MASK_GPIO_CTRL_GPIO0CD1 BIT(0)
+#define MASK_GPIO_CTRL_GPIO1CD2 BIT(1)
+#define MASK_GPIO_CTRL_GPIO_ON BIT(2)
+
+/* Mask for GPIO registers when aggregated into a 32-bit integer */
+#define GPIO_32_MASK 0x0003ffff
+
+struct gpio_twl4030_priv {
+ struct gpio_chip gpio_chip;
+ struct mutex mutex;
+ int irq_base;
+
+ /* Bitfields for state caching */
+ unsigned int usage_count;
+ unsigned int direction;
+ unsigned int out_state;
+};
+
+/*----------------------------------------------------------------------*/
+
+static inline struct gpio_twl4030_priv *to_gpio_twl4030(struct gpio_chip *chip)
+{
+ return container_of(chip, struct gpio_twl4030_priv, gpio_chip);
+}
+
+/*
+ * To configure TWL4030 GPIO module registers
+ */
+static inline int gpio_twl4030_write(u8 address, u8 data)
+{
+ return twl_i2c_write_u8(TWL4030_MODULE_GPIO, data, address);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * LED register offsets from TWL_MODULE_LED base
+ * PWMs A and B are dedicated to LEDs A and B, respectively.
+ */
+
+#define TWL4030_LED_LEDEN_REG 0x00
+#define TWL4030_PWMAON_REG 0x01
+#define TWL4030_PWMAOFF_REG 0x02
+#define TWL4030_PWMBON_REG 0x03
+#define TWL4030_PWMBOFF_REG 0x04
+
+/* LEDEN bits */
+#define LEDEN_LEDAON BIT(0)
+#define LEDEN_LEDBON BIT(1)
+#define LEDEN_LEDAEXT BIT(2)
+#define LEDEN_LEDBEXT BIT(3)
+#define LEDEN_LEDAPWM BIT(4)
+#define LEDEN_LEDBPWM BIT(5)
+#define LEDEN_PWM_LENGTHA BIT(6)
+#define LEDEN_PWM_LENGTHB BIT(7)
+
+#define PWMxON_LENGTH BIT(7)
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * To read a TWL4030 GPIO module register
+ */
+static inline int gpio_twl4030_read(u8 address)
+{
+ u8 data;
+ int ret = 0;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address);
+ return (ret < 0) ? ret : data;
+}
+
+/*----------------------------------------------------------------------*/
+
+static u8 cached_leden;
+
+/* The LED lines are open drain outputs ... a FET pulls to GND, so an
+ * external pullup is needed. We could also expose the integrated PWM
+ * as a LED brightness control; we initialize it as "always on".
+ */
+static void twl4030_led_set_value(int led, int value)
+{
+ u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM;
+
+ if (led)
+ mask <<= 1;
+
+ if (value)
+ cached_leden &= ~mask;
+ else
+ cached_leden |= mask;
+
+ WARN_ON_ONCE(twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
+ TWL4030_LED_LEDEN_REG));
+}
+
+static int twl4030_set_gpio_direction(int gpio, int is_input)
+{
+ u8 d_bnk = gpio >> 3;
+ u8 d_msk = BIT(gpio & 0x7);
+ u8 reg = 0;
+ u8 base = REG_GPIODATADIR1 + d_bnk;
+ int ret = 0;
+
+ ret = gpio_twl4030_read(base);
+ if (ret >= 0) {
+ if (is_input)
+ reg = ret & ~d_msk;
+ else
+ reg = ret | d_msk;
+
+ ret = gpio_twl4030_write(base, reg);
+ }
+ return ret;
+}
+
+static int twl4030_set_gpio_dataout(int gpio, int enable)
+{
+ u8 d_bnk = gpio >> 3;
+ u8 d_msk = BIT(gpio & 0x7);
+ u8 base = 0;
+
+ if (enable)
+ base = REG_SETGPIODATAOUT1 + d_bnk;
+ else
+ base = REG_CLEARGPIODATAOUT1 + d_bnk;
+
+ return gpio_twl4030_write(base, d_msk);
+}
+
+static int twl4030_get_gpio_datain(int gpio)
+{
+ u8 d_bnk = gpio >> 3;
+ u8 d_off = gpio & 0x7;
+ u8 base = 0;
+ int ret = 0;
+
+ base = REG_GPIODATAIN1 + d_bnk;
+ ret = gpio_twl4030_read(base);
+ if (ret > 0)
+ ret = (ret >> d_off) & 0x1;
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int twl_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
+ int status = 0;
+
+ mutex_lock(&priv->mutex);
+
+ /* Support the two LED outputs as output-only GPIOs. */
+ if (offset >= TWL4030_GPIO_MAX) {
+ u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT
+ | LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA;
+ u8 reg = TWL4030_PWMAON_REG;
+
+ offset -= TWL4030_GPIO_MAX;
+ if (offset) {
+ ledclr_mask <<= 1;
+ reg = TWL4030_PWMBON_REG;
+ }
+
+ /* initialize PWM to always-drive */
+ /* Configure PWM OFF register first */
+ status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg + 1);
+ if (status < 0)
+ goto done;
+
+ /* Followed by PWM ON register */
+ status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg);
+ if (status < 0)
+ goto done;
+
+ /* init LED to not-driven (high) */
+ status = twl_i2c_read_u8(TWL4030_MODULE_LED, &cached_leden,
+ TWL4030_LED_LEDEN_REG);
+ if (status < 0)
+ goto done;
+ cached_leden &= ~ledclr_mask;
+ status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
+ TWL4030_LED_LEDEN_REG);
+ if (status < 0)
+ goto done;
+
+ status = 0;
+ goto done;
+ }
+
+ /* on first use, turn GPIO module "on" */
+ if (!priv->usage_count) {
+ struct twl4030_gpio_platform_data *pdata;
+ u8 value = MASK_GPIO_CTRL_GPIO_ON;
+
+ /* optionally have the first two GPIOs switch vMMC1
+ * and vMMC2 power supplies based on card presence.
+ */
+ pdata = dev_get_platdata(chip->dev);
+ if (pdata)
+ value |= pdata->mmc_cd & 0x03;
+
+ status = gpio_twl4030_write(REG_GPIO_CTRL, value);
+ }
+
+done:
+ if (!status)
+ priv->usage_count |= BIT(offset);
+
+ mutex_unlock(&priv->mutex);
+ return status;
+}
+
+static void twl_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
+
+ mutex_lock(&priv->mutex);
+ if (offset >= TWL4030_GPIO_MAX) {
+ twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1);
+ goto out;
+ }
+
+ priv->usage_count &= ~BIT(offset);
+
+ /* on last use, switch off GPIO module */
+ if (!priv->usage_count)
+ gpio_twl4030_write(REG_GPIO_CTRL, 0x0);
+
+out:
+ mutex_unlock(&priv->mutex);
+}
+
+static int twl_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ if (offset < TWL4030_GPIO_MAX)
+ ret = twl4030_set_gpio_direction(offset, 1);
+ else
+ ret = -EINVAL; /* LED outputs can't be set as input */
+
+ if (!ret)
+ priv->direction &= ~BIT(offset);
+
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+
+static int twl_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
+ int ret;
+ int status = 0;
+
+ mutex_lock(&priv->mutex);
+ if (!(priv->usage_count & BIT(offset))) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (priv->direction & BIT(offset))
+ status = priv->out_state & BIT(offset);
+ else
+ status = twl4030_get_gpio_datain(offset);
+
+ ret = (status <= 0) ? 0 : 1;
+out:
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static void twl_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
+
+ mutex_lock(&priv->mutex);
+ if (offset < TWL4030_GPIO_MAX)
+ twl4030_set_gpio_dataout(offset, value);
+ else
+ twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value);
+
+ if (value)
+ priv->out_state |= BIT(offset);
+ else
+ priv->out_state &= ~BIT(offset);
+
+ mutex_unlock(&priv->mutex);
+}
+
+static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
+ int ret = 0;
+
+ mutex_lock(&priv->mutex);
+ if (offset < TWL4030_GPIO_MAX) {
+ ret = twl4030_set_gpio_direction(offset, 0);
+ if (ret) {
+ mutex_unlock(&priv->mutex);
+ return ret;
+ }
+ }
+
+ /*
+ * LED gpios i.e. offset >= TWL4030_GPIO_MAX are always output
+ */
+
+ priv->direction |= BIT(offset);
+ mutex_unlock(&priv->mutex);
+
+ twl_set(chip, offset, value);
+
+ return ret;
+}
+
+static int twl_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
+
+ return (priv->irq_base && (offset < TWL4030_GPIO_MAX))
+ ? (priv->irq_base + offset)
+ : -EINVAL;
+}
+
+static struct gpio_chip template_chip = {
+ .label = "twl4030",
+ .owner = THIS_MODULE,
+ .request = twl_request,
+ .free = twl_free,
+ .direction_input = twl_direction_in,
+ .get = twl_get,
+ .direction_output = twl_direction_out,
+ .set = twl_set,
+ .to_irq = twl_to_irq,
+ .can_sleep = true,
+};
+
+/*----------------------------------------------------------------------*/
+
+static int gpio_twl4030_pulls(u32 ups, u32 downs)
+{
+ u8 message[5];
+ unsigned i, gpio_bit;
+
+ /* For most pins, a pulldown was enabled by default.
+ * We should have data that's specific to this board.
+ */
+ for (gpio_bit = 1, i = 0; i < 5; i++) {
+ u8 bit_mask;
+ unsigned j;
+
+ for (bit_mask = 0, j = 0; j < 8; j += 2, gpio_bit <<= 1) {
+ if (ups & gpio_bit)
+ bit_mask |= 1 << (j + 1);
+ else if (downs & gpio_bit)
+ bit_mask |= 1 << (j + 0);
+ }
+ message[i] = bit_mask;
+ }
+
+ return twl_i2c_write(TWL4030_MODULE_GPIO, message,
+ REG_GPIOPUPDCTR1, 5);
+}
+
+static int gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
+{
+ u8 message[3];
+
+ /* 30 msec of debouncing is always used for MMC card detect,
+ * and is optional for everything else.
+ */
+ message[0] = (debounce & 0xff) | (mmc_cd & 0x03);
+ debounce >>= 8;
+ message[1] = (debounce & 0xff);
+ debounce >>= 8;
+ message[2] = (debounce & 0x03);
+
+ return twl_i2c_write(TWL4030_MODULE_GPIO, message,
+ REG_GPIO_DEBEN1, 3);
+}
+
+static int gpio_twl4030_remove(struct platform_device *pdev);
+
+static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev,
+ struct twl4030_gpio_platform_data *pdata)
+{
+ struct twl4030_gpio_platform_data *omap_twl_info;
+
+ omap_twl_info = devm_kzalloc(dev, sizeof(*omap_twl_info), GFP_KERNEL);
+ if (!omap_twl_info)
+ return NULL;
+
+ if (pdata)
+ *omap_twl_info = *pdata;
+
+ omap_twl_info->use_leds = of_property_read_bool(dev->of_node,
+ "ti,use-leds");
+
+ of_property_read_u32(dev->of_node, "ti,debounce",
+ &omap_twl_info->debounce);
+ of_property_read_u32(dev->of_node, "ti,mmc-cd",
+ (u32 *)&omap_twl_info->mmc_cd);
+ of_property_read_u32(dev->of_node, "ti,pullups",
+ &omap_twl_info->pullups);
+ of_property_read_u32(dev->of_node, "ti,pulldowns",
+ &omap_twl_info->pulldowns);
+
+ return omap_twl_info;
+}
+
+static int gpio_twl4030_probe(struct platform_device *pdev)
+{
+ struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *node = pdev->dev.of_node;
+ struct gpio_twl4030_priv *priv;
+ int ret, irq_base;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_twl4030_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* maybe setup IRQs */
+ if (is_module()) {
+ dev_err(&pdev->dev, "can't dispatch IRQs from modules\n");
+ goto no_irqs;
+ }
+
+ irq_base = irq_alloc_descs(-1, 0, TWL4030_GPIO_MAX, 0);
+ if (irq_base < 0) {
+ dev_err(&pdev->dev, "Failed to alloc irq_descs\n");
+ return irq_base;
+ }
+
+ irq_domain_add_legacy(node, TWL4030_GPIO_MAX, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+
+ ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base);
+ if (ret < 0)
+ return ret;
+
+ priv->irq_base = irq_base;
+
+no_irqs:
+ priv->gpio_chip = template_chip;
+ priv->gpio_chip.base = -1;
+ priv->gpio_chip.ngpio = TWL4030_GPIO_MAX;
+ priv->gpio_chip.dev = &pdev->dev;
+
+ mutex_init(&priv->mutex);
+
+ if (node)
+ pdata = of_gpio_twl4030(&pdev->dev, pdata);
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "Platform data is missing\n");
+ return -ENXIO;
+ }
+
+ /*
+ * NOTE: boards may waste power if they don't set pullups
+ * and pulldowns correctly ... default for non-ULPI pins is
+ * pulldown, and some other pins may have external pullups
+ * or pulldowns. Careful!
+ */
+ ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns);
+ if (ret)
+ dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
+ pdata->pullups, pdata->pulldowns, ret);
+
+ ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd);
+ if (ret)
+ dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
+ pdata->debounce, pdata->mmc_cd, ret);
+
+ /*
+ * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
+ * is (still) clear if use_leds is set.
+ */
+ if (pdata->use_leds)
+ priv->gpio_chip.ngpio += 2;
+
+ ret = gpiochip_add(&priv->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
+ priv->gpio_chip.ngpio = 0;
+ gpio_twl4030_remove(pdev);
+ goto out;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ if (pdata->setup) {
+ int status;
+
+ status = pdata->setup(&pdev->dev, priv->gpio_chip.base,
+ TWL4030_GPIO_MAX);
+ if (status)
+ dev_dbg(&pdev->dev, "setup --> %d\n", status);
+ }
+
+out:
+ return ret;
+}
+
+/* Cannot use as gpio_twl4030_probe() calls us */
+static int gpio_twl4030_remove(struct platform_device *pdev)
+{
+ struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev);
+ int status;
+
+ if (pdata && pdata->teardown) {
+ status = pdata->teardown(&pdev->dev, priv->gpio_chip.base,
+ TWL4030_GPIO_MAX);
+ if (status) {
+ dev_dbg(&pdev->dev, "teardown --> %d\n", status);
+ return status;
+ }
+ }
+
+ gpiochip_remove(&priv->gpio_chip);
+
+ if (is_module())
+ return 0;
+
+ /* REVISIT no support yet for deregistering all the IRQs */
+ WARN_ON(1);
+ return -EIO;
+}
+
+static const struct of_device_id twl_gpio_match[] = {
+ { .compatible = "ti,twl4030-gpio", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl_gpio_match);
+
+/* Note: this hardware lives inside an I2C-based multi-function device. */
+MODULE_ALIAS("platform:twl4030_gpio");
+
+static struct platform_driver gpio_twl4030_driver = {
+ .driver = {
+ .name = "twl4030_gpio",
+ .of_match_table = twl_gpio_match,
+ },
+ .probe = gpio_twl4030_probe,
+ .remove = gpio_twl4030_remove,
+};
+
+static int __init gpio_twl4030_init(void)
+{
+ return platform_driver_register(&gpio_twl4030_driver);
+}
+subsys_initcall(gpio_twl4030_init);
+
+static void __exit gpio_twl4030_exit(void)
+{
+ platform_driver_unregister(&gpio_twl4030_driver);
+}
+module_exit(gpio_twl4030_exit);
+
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("GPIO interface for TWL4030");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c
new file mode 100644
index 000000000..c946e7eef
--- /dev/null
+++ b/drivers/gpio/gpio-twl6040.c
@@ -0,0 +1,133 @@
+/*
+ * Access to GPOs on TWL6040 chip
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Authors:
+ * Sergio Aguirre <saaguirre@ti.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include <linux/mfd/twl6040.h>
+
+static struct gpio_chip twl6040gpo_chip;
+
+static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+ int ret = 0;
+
+ ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+ if (ret < 0)
+ return ret;
+
+ return (ret >> offset) & 1;
+}
+
+static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ /* This only drives GPOs, and can't change direction */
+ return 0;
+}
+
+static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+ int ret;
+ u8 gpoctl;
+
+ ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+ if (ret < 0)
+ return;
+
+ if (value)
+ gpoctl = ret | (1 << offset);
+ else
+ gpoctl = ret & ~(1 << offset);
+
+ twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
+}
+
+static struct gpio_chip twl6040gpo_chip = {
+ .label = "twl6040",
+ .owner = THIS_MODULE,
+ .get = twl6040gpo_get,
+ .direction_output = twl6040gpo_direction_out,
+ .set = twl6040gpo_set,
+ .can_sleep = true,
+};
+
+/*----------------------------------------------------------------------*/
+
+static int gpo_twl6040_probe(struct platform_device *pdev)
+{
+ struct device *twl6040_core_dev = pdev->dev.parent;
+ struct twl6040 *twl6040 = dev_get_drvdata(twl6040_core_dev);
+ int ret;
+
+ twl6040gpo_chip.base = -1;
+
+ if (twl6040_get_revid(twl6040) < TWL6041_REV_ES2_0)
+ twl6040gpo_chip.ngpio = 3; /* twl6040 have 3 GPO */
+ else
+ twl6040gpo_chip.ngpio = 1; /* twl6041 have 1 GPO */
+
+ twl6040gpo_chip.dev = &pdev->dev;
+#ifdef CONFIG_OF_GPIO
+ twl6040gpo_chip.of_node = twl6040_core_dev->of_node;
+#endif
+
+ ret = gpiochip_add(&twl6040gpo_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
+ twl6040gpo_chip.ngpio = 0;
+ }
+
+ return ret;
+}
+
+static int gpo_twl6040_remove(struct platform_device *pdev)
+{
+ gpiochip_remove(&twl6040gpo_chip);
+ return 0;
+}
+
+/* Note: this hardware lives inside an I2C-based multi-function device. */
+MODULE_ALIAS("platform:twl6040-gpo");
+
+static struct platform_driver gpo_twl6040_driver = {
+ .driver = {
+ .name = "twl6040-gpo",
+ },
+ .probe = gpo_twl6040_probe,
+ .remove = gpo_twl6040_remove,
+};
+
+module_platform_driver(gpo_twl6040_driver);
+
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("GPO interface for TWL6040");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-tz1090-pdc.c b/drivers/gpio/gpio-tz1090-pdc.c
new file mode 100644
index 000000000..ede7e403f
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090-pdc.c
@@ -0,0 +1,242 @@
+/*
+ * Toumaz Xenif TZ1090 PDC GPIO handling.
+ *
+ * Copyright (C) 2012-2013 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <asm/global_lock.h>
+
+/* Register offsets from SOC_GPIO_CONTROL0 */
+#define REG_SOC_GPIO_CONTROL0 0x00
+#define REG_SOC_GPIO_CONTROL1 0x04
+#define REG_SOC_GPIO_CONTROL2 0x08
+#define REG_SOC_GPIO_CONTROL3 0x0c
+#define REG_SOC_GPIO_STATUS 0x80
+
+/* PDC GPIOs go after normal GPIOs */
+#define GPIO_PDC_BASE 90
+#define GPIO_PDC_NGPIO 7
+
+/* Out of PDC gpios, only syswakes have irqs */
+#define GPIO_PDC_IRQ_FIRST 2
+#define GPIO_PDC_NIRQ 3
+
+/**
+ * struct tz1090_pdc_gpio - GPIO bank private data
+ * @chip: Generic GPIO chip for GPIO bank
+ * @reg: Base of registers, offset for this GPIO bank
+ * @irq: IRQ numbers for Syswake GPIOs
+ *
+ * This is the main private data for the PDC GPIO driver. It encapsulates a
+ * gpio_chip, and the callbacks for the gpio_chip can access the private data
+ * with the to_pdc() macro below.
+ */
+struct tz1090_pdc_gpio {
+ struct gpio_chip chip;
+ void __iomem *reg;
+ int irq[GPIO_PDC_NIRQ];
+};
+#define to_pdc(c) container_of(c, struct tz1090_pdc_gpio, chip)
+
+/* Register accesses into the PDC MMIO area */
+
+static inline void pdc_write(struct tz1090_pdc_gpio *priv, unsigned int reg_offs,
+ unsigned int data)
+{
+ writel(data, priv->reg + reg_offs);
+}
+
+static inline unsigned int pdc_read(struct tz1090_pdc_gpio *priv,
+ unsigned int reg_offs)
+{
+ return readl(priv->reg + reg_offs);
+}
+
+/* Generic GPIO interface */
+
+static int tz1090_pdc_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ u32 value;
+ int lstat;
+
+ __global_lock2(lstat);
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
+ value |= BIT(offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
+ __global_unlock2(lstat);
+
+ return 0;
+}
+
+static int tz1090_pdc_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset,
+ int output_value)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ u32 value;
+ int lstat;
+
+ __global_lock2(lstat);
+ /* EXT_POWER doesn't seem to have an output value bit */
+ if (offset < 6) {
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
+ if (output_value)
+ value |= BIT(offset);
+ else
+ value &= ~BIT(offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
+ }
+
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
+ value &= ~BIT(offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
+ __global_unlock2(lstat);
+
+ return 0;
+}
+
+static int tz1090_pdc_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ return pdc_read(priv, REG_SOC_GPIO_STATUS) & BIT(offset);
+}
+
+static void tz1090_pdc_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int output_value)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ u32 value;
+ int lstat;
+
+ /* EXT_POWER doesn't seem to have an output value bit */
+ if (offset >= 6)
+ return;
+
+ __global_lock2(lstat);
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
+ if (output_value)
+ value |= BIT(offset);
+ else
+ value &= ~BIT(offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
+ __global_unlock2(lstat);
+}
+
+static int tz1090_pdc_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tz1090_pdc_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ unsigned int syswake = offset - GPIO_PDC_IRQ_FIRST;
+ int irq;
+
+ /* only syswakes have irqs */
+ if (syswake >= GPIO_PDC_NIRQ)
+ return -EINVAL;
+
+ irq = priv->irq[syswake];
+ if (!irq)
+ return -EINVAL;
+
+ return irq;
+}
+
+static int tz1090_pdc_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res_regs;
+ struct tz1090_pdc_gpio *priv;
+ unsigned int i;
+
+ if (!np) {
+ dev_err(&pdev->dev, "must be instantiated via devicetree\n");
+ return -ENOENT;
+ }
+
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_regs) {
+ dev_err(&pdev->dev, "cannot find registers resource\n");
+ return -ENOENT;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "unable to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ /* Ioremap the registers */
+ priv->reg = devm_ioremap(&pdev->dev, res_regs->start,
+ resource_size(res_regs));
+ if (!priv->reg) {
+ dev_err(&pdev->dev, "unable to ioremap registers\n");
+ return -ENOMEM;
+ }
+
+ /* Set up GPIO chip */
+ priv->chip.label = "tz1090-pdc-gpio";
+ priv->chip.dev = &pdev->dev;
+ priv->chip.direction_input = tz1090_pdc_gpio_direction_input;
+ priv->chip.direction_output = tz1090_pdc_gpio_direction_output;
+ priv->chip.get = tz1090_pdc_gpio_get;
+ priv->chip.set = tz1090_pdc_gpio_set;
+ priv->chip.free = tz1090_pdc_gpio_free;
+ priv->chip.request = tz1090_pdc_gpio_request;
+ priv->chip.to_irq = tz1090_pdc_gpio_to_irq;
+ priv->chip.of_node = np;
+
+ /* GPIO numbering */
+ priv->chip.base = GPIO_PDC_BASE;
+ priv->chip.ngpio = GPIO_PDC_NGPIO;
+
+ /* Map the syswake irqs */
+ for (i = 0; i < GPIO_PDC_NIRQ; ++i)
+ priv->irq[i] = irq_of_parse_and_map(np, i);
+
+ /* Add the GPIO bank */
+ gpiochip_add(&priv->chip);
+
+ return 0;
+}
+
+static struct of_device_id tz1090_pdc_gpio_of_match[] = {
+ { .compatible = "img,tz1090-pdc-gpio" },
+ { },
+};
+
+static struct platform_driver tz1090_pdc_gpio_driver = {
+ .driver = {
+ .name = "tz1090-pdc-gpio",
+ .of_match_table = tz1090_pdc_gpio_of_match,
+ },
+ .probe = tz1090_pdc_gpio_probe,
+};
+
+static int __init tz1090_pdc_gpio_init(void)
+{
+ return platform_driver_register(&tz1090_pdc_gpio_driver);
+}
+subsys_initcall(tz1090_pdc_gpio_init);
diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c
new file mode 100644
index 000000000..445660adc
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090.c
@@ -0,0 +1,605 @@
+/*
+ * Toumaz Xenif TZ1090 GPIO handling.
+ *
+ * Copyright (C) 2008-2013 Imagination Technologies Ltd.
+ *
+ * Based on ARM PXA code and others.
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <asm/global_lock.h>
+
+/* Register offsets from bank base address */
+#define REG_GPIO_DIR 0x00
+#define REG_GPIO_IRQ_PLRT 0x20
+#define REG_GPIO_IRQ_TYPE 0x30
+#define REG_GPIO_IRQ_EN 0x40
+#define REG_GPIO_IRQ_STS 0x50
+#define REG_GPIO_BIT_EN 0x60
+#define REG_GPIO_DIN 0x70
+#define REG_GPIO_DOUT 0x80
+
+/* REG_GPIO_IRQ_PLRT */
+#define REG_GPIO_IRQ_PLRT_LOW 0
+#define REG_GPIO_IRQ_PLRT_HIGH 1
+
+/* REG_GPIO_IRQ_TYPE */
+#define REG_GPIO_IRQ_TYPE_LEVEL 0
+#define REG_GPIO_IRQ_TYPE_EDGE 1
+
+/**
+ * struct tz1090_gpio_bank - GPIO bank private data
+ * @chip: Generic GPIO chip for GPIO bank
+ * @domain: IRQ domain for GPIO bank (may be NULL)
+ * @reg: Base of registers, offset for this GPIO bank
+ * @irq: IRQ number for GPIO bank
+ * @label: Debug GPIO bank label, used for storage of chip->label
+ *
+ * This is the main private data for a GPIO bank. It encapsulates a gpio_chip,
+ * and the callbacks for the gpio_chip can access the private data with the
+ * to_bank() macro below.
+ */
+struct tz1090_gpio_bank {
+ struct gpio_chip chip;
+ struct irq_domain *domain;
+ void __iomem *reg;
+ int irq;
+ char label[16];
+};
+#define to_bank(c) container_of(c, struct tz1090_gpio_bank, chip)
+
+/**
+ * struct tz1090_gpio - Overall GPIO device private data
+ * @dev: Device (from platform device)
+ * @reg: Base of GPIO registers
+ *
+ * Represents the overall GPIO device. This structure is actually only
+ * temporary, and used during init.
+ */
+struct tz1090_gpio {
+ struct device *dev;
+ void __iomem *reg;
+};
+
+/**
+ * struct tz1090_gpio_bank_info - Temporary registration info for GPIO bank
+ * @priv: Overall GPIO device private data
+ * @node: Device tree node specific to this GPIO bank
+ * @index: Index of bank in range 0-2
+ */
+struct tz1090_gpio_bank_info {
+ struct tz1090_gpio *priv;
+ struct device_node *node;
+ unsigned int index;
+};
+
+/* Convenience register accessors */
+static inline void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs, u32 data)
+{
+ iowrite32(data, bank->reg + reg_offs);
+}
+
+static inline u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs)
+{
+ return ioread32(bank->reg + reg_offs);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value &= ~BIT(offset);
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_clear_bit(bank, reg_offs, offset);
+ __global_unlock2(lstat);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value |= BIT(offset);
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_set_bit(bank, reg_offs, offset);
+ __global_unlock2(lstat);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset,
+ bool val)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value &= ~BIT(offset);
+ if (val)
+ value |= BIT(offset);
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset,
+ bool val)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_mod_bit(bank, reg_offs, offset, val);
+ __global_unlock2(lstat);
+}
+
+static inline int tz1090_gpio_read_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ return tz1090_gpio_read(bank, reg_offs) & BIT(offset);
+}
+
+/* GPIO chip callbacks */
+
+static int tz1090_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
+
+ return 0;
+}
+
+static int tz1090_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int output_value)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
+ _tz1090_gpio_clear_bit(bank, REG_GPIO_DIR, offset);
+ __global_unlock2(lstat);
+
+ return 0;
+}
+
+/*
+ * Return GPIO level
+ */
+static int tz1090_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ return tz1090_gpio_read_bit(bank, REG_GPIO_DIN, offset);
+}
+
+/*
+ * Set output GPIO level
+ */
+static void tz1090_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int output_value)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
+}
+
+static int tz1090_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ int ret;
+
+ ret = pinctrl_request_gpio(chip->base + offset);
+ if (ret)
+ return ret;
+
+ tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
+ tz1090_gpio_set_bit(bank, REG_GPIO_BIT_EN, offset);
+
+ return 0;
+}
+
+static void tz1090_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ pinctrl_free_gpio(chip->base + offset);
+
+ tz1090_gpio_clear_bit(bank, REG_GPIO_BIT_EN, offset);
+}
+
+static int tz1090_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ if (!bank->domain)
+ return -EINVAL;
+
+ return irq_create_mapping(bank->domain, offset);
+}
+
+/* IRQ chip handlers */
+
+/* Get TZ1090 GPIO chip from irq data provided to generic IRQ callbacks */
+static inline struct tz1090_gpio_bank *irqd_to_gpio_bank(struct irq_data *data)
+{
+ return (struct tz1090_gpio_bank *)data->domain->host_data;
+}
+
+static void tz1090_gpio_irq_polarity(struct tz1090_gpio_bank *bank,
+ unsigned int offset, unsigned int polarity)
+{
+ tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_PLRT, offset, polarity);
+}
+
+static void tz1090_gpio_irq_type(struct tz1090_gpio_bank *bank,
+ unsigned int offset, unsigned int type)
+{
+ tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_TYPE, offset, type);
+}
+
+/* set polarity to trigger on next edge, whether rising or falling */
+static void tz1090_gpio_irq_next_edge(struct tz1090_gpio_bank *bank,
+ unsigned int offset)
+{
+ unsigned int value_p, value_i;
+ int lstat;
+
+ /*
+ * Set the GPIO's interrupt polarity to the opposite of the current
+ * input value so that the next edge triggers an interrupt.
+ */
+ __global_lock2(lstat);
+ value_i = ~tz1090_gpio_read(bank, REG_GPIO_DIN);
+ value_p = tz1090_gpio_read(bank, REG_GPIO_IRQ_PLRT);
+ value_p &= ~BIT(offset);
+ value_p |= value_i & BIT(offset);
+ tz1090_gpio_write(bank, REG_GPIO_IRQ_PLRT, value_p);
+ __global_unlock2(lstat);
+}
+
+static unsigned int gpio_startup_irq(struct irq_data *data)
+{
+ /*
+ * This warning indicates that the type of the irq hasn't been set
+ * before enabling the irq. This would normally be done by passing some
+ * trigger flags to request_irq().
+ */
+ WARN(irqd_get_trigger_type(data) == IRQ_TYPE_NONE,
+ "irq type not set before enabling gpio irq %d", data->irq);
+
+ irq_gc_ack_clr_bit(data);
+ irq_gc_mask_set_bit(data);
+ return 0;
+}
+
+static int gpio_set_irq_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+ unsigned int type;
+ unsigned int polarity;
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ type = REG_GPIO_IRQ_TYPE_EDGE;
+ polarity = REG_GPIO_IRQ_PLRT_LOW;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ type = REG_GPIO_IRQ_TYPE_EDGE;
+ polarity = REG_GPIO_IRQ_PLRT_HIGH;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ type = REG_GPIO_IRQ_TYPE_EDGE;
+ polarity = REG_GPIO_IRQ_PLRT_LOW;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ type = REG_GPIO_IRQ_TYPE_LEVEL;
+ polarity = REG_GPIO_IRQ_PLRT_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ type = REG_GPIO_IRQ_TYPE_LEVEL;
+ polarity = REG_GPIO_IRQ_PLRT_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tz1090_gpio_irq_type(bank, data->hwirq, type);
+ irq_setup_alt_chip(data, flow_type);
+
+ if (flow_type == IRQ_TYPE_EDGE_BOTH)
+ tz1090_gpio_irq_next_edge(bank, data->hwirq);
+ else
+ tz1090_gpio_irq_polarity(bank, data->hwirq, polarity);
+
+ return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int gpio_set_irq_wake(struct irq_data *data, unsigned int on)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+#ifdef CONFIG_PM_DEBUG
+ pr_info("irq_wake irq%d state:%d\n", data->irq, on);
+#endif
+
+ /* wake on gpio block interrupt */
+ return irq_set_irq_wake(bank->irq, on);
+}
+#else
+#define gpio_set_irq_wake NULL
+#endif
+
+static void tz1090_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ irq_hw_number_t hw;
+ unsigned int irq_stat, irq_no;
+ struct tz1090_gpio_bank *bank;
+ struct irq_desc *child_desc;
+
+ bank = (struct tz1090_gpio_bank *)irq_desc_get_handler_data(desc);
+ irq_stat = tz1090_gpio_read(bank, REG_GPIO_DIR) &
+ tz1090_gpio_read(bank, REG_GPIO_IRQ_STS) &
+ tz1090_gpio_read(bank, REG_GPIO_IRQ_EN) &
+ 0x3FFFFFFF; /* 30 bits only */
+
+ for (hw = 0; irq_stat; irq_stat >>= 1, ++hw) {
+ if (!(irq_stat & 1))
+ continue;
+
+ irq_no = irq_linear_revmap(bank->domain, hw);
+ child_desc = irq_to_desc(irq_no);
+
+ /* Toggle edge for pin with both edges triggering enabled */
+ if (irqd_get_trigger_type(&child_desc->irq_data)
+ == IRQ_TYPE_EDGE_BOTH)
+ tz1090_gpio_irq_next_edge(bank, hw);
+
+ generic_handle_irq_desc(irq_no, child_desc);
+ }
+}
+
+static int tz1090_gpio_bank_probe(struct tz1090_gpio_bank_info *info)
+{
+ struct device_node *np = info->node;
+ struct device *dev = info->priv->dev;
+ struct tz1090_gpio_bank *bank;
+ struct irq_chip_generic *gc;
+ int err;
+
+ bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
+ if (!bank) {
+ dev_err(dev, "unable to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ /* Offset the main registers to the first register in this bank */
+ bank->reg = info->priv->reg + info->index * 4;
+
+ /* Set up GPIO chip */
+ snprintf(bank->label, sizeof(bank->label), "tz1090-gpio-%u",
+ info->index);
+ bank->chip.label = bank->label;
+ bank->chip.dev = dev;
+ bank->chip.direction_input = tz1090_gpio_direction_input;
+ bank->chip.direction_output = tz1090_gpio_direction_output;
+ bank->chip.get = tz1090_gpio_get;
+ bank->chip.set = tz1090_gpio_set;
+ bank->chip.free = tz1090_gpio_free;
+ bank->chip.request = tz1090_gpio_request;
+ bank->chip.to_irq = tz1090_gpio_to_irq;
+ bank->chip.of_node = np;
+
+ /* GPIO numbering from 0 */
+ bank->chip.base = info->index * 30;
+ bank->chip.ngpio = 30;
+
+ /* Add the GPIO bank */
+ gpiochip_add(&bank->chip);
+
+ /* Get the GPIO bank IRQ if provided */
+ bank->irq = irq_of_parse_and_map(np, 0);
+
+ /* The interrupt is optional (it may be used by another core on chip) */
+ if (!bank->irq) {
+ dev_info(dev, "IRQ not provided for bank %u, IRQs disabled\n",
+ info->index);
+ return 0;
+ }
+
+ dev_info(dev, "Setting up IRQs for GPIO bank %u\n",
+ info->index);
+
+ /*
+ * Initialise all interrupts to disabled so we don't get
+ * spurious ones on a dirty boot and hit the BUG_ON in the
+ * handler.
+ */
+ tz1090_gpio_write(bank, REG_GPIO_IRQ_EN, 0);
+
+ /* Add a virtual IRQ for each GPIO */
+ bank->domain = irq_domain_add_linear(np,
+ bank->chip.ngpio,
+ &irq_generic_chip_ops,
+ bank);
+
+ /* Set up a generic irq chip with 2 chip types (level and edge) */
+ err = irq_alloc_domain_generic_chips(bank->domain, bank->chip.ngpio, 2,
+ bank->label, handle_bad_irq, 0, 0,
+ IRQ_GC_INIT_NESTED_LOCK);
+ if (err) {
+ dev_info(dev,
+ "irq_alloc_domain_generic_chips failed for bank %u, IRQs disabled\n",
+ info->index);
+ irq_domain_remove(bank->domain);
+ return 0;
+ }
+
+ gc = irq_get_domain_generic_chip(bank->domain, 0);
+ gc->reg_base = bank->reg;
+
+ /* level chip type */
+ gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
+ gc->chip_types[0].handler = handle_level_irq;
+ gc->chip_types[0].regs.ack = REG_GPIO_IRQ_STS;
+ gc->chip_types[0].regs.mask = REG_GPIO_IRQ_EN;
+ gc->chip_types[0].chip.irq_startup = gpio_startup_irq;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_type = gpio_set_irq_type;
+ gc->chip_types[0].chip.irq_set_wake = gpio_set_irq_wake;
+ gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
+ /* edge chip type */
+ gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types[1].handler = handle_edge_irq;
+ gc->chip_types[1].regs.ack = REG_GPIO_IRQ_STS;
+ gc->chip_types[1].regs.mask = REG_GPIO_IRQ_EN;
+ gc->chip_types[1].chip.irq_startup = gpio_startup_irq;
+ gc->chip_types[1].chip.irq_ack = irq_gc_ack_clr_bit;
+ gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[1].chip.irq_set_type = gpio_set_irq_type;
+ gc->chip_types[1].chip.irq_set_wake = gpio_set_irq_wake;
+ gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
+ /* Setup chained handler for this GPIO bank */
+ irq_set_handler_data(bank->irq, bank);
+ irq_set_chained_handler(bank->irq, tz1090_gpio_irq_handler);
+
+ return 0;
+}
+
+static void tz1090_gpio_register_banks(struct tz1090_gpio *priv)
+{
+ struct device_node *np = priv->dev->of_node;
+ struct device_node *node;
+
+ for_each_available_child_of_node(np, node) {
+ struct tz1090_gpio_bank_info info;
+ u32 addr;
+ int ret;
+
+ ret = of_property_read_u32(node, "reg", &addr);
+ if (ret) {
+ dev_err(priv->dev, "invalid reg on %s\n",
+ node->full_name);
+ continue;
+ }
+ if (addr >= 3) {
+ dev_err(priv->dev, "index %u in %s out of range\n",
+ addr, node->full_name);
+ continue;
+ }
+
+ info.index = addr;
+ info.node = of_node_get(node);
+ info.priv = priv;
+
+ ret = tz1090_gpio_bank_probe(&info);
+ if (ret) {
+ dev_err(priv->dev, "failure registering %s\n",
+ node->full_name);
+ of_node_put(node);
+ continue;
+ }
+ }
+}
+
+static int tz1090_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res_regs;
+ struct tz1090_gpio priv;
+
+ if (!np) {
+ dev_err(&pdev->dev, "must be instantiated via devicetree\n");
+ return -ENOENT;
+ }
+
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_regs) {
+ dev_err(&pdev->dev, "cannot find registers resource\n");
+ return -ENOENT;
+ }
+
+ priv.dev = &pdev->dev;
+
+ /* Ioremap the registers */
+ priv.reg = devm_ioremap(&pdev->dev, res_regs->start,
+ resource_size(res_regs));
+ if (!priv.reg) {
+ dev_err(&pdev->dev, "unable to ioremap registers\n");
+ return -ENOMEM;
+ }
+
+ /* Look for banks */
+ tz1090_gpio_register_banks(&priv);
+
+ return 0;
+}
+
+static struct of_device_id tz1090_gpio_of_match[] = {
+ { .compatible = "img,tz1090-gpio" },
+ { },
+};
+
+static struct platform_driver tz1090_gpio_driver = {
+ .driver = {
+ .name = "tz1090-gpio",
+ .of_match_table = tz1090_gpio_of_match,
+ },
+ .probe = tz1090_gpio_probe,
+};
+
+static int __init tz1090_gpio_init(void)
+{
+ return platform_driver_register(&tz1090_gpio_driver);
+}
+subsys_initcall(tz1090_gpio_init);
diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c
new file mode 100644
index 000000000..d50282515
--- /dev/null
+++ b/drivers/gpio/gpio-ucb1400.c
@@ -0,0 +1,108 @@
+/*
+ * Philips UCB1400 GPIO driver
+ *
+ * Author: Marek Vasut <marek.vasut@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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/ucb1400.h>
+
+static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off)
+{
+ struct ucb1400_gpio *gpio;
+ gpio = container_of(gc, struct ucb1400_gpio, gc);
+ ucb1400_gpio_set_direction(gpio->ac97, off, 0);
+ return 0;
+}
+
+static int ucb1400_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val)
+{
+ struct ucb1400_gpio *gpio;
+ gpio = container_of(gc, struct ucb1400_gpio, gc);
+ ucb1400_gpio_set_direction(gpio->ac97, off, 1);
+ ucb1400_gpio_set_value(gpio->ac97, off, val);
+ return 0;
+}
+
+static int ucb1400_gpio_get(struct gpio_chip *gc, unsigned off)
+{
+ struct ucb1400_gpio *gpio;
+ gpio = container_of(gc, struct ucb1400_gpio, gc);
+ return ucb1400_gpio_get_value(gpio->ac97, off);
+}
+
+static void ucb1400_gpio_set(struct gpio_chip *gc, unsigned off, int val)
+{
+ struct ucb1400_gpio *gpio;
+ gpio = container_of(gc, struct ucb1400_gpio, gc);
+ ucb1400_gpio_set_value(gpio->ac97, off, val);
+}
+
+static int ucb1400_gpio_probe(struct platform_device *dev)
+{
+ struct ucb1400_gpio *ucb = dev_get_platdata(&dev->dev);
+ int err = 0;
+
+ if (!(ucb && ucb->gpio_offset)) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ platform_set_drvdata(dev, ucb);
+
+ ucb->gc.label = "ucb1400_gpio";
+ ucb->gc.base = ucb->gpio_offset;
+ ucb->gc.ngpio = 10;
+ ucb->gc.owner = THIS_MODULE;
+
+ ucb->gc.direction_input = ucb1400_gpio_dir_in;
+ ucb->gc.direction_output = ucb1400_gpio_dir_out;
+ ucb->gc.get = ucb1400_gpio_get;
+ ucb->gc.set = ucb1400_gpio_set;
+ ucb->gc.can_sleep = true;
+
+ err = gpiochip_add(&ucb->gc);
+ if (err)
+ goto err;
+
+ if (ucb->gpio_setup)
+ err = ucb->gpio_setup(&dev->dev, ucb->gc.ngpio);
+
+err:
+ return err;
+
+}
+
+static int ucb1400_gpio_remove(struct platform_device *dev)
+{
+ int err = 0;
+ struct ucb1400_gpio *ucb = platform_get_drvdata(dev);
+
+ if (ucb && ucb->gpio_teardown) {
+ err = ucb->gpio_teardown(&dev->dev, ucb->gc.ngpio);
+ if (err)
+ return err;
+ }
+
+ gpiochip_remove(&ucb->gc);
+ return err;
+}
+
+static struct platform_driver ucb1400_gpio_driver = {
+ .probe = ucb1400_gpio_probe,
+ .remove = ucb1400_gpio_remove,
+ .driver = {
+ .name = "ucb1400_gpio"
+ },
+};
+
+module_platform_driver(ucb1400_gpio_driver);
+
+MODULE_DESCRIPTION("Philips UCB1400 GPIO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ucb1400_gpio");
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
new file mode 100644
index 000000000..7bd9f209f
--- /dev/null
+++ b/drivers/gpio/gpio-vf610.c
@@ -0,0 +1,294 @@
+/*
+ * vf610 GPIO support through PORT and GPIO module
+ *
+ * Copyright (c) 2014 Toradex AG.
+ *
+ * Author: Stefan Agner <stefan@agner.ch>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#define VF610_GPIO_PER_PORT 32
+
+struct vf610_gpio_port {
+ struct gpio_chip gc;
+ void __iomem *base;
+ void __iomem *gpio_base;
+ u8 irqc[VF610_GPIO_PER_PORT];
+ int irq;
+};
+
+#define GPIO_PDOR 0x00
+#define GPIO_PSOR 0x04
+#define GPIO_PCOR 0x08
+#define GPIO_PTOR 0x0c
+#define GPIO_PDIR 0x10
+
+#define PORT_PCR(n) ((n) * 0x4)
+#define PORT_PCR_IRQC_OFFSET 16
+
+#define PORT_ISFR 0xa0
+#define PORT_DFER 0xc0
+#define PORT_DFCR 0xc4
+#define PORT_DFWR 0xc8
+
+#define PORT_INT_OFF 0x0
+#define PORT_INT_LOGIC_ZERO 0x8
+#define PORT_INT_RISING_EDGE 0x9
+#define PORT_INT_FALLING_EDGE 0xa
+#define PORT_INT_EITHER_EDGE 0xb
+#define PORT_INT_LOGIC_ONE 0xc
+
+static const struct of_device_id vf610_gpio_dt_ids[] = {
+ { .compatible = "fsl,vf610-gpio" },
+ { /* sentinel */ }
+};
+
+static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
+{
+ writel_relaxed(val, reg);
+}
+
+static inline u32 vf610_gpio_readl(void __iomem *reg)
+{
+ return readl_relaxed(reg);
+}
+
+static int vf610_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void vf610_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct vf610_gpio_port *port =
+ container_of(gc, struct vf610_gpio_port, gc);
+
+ return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR) & BIT(gpio));
+}
+
+static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct vf610_gpio_port *port =
+ container_of(gc, struct vf610_gpio_port, gc);
+ unsigned long mask = BIT(gpio);
+
+ if (val)
+ vf610_gpio_writel(mask, port->gpio_base + GPIO_PSOR);
+ else
+ vf610_gpio_writel(mask, port->gpio_base + GPIO_PCOR);
+}
+
+static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ return pinctrl_gpio_direction_input(chip->base + gpio);
+}
+
+static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ vf610_gpio_set(chip, gpio, value);
+
+ return pinctrl_gpio_direction_output(chip->base + gpio);
+}
+
+static void vf610_gpio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ struct vf610_gpio_port *port = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ int pin;
+ unsigned long irq_isfr;
+
+ chained_irq_enter(chip, desc);
+
+ irq_isfr = vf610_gpio_readl(port->base + PORT_ISFR);
+
+ 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.irqdomain, pin));
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static void vf610_gpio_irq_ack(struct irq_data *d)
+{
+ struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+ int gpio = d->hwirq;
+
+ vf610_gpio_writel(BIT(gpio), port->base + PORT_ISFR);
+}
+
+static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)
+{
+ struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+ u8 irqc;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ irqc = PORT_INT_RISING_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ irqc = PORT_INT_FALLING_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ irqc = PORT_INT_EITHER_EDGE;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ irqc = PORT_INT_LOGIC_ZERO;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ irqc = PORT_INT_LOGIC_ONE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ port->irqc[d->hwirq] = irqc;
+
+ return 0;
+}
+
+static void vf610_gpio_irq_mask(struct irq_data *d)
+{
+ struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+ void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
+
+ vf610_gpio_writel(0, pcr_base);
+}
+
+static void vf610_gpio_irq_unmask(struct irq_data *d)
+{
+ struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+ void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
+
+ vf610_gpio_writel(port->irqc[d->hwirq] << PORT_PCR_IRQC_OFFSET,
+ pcr_base);
+}
+
+static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)
+{
+ struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+
+ if (enable)
+ enable_irq_wake(port->irq);
+ else
+ disable_irq_wake(port->irq);
+
+ return 0;
+}
+
+static struct irq_chip vf610_gpio_irq_chip = {
+ .name = "gpio-vf610",
+ .irq_ack = vf610_gpio_irq_ack,
+ .irq_mask = vf610_gpio_irq_mask,
+ .irq_unmask = vf610_gpio_irq_unmask,
+ .irq_set_type = vf610_gpio_irq_set_type,
+ .irq_set_wake = vf610_gpio_irq_set_wake,
+};
+
+static int vf610_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct vf610_gpio_port *port;
+ struct resource *iores;
+ struct gpio_chip *gc;
+ int ret;
+
+ port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ port->base = devm_ioremap_resource(dev, iores);
+ if (IS_ERR(port->base))
+ return PTR_ERR(port->base);
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ port->gpio_base = devm_ioremap_resource(dev, iores);
+ if (IS_ERR(port->gpio_base))
+ return PTR_ERR(port->gpio_base);
+
+ port->irq = platform_get_irq(pdev, 0);
+ if (port->irq < 0)
+ return port->irq;
+
+ gc = &port->gc;
+ gc->of_node = np;
+ gc->dev = dev;
+ gc->label = "vf610-gpio";
+ gc->ngpio = VF610_GPIO_PER_PORT;
+ gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;
+
+ gc->request = vf610_gpio_request;
+ gc->free = vf610_gpio_free;
+ gc->direction_input = vf610_gpio_direction_input;
+ gc->get = vf610_gpio_get;
+ gc->direction_output = vf610_gpio_direction_output;
+ gc->set = vf610_gpio_set;
+
+ ret = gpiochip_add(gc);
+ if (ret < 0)
+ return ret;
+
+ /* Clear the interrupt status register for all GPIO's */
+ vf610_gpio_writel(~0, port->base + PORT_ISFR);
+
+ ret = gpiochip_irqchip_add(gc, &vf610_gpio_irq_chip, 0,
+ handle_simple_irq, IRQ_TYPE_NONE);
+ if (ret) {
+ dev_err(dev, "failed to add irqchip\n");
+ gpiochip_remove(gc);
+ return ret;
+ }
+ gpiochip_set_chained_irqchip(gc, &vf610_gpio_irq_chip, port->irq,
+ vf610_gpio_irq_handler);
+
+ return 0;
+}
+
+static struct platform_driver vf610_gpio_driver = {
+ .driver = {
+ .name = "gpio-vf610",
+ .of_match_table = vf610_gpio_dt_ids,
+ },
+ .probe = vf610_gpio_probe,
+};
+
+static int __init gpio_vf610_init(void)
+{
+ return platform_driver_register(&vf610_gpio_driver);
+}
+device_initcall(gpio_vf610_init);
+
+MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
+MODULE_DESCRIPTION("Freescale VF610 GPIO");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c
new file mode 100644
index 000000000..e2a11f278
--- /dev/null
+++ b/drivers/gpio/gpio-viperboard.c
@@ -0,0 +1,510 @@
+/*
+ * Nano River Technologies viperboard GPIO lib driver
+ *
+ * (C) 2012 by Lemonage GmbH
+ * Author: Lars Poeschel <poeschel@lemonage.de>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/viperboard.h>
+
+#define VPRBRD_GPIOA_CLK_1MHZ 0
+#define VPRBRD_GPIOA_CLK_100KHZ 1
+#define VPRBRD_GPIOA_CLK_10KHZ 2
+#define VPRBRD_GPIOA_CLK_1KHZ 3
+#define VPRBRD_GPIOA_CLK_100HZ 4
+#define VPRBRD_GPIOA_CLK_10HZ 5
+
+#define VPRBRD_GPIOA_FREQ_DEFAULT 1000
+
+#define VPRBRD_GPIOA_CMD_CONT 0x00
+#define VPRBRD_GPIOA_CMD_PULSE 0x01
+#define VPRBRD_GPIOA_CMD_PWM 0x02
+#define VPRBRD_GPIOA_CMD_SETOUT 0x03
+#define VPRBRD_GPIOA_CMD_SETIN 0x04
+#define VPRBRD_GPIOA_CMD_SETINT 0x05
+#define VPRBRD_GPIOA_CMD_GETIN 0x06
+
+#define VPRBRD_GPIOB_CMD_SETDIR 0x00
+#define VPRBRD_GPIOB_CMD_SETVAL 0x01
+
+struct vprbrd_gpioa_msg {
+ u8 cmd;
+ u8 clk;
+ u8 offset;
+ u8 t1;
+ u8 t2;
+ u8 invert;
+ u8 pwmlevel;
+ u8 outval;
+ u8 risefall;
+ u8 answer;
+ u8 __fill;
+} __packed;
+
+struct vprbrd_gpiob_msg {
+ u8 cmd;
+ u16 val;
+ u16 mask;
+} __packed;
+
+struct vprbrd_gpio {
+ struct gpio_chip gpioa; /* gpio a related things */
+ u32 gpioa_out;
+ u32 gpioa_val;
+ struct gpio_chip gpiob; /* gpio b related things */
+ u32 gpiob_out;
+ u32 gpiob_val;
+ struct vprbrd *vb;
+};
+
+/* gpioa sampling clock module parameter */
+static unsigned char gpioa_clk;
+static unsigned int gpioa_freq = VPRBRD_GPIOA_FREQ_DEFAULT;
+module_param(gpioa_freq, uint, 0);
+MODULE_PARM_DESC(gpioa_freq,
+ "gpio-a sampling freq in Hz (default is 1000Hz) valid values: 10, 100, 1000, 10000, 100000, 1000000");
+
+/* ----- begin of gipo a chip -------------------------------------------- */
+
+static int vprbrd_gpioa_get(struct gpio_chip *chip,
+ unsigned offset)
+{
+ int ret, answer, error = 0;
+ struct vprbrd_gpio *gpio =
+ container_of(chip, struct vprbrd_gpio, gpioa);
+ struct vprbrd *vb = gpio->vb;
+ struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
+
+ /* if io is set to output, just return the saved value */
+ if (gpio->gpioa_out & (1 << offset))
+ return gpio->gpioa_val & (1 << offset);
+
+ mutex_lock(&vb->lock);
+
+ gamsg->cmd = VPRBRD_GPIOA_CMD_GETIN;
+ gamsg->clk = 0x00;
+ gamsg->offset = offset;
+ gamsg->t1 = 0x00;
+ gamsg->t2 = 0x00;
+ gamsg->invert = 0x00;
+ gamsg->pwmlevel = 0x00;
+ gamsg->outval = 0x00;
+ gamsg->risefall = 0x00;
+ gamsg->answer = 0x00;
+ gamsg->__fill = 0x00;
+
+ ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
+ VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
+ 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
+ VPRBRD_USB_TIMEOUT_MS);
+ if (ret != sizeof(struct vprbrd_gpioa_msg))
+ error = -EREMOTEIO;
+
+ ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
+ VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_IN, 0x0000,
+ 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
+ VPRBRD_USB_TIMEOUT_MS);
+ answer = gamsg->answer & 0x01;
+
+ mutex_unlock(&vb->lock);
+
+ if (ret != sizeof(struct vprbrd_gpioa_msg))
+ error = -EREMOTEIO;
+
+ if (error)
+ return error;
+
+ return answer;
+}
+
+static void vprbrd_gpioa_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ int ret;
+ struct vprbrd_gpio *gpio =
+ container_of(chip, struct vprbrd_gpio, gpioa);
+ struct vprbrd *vb = gpio->vb;
+ struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
+
+ if (gpio->gpioa_out & (1 << offset)) {
+ if (value)
+ gpio->gpioa_val |= (1 << offset);
+ else
+ gpio->gpioa_val &= ~(1 << offset);
+
+ mutex_lock(&vb->lock);
+
+ gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT;
+ gamsg->clk = 0x00;
+ gamsg->offset = offset;
+ gamsg->t1 = 0x00;
+ gamsg->t2 = 0x00;
+ gamsg->invert = 0x00;
+ gamsg->pwmlevel = 0x00;
+ gamsg->outval = value;
+ gamsg->risefall = 0x00;
+ gamsg->answer = 0x00;
+ gamsg->__fill = 0x00;
+
+ ret = usb_control_msg(vb->usb_dev,
+ usb_sndctrlpipe(vb->usb_dev, 0),
+ VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT,
+ 0x0000, 0x0000, gamsg,
+ sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS);
+
+ mutex_unlock(&vb->lock);
+
+ if (ret != sizeof(struct vprbrd_gpioa_msg))
+ dev_err(chip->dev, "usb error setting pin value\n");
+ }
+}
+
+static int vprbrd_gpioa_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ int ret;
+ struct vprbrd_gpio *gpio =
+ container_of(chip, struct vprbrd_gpio, gpioa);
+ struct vprbrd *vb = gpio->vb;
+ struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
+
+ gpio->gpioa_out &= ~(1 << offset);
+
+ mutex_lock(&vb->lock);
+
+ gamsg->cmd = VPRBRD_GPIOA_CMD_SETIN;
+ gamsg->clk = gpioa_clk;
+ gamsg->offset = offset;
+ gamsg->t1 = 0x00;
+ gamsg->t2 = 0x00;
+ gamsg->invert = 0x00;
+ gamsg->pwmlevel = 0x00;
+ gamsg->outval = 0x00;
+ gamsg->risefall = 0x00;
+ gamsg->answer = 0x00;
+ gamsg->__fill = 0x00;
+
+ ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
+ VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
+ 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
+ VPRBRD_USB_TIMEOUT_MS);
+
+ mutex_unlock(&vb->lock);
+
+ if (ret != sizeof(struct vprbrd_gpioa_msg))
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int vprbrd_gpioa_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ int ret;
+ struct vprbrd_gpio *gpio =
+ container_of(chip, struct vprbrd_gpio, gpioa);
+ struct vprbrd *vb = gpio->vb;
+ struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
+
+ gpio->gpioa_out |= (1 << offset);
+ if (value)
+ gpio->gpioa_val |= (1 << offset);
+ else
+ gpio->gpioa_val &= ~(1 << offset);
+
+ mutex_lock(&vb->lock);
+
+ gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT;
+ gamsg->clk = 0x00;
+ gamsg->offset = offset;
+ gamsg->t1 = 0x00;
+ gamsg->t2 = 0x00;
+ gamsg->invert = 0x00;
+ gamsg->pwmlevel = 0x00;
+ gamsg->outval = value;
+ gamsg->risefall = 0x00;
+ gamsg->answer = 0x00;
+ gamsg->__fill = 0x00;
+
+ ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
+ VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
+ 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
+ VPRBRD_USB_TIMEOUT_MS);
+
+ mutex_unlock(&vb->lock);
+
+ if (ret != sizeof(struct vprbrd_gpioa_msg))
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+/* ----- end of gpio a chip ---------------------------------------------- */
+
+/* ----- begin of gipo b chip -------------------------------------------- */
+
+static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset,
+ unsigned dir)
+{
+ struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
+ int ret;
+
+ gbmsg->cmd = VPRBRD_GPIOB_CMD_SETDIR;
+ gbmsg->val = cpu_to_be16(dir << offset);
+ gbmsg->mask = cpu_to_be16(0x0001 << offset);
+
+ ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
+ VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, 0x0000,
+ 0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg),
+ VPRBRD_USB_TIMEOUT_MS);
+
+ if (ret != sizeof(struct vprbrd_gpiob_msg))
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int vprbrd_gpiob_get(struct gpio_chip *chip,
+ unsigned offset)
+{
+ int ret;
+ u16 val;
+ struct vprbrd_gpio *gpio =
+ container_of(chip, struct vprbrd_gpio, gpiob);
+ struct vprbrd *vb = gpio->vb;
+ struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
+
+ /* if io is set to output, just return the saved value */
+ if (gpio->gpiob_out & (1 << offset))
+ return gpio->gpiob_val & (1 << offset);
+
+ mutex_lock(&vb->lock);
+
+ ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
+ VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_IN, 0x0000,
+ 0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg),
+ VPRBRD_USB_TIMEOUT_MS);
+ val = gbmsg->val;
+
+ mutex_unlock(&vb->lock);
+
+ if (ret != sizeof(struct vprbrd_gpiob_msg))
+ return ret;
+
+ /* cache the read values */
+ gpio->gpiob_val = be16_to_cpu(val);
+
+ return (gpio->gpiob_val >> offset) & 0x1;
+}
+
+static void vprbrd_gpiob_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ int ret;
+ struct vprbrd_gpio *gpio =
+ container_of(chip, struct vprbrd_gpio, gpiob);
+ struct vprbrd *vb = gpio->vb;
+ struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
+
+ if (gpio->gpiob_out & (1 << offset)) {
+ if (value)
+ gpio->gpiob_val |= (1 << offset);
+ else
+ gpio->gpiob_val &= ~(1 << offset);
+
+ mutex_lock(&vb->lock);
+
+ gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL;
+ gbmsg->val = cpu_to_be16(value << offset);
+ gbmsg->mask = cpu_to_be16(0x0001 << offset);
+
+ ret = usb_control_msg(vb->usb_dev,
+ usb_sndctrlpipe(vb->usb_dev, 0),
+ VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT,
+ 0x0000, 0x0000, gbmsg,
+ sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS);
+
+ mutex_unlock(&vb->lock);
+
+ if (ret != sizeof(struct vprbrd_gpiob_msg))
+ dev_err(chip->dev, "usb error setting pin value\n");
+ }
+}
+
+static int vprbrd_gpiob_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ int ret;
+ struct vprbrd_gpio *gpio =
+ container_of(chip, struct vprbrd_gpio, gpiob);
+ struct vprbrd *vb = gpio->vb;
+
+ gpio->gpiob_out &= ~(1 << offset);
+
+ mutex_lock(&vb->lock);
+
+ ret = vprbrd_gpiob_setdir(vb, offset, 0);
+
+ mutex_unlock(&vb->lock);
+
+ if (ret)
+ dev_err(chip->dev, "usb error setting pin to input\n");
+
+ return ret;
+}
+
+static int vprbrd_gpiob_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ int ret;
+ struct vprbrd_gpio *gpio =
+ container_of(chip, struct vprbrd_gpio, gpiob);
+ struct vprbrd *vb = gpio->vb;
+
+ gpio->gpiob_out |= (1 << offset);
+
+ mutex_lock(&vb->lock);
+
+ ret = vprbrd_gpiob_setdir(vb, offset, 1);
+ if (ret)
+ dev_err(chip->dev, "usb error setting pin to output\n");
+
+ mutex_unlock(&vb->lock);
+
+ vprbrd_gpiob_set(chip, offset, value);
+
+ return ret;
+}
+
+/* ----- end of gpio b chip ---------------------------------------------- */
+
+static int vprbrd_gpio_probe(struct platform_device *pdev)
+{
+ struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
+ struct vprbrd_gpio *vb_gpio;
+ int ret;
+
+ vb_gpio = devm_kzalloc(&pdev->dev, sizeof(*vb_gpio), GFP_KERNEL);
+ if (vb_gpio == NULL)
+ return -ENOMEM;
+
+ vb_gpio->vb = vb;
+ /* registering gpio a */
+ vb_gpio->gpioa.label = "viperboard gpio a";
+ vb_gpio->gpioa.dev = &pdev->dev;
+ vb_gpio->gpioa.owner = THIS_MODULE;
+ vb_gpio->gpioa.base = -1;
+ vb_gpio->gpioa.ngpio = 16;
+ vb_gpio->gpioa.can_sleep = true;
+ vb_gpio->gpioa.set = vprbrd_gpioa_set;
+ 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 = gpiochip_add(&vb_gpio->gpioa);
+ if (ret < 0) {
+ dev_err(vb_gpio->gpioa.dev, "could not add gpio a");
+ goto err_gpioa;
+ }
+
+ /* registering gpio b */
+ vb_gpio->gpiob.label = "viperboard gpio b";
+ vb_gpio->gpiob.dev = &pdev->dev;
+ vb_gpio->gpiob.owner = THIS_MODULE;
+ vb_gpio->gpiob.base = -1;
+ vb_gpio->gpiob.ngpio = 16;
+ vb_gpio->gpiob.can_sleep = true;
+ vb_gpio->gpiob.set = vprbrd_gpiob_set;
+ 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 = gpiochip_add(&vb_gpio->gpiob);
+ if (ret < 0) {
+ dev_err(vb_gpio->gpiob.dev, "could not add gpio b");
+ goto err_gpiob;
+ }
+
+ platform_set_drvdata(pdev, vb_gpio);
+
+ return ret;
+
+err_gpiob:
+ gpiochip_remove(&vb_gpio->gpioa);
+
+err_gpioa:
+ return ret;
+}
+
+static int vprbrd_gpio_remove(struct platform_device *pdev)
+{
+ struct vprbrd_gpio *vb_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&vb_gpio->gpiob);
+
+ return 0;
+}
+
+static struct platform_driver vprbrd_gpio_driver = {
+ .driver.name = "viperboard-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = vprbrd_gpio_probe,
+ .remove = vprbrd_gpio_remove,
+};
+
+static int __init vprbrd_gpio_init(void)
+{
+ switch (gpioa_freq) {
+ case 1000000:
+ gpioa_clk = VPRBRD_GPIOA_CLK_1MHZ;
+ break;
+ case 100000:
+ gpioa_clk = VPRBRD_GPIOA_CLK_100KHZ;
+ break;
+ case 10000:
+ gpioa_clk = VPRBRD_GPIOA_CLK_10KHZ;
+ break;
+ case 1000:
+ gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ;
+ break;
+ case 100:
+ gpioa_clk = VPRBRD_GPIOA_CLK_100HZ;
+ break;
+ case 10:
+ gpioa_clk = VPRBRD_GPIOA_CLK_10HZ;
+ break;
+ default:
+ pr_warn("invalid gpioa_freq (%d)\n", gpioa_freq);
+ gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ;
+ }
+
+ return platform_driver_register(&vprbrd_gpio_driver);
+}
+subsys_initcall(vprbrd_gpio_init);
+
+static void __exit vprbrd_gpio_exit(void)
+{
+ platform_driver_unregister(&vprbrd_gpio_driver);
+}
+module_exit(vprbrd_gpio_exit);
+
+MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
+MODULE_DESCRIPTION("GPIO driver for Nano River Techs Viperboard");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:viperboard-gpio");
diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c
new file mode 100644
index 000000000..c1caa459c
--- /dev/null
+++ b/drivers/gpio/gpio-vr41xx.c
@@ -0,0 +1,597 @@
+/*
+ * Driver for NEC VR4100 series General-purpose I/O Unit.
+ *
+ * Copyright (C) 2002 MontaVista Software Inc.
+ * Author: Yoichi Yuasa <source@mvista.com>
+ * Copyright (C) 2003-2009 Yoichi Yuasa <yuasa@linux-mips.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/gpio.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>
+#include <linux/types.h>
+
+#include <asm/vr41xx/giu.h>
+#include <asm/vr41xx/irq.h>
+#include <asm/vr41xx/vr41xx.h>
+
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
+MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver");
+MODULE_LICENSE("GPL");
+
+#define GIUIOSELL 0x00
+#define GIUIOSELH 0x02
+#define GIUPIODL 0x04
+#define GIUPIODH 0x06
+#define GIUINTSTATL 0x08
+#define GIUINTSTATH 0x0a
+#define GIUINTENL 0x0c
+#define GIUINTENH 0x0e
+#define GIUINTTYPL 0x10
+#define GIUINTTYPH 0x12
+#define GIUINTALSELL 0x14
+#define GIUINTALSELH 0x16
+#define GIUINTHTSELL 0x18
+#define GIUINTHTSELH 0x1a
+#define GIUPODATL 0x1c
+#define GIUPODATEN 0x1c
+#define GIUPODATH 0x1e
+ #define PIOEN0 0x0100
+ #define PIOEN1 0x0200
+#define GIUPODAT 0x1e
+#define GIUFEDGEINHL 0x20
+#define GIUFEDGEINHH 0x22
+#define GIUREDGEINHL 0x24
+#define GIUREDGEINHH 0x26
+
+#define GIUUSEUPDN 0x1e0
+#define GIUTERMUPDN 0x1e2
+
+#define GPIO_HAS_PULLUPDOWN_IO 0x0001
+#define GPIO_HAS_OUTPUT_ENABLE 0x0002
+#define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100
+
+enum {
+ GPIO_INPUT,
+ GPIO_OUTPUT,
+};
+
+static DEFINE_SPINLOCK(giu_lock);
+static unsigned long giu_flags;
+
+static void __iomem *giu_base;
+static struct gpio_chip vr41xx_gpio_chip;
+
+#define giu_read(offset) readw(giu_base + (offset))
+#define giu_write(offset, value) writew((value), giu_base + (offset))
+
+#define GPIO_PIN_OF_IRQ(irq) ((irq) - GIU_IRQ_BASE)
+#define GIUINT_HIGH_OFFSET 16
+#define GIUINT_HIGH_MAX 32
+
+static inline u16 giu_set(u16 offset, u16 set)
+{
+ u16 data;
+
+ data = giu_read(offset);
+ data |= set;
+ giu_write(offset, data);
+
+ return data;
+}
+
+static inline u16 giu_clear(u16 offset, u16 clear)
+{
+ u16 data;
+
+ data = giu_read(offset);
+ data &= ~clear;
+ giu_write(offset, data);
+
+ return data;
+}
+
+static void ack_giuint_low(struct irq_data *d)
+{
+ giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(d->irq));
+}
+
+static void mask_giuint_low(struct irq_data *d)
+{
+ giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq));
+}
+
+static void mask_ack_giuint_low(struct irq_data *d)
+{
+ unsigned int pin;
+
+ pin = GPIO_PIN_OF_IRQ(d->irq);
+ giu_clear(GIUINTENL, 1 << pin);
+ giu_write(GIUINTSTATL, 1 << pin);
+}
+
+static void unmask_giuint_low(struct irq_data *d)
+{
+ giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq));
+}
+
+static unsigned int startup_giuint(struct irq_data *data)
+{
+ if (gpiochip_lock_as_irq(&vr41xx_gpio_chip, data->hwirq))
+ dev_err(vr41xx_gpio_chip.dev,
+ "unable to lock HW IRQ %lu for IRQ\n",
+ data->hwirq);
+ /* Satisfy the .enable semantics by unmasking the line */
+ unmask_giuint_low(data);
+ return 0;
+}
+
+static void shutdown_giuint(struct irq_data *data)
+{
+ mask_giuint_low(data);
+ gpiochip_unlock_as_irq(&vr41xx_gpio_chip, data->hwirq);
+}
+
+static struct irq_chip giuint_low_irq_chip = {
+ .name = "GIUINTL",
+ .irq_ack = ack_giuint_low,
+ .irq_mask = mask_giuint_low,
+ .irq_mask_ack = mask_ack_giuint_low,
+ .irq_unmask = unmask_giuint_low,
+ .irq_startup = startup_giuint,
+ .irq_shutdown = shutdown_giuint,
+};
+
+static void ack_giuint_high(struct irq_data *d)
+{
+ giu_write(GIUINTSTATH,
+ 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET));
+}
+
+static void mask_giuint_high(struct irq_data *d)
+{
+ giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET));
+}
+
+static void mask_ack_giuint_high(struct irq_data *d)
+{
+ unsigned int pin;
+
+ pin = GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET;
+ giu_clear(GIUINTENH, 1 << pin);
+ giu_write(GIUINTSTATH, 1 << pin);
+}
+
+static void unmask_giuint_high(struct irq_data *d)
+{
+ giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET));
+}
+
+static struct irq_chip giuint_high_irq_chip = {
+ .name = "GIUINTH",
+ .irq_ack = ack_giuint_high,
+ .irq_mask = mask_giuint_high,
+ .irq_mask_ack = mask_ack_giuint_high,
+ .irq_unmask = unmask_giuint_high,
+};
+
+static int giu_get_irq(unsigned int irq)
+{
+ u16 pendl, pendh, maskl, maskh;
+ int i;
+
+ pendl = giu_read(GIUINTSTATL);
+ pendh = giu_read(GIUINTSTATH);
+ maskl = giu_read(GIUINTENL);
+ maskh = giu_read(GIUINTENH);
+
+ maskl &= pendl;
+ maskh &= pendh;
+
+ if (maskl) {
+ for (i = 0; i < 16; i++) {
+ if (maskl & (1 << i))
+ return GIU_IRQ(i);
+ }
+ } else if (maskh) {
+ for (i = 0; i < 16; i++) {
+ if (maskh & (1 << i))
+ return GIU_IRQ(i + GIUINT_HIGH_OFFSET);
+ }
+ }
+
+ printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n",
+ maskl, pendl, maskh, pendh);
+
+ atomic_inc(&irq_err_count);
+
+ return -EINVAL;
+}
+
+void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger,
+ irq_signal_t signal)
+{
+ u16 mask;
+
+ if (pin < GIUINT_HIGH_OFFSET) {
+ mask = 1 << pin;
+ if (trigger != IRQ_TRIGGER_LEVEL) {
+ giu_set(GIUINTTYPL, mask);
+ if (signal == IRQ_SIGNAL_HOLD)
+ giu_set(GIUINTHTSELL, mask);
+ else
+ giu_clear(GIUINTHTSELL, mask);
+ if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) {
+ switch (trigger) {
+ case IRQ_TRIGGER_EDGE_FALLING:
+ giu_set(GIUFEDGEINHL, mask);
+ giu_clear(GIUREDGEINHL, mask);
+ break;
+ case IRQ_TRIGGER_EDGE_RISING:
+ giu_clear(GIUFEDGEINHL, mask);
+ giu_set(GIUREDGEINHL, mask);
+ break;
+ default:
+ giu_set(GIUFEDGEINHL, mask);
+ giu_set(GIUREDGEINHL, mask);
+ break;
+ }
+ }
+ irq_set_chip_and_handler(GIU_IRQ(pin),
+ &giuint_low_irq_chip,
+ handle_edge_irq);
+ } else {
+ giu_clear(GIUINTTYPL, mask);
+ giu_clear(GIUINTHTSELL, mask);
+ irq_set_chip_and_handler(GIU_IRQ(pin),
+ &giuint_low_irq_chip,
+ handle_level_irq);
+ }
+ giu_write(GIUINTSTATL, mask);
+ } else if (pin < GIUINT_HIGH_MAX) {
+ mask = 1 << (pin - GIUINT_HIGH_OFFSET);
+ if (trigger != IRQ_TRIGGER_LEVEL) {
+ giu_set(GIUINTTYPH, mask);
+ if (signal == IRQ_SIGNAL_HOLD)
+ giu_set(GIUINTHTSELH, mask);
+ else
+ giu_clear(GIUINTHTSELH, mask);
+ if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) {
+ switch (trigger) {
+ case IRQ_TRIGGER_EDGE_FALLING:
+ giu_set(GIUFEDGEINHH, mask);
+ giu_clear(GIUREDGEINHH, mask);
+ break;
+ case IRQ_TRIGGER_EDGE_RISING:
+ giu_clear(GIUFEDGEINHH, mask);
+ giu_set(GIUREDGEINHH, mask);
+ break;
+ default:
+ giu_set(GIUFEDGEINHH, mask);
+ giu_set(GIUREDGEINHH, mask);
+ break;
+ }
+ }
+ irq_set_chip_and_handler(GIU_IRQ(pin),
+ &giuint_high_irq_chip,
+ handle_edge_irq);
+ } else {
+ giu_clear(GIUINTTYPH, mask);
+ giu_clear(GIUINTHTSELH, mask);
+ irq_set_chip_and_handler(GIU_IRQ(pin),
+ &giuint_high_irq_chip,
+ handle_level_irq);
+ }
+ giu_write(GIUINTSTATH, mask);
+ }
+}
+EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger);
+
+void vr41xx_set_irq_level(unsigned int pin, irq_level_t level)
+{
+ u16 mask;
+
+ if (pin < GIUINT_HIGH_OFFSET) {
+ mask = 1 << pin;
+ if (level == IRQ_LEVEL_HIGH)
+ giu_set(GIUINTALSELL, mask);
+ else
+ giu_clear(GIUINTALSELL, mask);
+ giu_write(GIUINTSTATL, mask);
+ } else if (pin < GIUINT_HIGH_MAX) {
+ mask = 1 << (pin - GIUINT_HIGH_OFFSET);
+ if (level == IRQ_LEVEL_HIGH)
+ giu_set(GIUINTALSELH, mask);
+ else
+ giu_clear(GIUINTALSELH, mask);
+ giu_write(GIUINTSTATH, mask);
+ }
+}
+EXPORT_SYMBOL_GPL(vr41xx_set_irq_level);
+
+static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir)
+{
+ u16 offset, mask, reg;
+ unsigned long flags;
+
+ if (pin >= chip->ngpio)
+ return -EINVAL;
+
+ if (pin < 16) {
+ offset = GIUIOSELL;
+ mask = 1 << pin;
+ } else if (pin < 32) {
+ offset = GIUIOSELH;
+ mask = 1 << (pin - 16);
+ } else {
+ if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) {
+ offset = GIUPODATEN;
+ mask = 1 << (pin - 32);
+ } else {
+ switch (pin) {
+ case 48:
+ offset = GIUPODATH;
+ mask = PIOEN0;
+ break;
+ case 49:
+ offset = GIUPODATH;
+ mask = PIOEN1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&giu_lock, flags);
+
+ reg = giu_read(offset);
+ if (dir == GPIO_OUTPUT)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ giu_write(offset, reg);
+
+ spin_unlock_irqrestore(&giu_lock, flags);
+
+ return 0;
+}
+
+int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull)
+{
+ u16 reg, mask;
+ unsigned long flags;
+
+ if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO)
+ return -EPERM;
+
+ if (pin >= 15)
+ return -EINVAL;
+
+ mask = 1 << pin;
+
+ spin_lock_irqsave(&giu_lock, flags);
+
+ if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) {
+ reg = giu_read(GIUTERMUPDN);
+ if (pull == GPIO_PULL_UP)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ giu_write(GIUTERMUPDN, reg);
+
+ reg = giu_read(GIUUSEUPDN);
+ reg |= mask;
+ giu_write(GIUUSEUPDN, reg);
+ } else {
+ reg = giu_read(GIUUSEUPDN);
+ reg &= ~mask;
+ giu_write(GIUUSEUPDN, reg);
+ }
+
+ spin_unlock_irqrestore(&giu_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown);
+
+static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin)
+{
+ u16 reg, mask;
+
+ if (pin >= chip->ngpio)
+ return -EINVAL;
+
+ if (pin < 16) {
+ reg = giu_read(GIUPIODL);
+ mask = 1 << pin;
+ } else if (pin < 32) {
+ reg = giu_read(GIUPIODH);
+ mask = 1 << (pin - 16);
+ } else if (pin < 48) {
+ reg = giu_read(GIUPODATL);
+ mask = 1 << (pin - 32);
+ } else {
+ reg = giu_read(GIUPODATH);
+ mask = 1 << (pin - 48);
+ }
+
+ if (reg & mask)
+ return 1;
+
+ return 0;
+}
+
+static void vr41xx_gpio_set(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ u16 offset, mask, reg;
+ unsigned long flags;
+
+ if (pin >= chip->ngpio)
+ return;
+
+ if (pin < 16) {
+ offset = GIUPIODL;
+ mask = 1 << pin;
+ } else if (pin < 32) {
+ offset = GIUPIODH;
+ mask = 1 << (pin - 16);
+ } else if (pin < 48) {
+ offset = GIUPODATL;
+ mask = 1 << (pin - 32);
+ } else {
+ offset = GIUPODATH;
+ mask = 1 << (pin - 48);
+ }
+
+ spin_lock_irqsave(&giu_lock, flags);
+
+ reg = giu_read(offset);
+ if (value)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ giu_write(offset, reg);
+
+ spin_unlock_irqrestore(&giu_lock, flags);
+}
+
+
+static int vr41xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ return giu_set_direction(chip, offset, GPIO_INPUT);
+}
+
+static int vr41xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ vr41xx_gpio_set(chip, offset, value);
+
+ return giu_set_direction(chip, offset, GPIO_OUTPUT);
+}
+
+static int vr41xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ return GIU_IRQ_BASE + offset;
+}
+
+static struct gpio_chip vr41xx_gpio_chip = {
+ .label = "vr41xx",
+ .owner = THIS_MODULE,
+ .direction_input = vr41xx_gpio_direction_input,
+ .get = vr41xx_gpio_get,
+ .direction_output = vr41xx_gpio_direction_output,
+ .set = vr41xx_gpio_set,
+ .to_irq = vr41xx_gpio_to_irq,
+};
+
+static int giu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned int trigger, i, pin;
+ struct irq_chip *chip;
+ int irq, ret;
+
+ switch (pdev->id) {
+ case GPIO_50PINS_PULLUPDOWN:
+ giu_flags = GPIO_HAS_PULLUPDOWN_IO;
+ vr41xx_gpio_chip.ngpio = 50;
+ break;
+ case GPIO_36PINS:
+ vr41xx_gpio_chip.ngpio = 36;
+ break;
+ case GPIO_48PINS_EDGE_SELECT:
+ giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT;
+ vr41xx_gpio_chip.ngpio = 48;
+ break;
+ default:
+ dev_err(&pdev->dev, "GIU: unknown ID %d\n", pdev->id);
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EBUSY;
+
+ giu_base = ioremap(res->start, resource_size(res));
+ if (!giu_base)
+ return -ENOMEM;
+
+ vr41xx_gpio_chip.dev = &pdev->dev;
+
+ ret = gpiochip_add(&vr41xx_gpio_chip);
+ if (!ret) {
+ iounmap(giu_base);
+ return -ENODEV;
+ }
+
+ giu_write(GIUINTENL, 0);
+ giu_write(GIUINTENH, 0);
+
+ trigger = giu_read(GIUINTTYPH) << 16;
+ trigger |= giu_read(GIUINTTYPL);
+ for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) {
+ pin = GPIO_PIN_OF_IRQ(i);
+ if (pin < GIUINT_HIGH_OFFSET)
+ chip = &giuint_low_irq_chip;
+ else
+ chip = &giuint_high_irq_chip;
+
+ if (trigger & (1 << pin))
+ irq_set_chip_and_handler(i, chip, handle_edge_irq);
+ else
+ irq_set_chip_and_handler(i, chip, handle_level_irq);
+
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0 || irq >= nr_irqs)
+ return -EBUSY;
+
+ return cascade_irq(irq, giu_get_irq);
+}
+
+static int giu_remove(struct platform_device *pdev)
+{
+ if (giu_base) {
+ iounmap(giu_base);
+ giu_base = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver giu_device_driver = {
+ .probe = giu_probe,
+ .remove = giu_remove,
+ .driver = {
+ .name = "GIU",
+ },
+};
+
+module_platform_driver(giu_device_driver);
diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c
new file mode 100644
index 000000000..57b470d5b
--- /dev/null
+++ b/drivers/gpio/gpio-vx855.c
@@ -0,0 +1,287 @@
+/*
+ * Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO
+ *
+ * Copyright (C) 2009 VIA Technologies, Inc.
+ * Copyright (C) 2010 One Laptop per Child
+ * Author: Harald Welte <HaraldWelte@viatech.com>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+
+#define MODULE_NAME "vx855_gpio"
+
+/* The VX855 south bridge has the following GPIO pins:
+ * GPI 0...13 General Purpose Input
+ * GPO 0...12 General Purpose Output
+ * GPIO 0...14 General Purpose I/O (Open-Drain)
+ */
+
+#define NR_VX855_GPI 14
+#define NR_VX855_GPO 13
+#define NR_VX855_GPIO 15
+
+#define NR_VX855_GPInO (NR_VX855_GPI + NR_VX855_GPO)
+#define NR_VX855_GP (NR_VX855_GPI + NR_VX855_GPO + NR_VX855_GPIO)
+
+struct vx855_gpio {
+ struct gpio_chip gpio;
+ spinlock_t lock;
+ u32 io_gpi;
+ u32 io_gpo;
+};
+
+/* resolve a GPIx into the corresponding bit position */
+static inline u_int32_t gpi_i_bit(int i)
+{
+ if (i < 10)
+ return 1 << i;
+ else
+ return 1 << (i + 14);
+}
+
+static inline u_int32_t gpo_o_bit(int i)
+{
+ if (i < 11)
+ return 1 << i;
+ else
+ return 1 << (i + 14);
+}
+
+static inline u_int32_t gpio_i_bit(int i)
+{
+ if (i < 14)
+ return 1 << (i + 10);
+ else
+ return 1 << (i + 14);
+}
+
+static inline u_int32_t gpio_o_bit(int i)
+{
+ if (i < 14)
+ return 1 << (i + 11);
+ else
+ return 1 << (i + 13);
+}
+
+/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering:
+ * 0..13 GPI 0..13
+ * 14..26 GPO 0..12
+ * 27..41 GPIO 0..14
+ */
+
+static int vx855gpio_direction_input(struct gpio_chip *gpio,
+ unsigned int nr)
+{
+ struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
+ unsigned long flags;
+ u_int32_t reg_out;
+
+ /* Real GPI bits are always in input direction */
+ if (nr < NR_VX855_GPI)
+ return 0;
+
+ /* Real GPO bits cannot be put in output direction */
+ if (nr < NR_VX855_GPInO)
+ return -EINVAL;
+
+ /* Open Drain GPIO have to be set to one */
+ spin_lock_irqsave(&vg->lock, flags);
+ reg_out = inl(vg->io_gpo);
+ reg_out |= gpio_o_bit(nr - NR_VX855_GPInO);
+ outl(reg_out, vg->io_gpo);
+ spin_unlock_irqrestore(&vg->lock, flags);
+
+ return 0;
+}
+
+static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr)
+{
+ struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
+ u_int32_t reg_in;
+ int ret = 0;
+
+ if (nr < NR_VX855_GPI) {
+ reg_in = inl(vg->io_gpi);
+ if (reg_in & gpi_i_bit(nr))
+ ret = 1;
+ } else if (nr < NR_VX855_GPInO) {
+ /* GPO don't have an input bit, we need to read it
+ * back from the output register */
+ reg_in = inl(vg->io_gpo);
+ if (reg_in & gpo_o_bit(nr - NR_VX855_GPI))
+ ret = 1;
+ } else {
+ reg_in = inl(vg->io_gpi);
+ if (reg_in & gpio_i_bit(nr - NR_VX855_GPInO))
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr,
+ int val)
+{
+ struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
+ unsigned long flags;
+ u_int32_t reg_out;
+
+ /* True GPI cannot be switched to output mode */
+ if (nr < NR_VX855_GPI)
+ return;
+
+ spin_lock_irqsave(&vg->lock, flags);
+ reg_out = inl(vg->io_gpo);
+ if (nr < NR_VX855_GPInO) {
+ if (val)
+ reg_out |= gpo_o_bit(nr - NR_VX855_GPI);
+ else
+ reg_out &= ~gpo_o_bit(nr - NR_VX855_GPI);
+ } else {
+ if (val)
+ reg_out |= gpio_o_bit(nr - NR_VX855_GPInO);
+ else
+ reg_out &= ~gpio_o_bit(nr - NR_VX855_GPInO);
+ }
+ outl(reg_out, vg->io_gpo);
+ spin_unlock_irqrestore(&vg->lock, flags);
+}
+
+static int vx855gpio_direction_output(struct gpio_chip *gpio,
+ unsigned int nr, int val)
+{
+ /* True GPI cannot be switched to output mode */
+ if (nr < NR_VX855_GPI)
+ return -EINVAL;
+
+ /* True GPO don't need to be switched to output mode,
+ * and GPIO are open-drain, i.e. also need no switching,
+ * so all we do is set the level */
+ vx855gpio_set(gpio, nr, val);
+
+ return 0;
+}
+
+static const char *vx855gpio_names[NR_VX855_GP] = {
+ "VX855_GPI0", "VX855_GPI1", "VX855_GPI2", "VX855_GPI3", "VX855_GPI4",
+ "VX855_GPI5", "VX855_GPI6", "VX855_GPI7", "VX855_GPI8", "VX855_GPI9",
+ "VX855_GPI10", "VX855_GPI11", "VX855_GPI12", "VX855_GPI13",
+ "VX855_GPO0", "VX855_GPO1", "VX855_GPO2", "VX855_GPO3", "VX855_GPO4",
+ "VX855_GPO5", "VX855_GPO6", "VX855_GPO7", "VX855_GPO8", "VX855_GPO9",
+ "VX855_GPO10", "VX855_GPO11", "VX855_GPO12",
+ "VX855_GPIO0", "VX855_GPIO1", "VX855_GPIO2", "VX855_GPIO3",
+ "VX855_GPIO4", "VX855_GPIO5", "VX855_GPIO6", "VX855_GPIO7",
+ "VX855_GPIO8", "VX855_GPIO9", "VX855_GPIO10", "VX855_GPIO11",
+ "VX855_GPIO12", "VX855_GPIO13", "VX855_GPIO14"
+};
+
+static void vx855gpio_gpio_setup(struct vx855_gpio *vg)
+{
+ struct gpio_chip *c = &vg->gpio;
+
+ c->label = "VX855 South Bridge";
+ c->owner = THIS_MODULE;
+ c->direction_input = vx855gpio_direction_input;
+ c->direction_output = vx855gpio_direction_output;
+ c->get = vx855gpio_get;
+ c->set = vx855gpio_set;
+ c->dbg_show = NULL;
+ c->base = 0;
+ c->ngpio = NR_VX855_GP;
+ c->can_sleep = false;
+ c->names = vx855gpio_names;
+}
+
+/* This platform device is ordinarily registered by the vx855 mfd driver */
+static int vx855gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res_gpi;
+ struct resource *res_gpo;
+ struct vx855_gpio *vg;
+
+ res_gpi = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ res_gpo = platform_get_resource(pdev, IORESOURCE_IO, 1);
+ if (!res_gpi || !res_gpo)
+ return -EBUSY;
+
+ vg = devm_kzalloc(&pdev->dev, sizeof(*vg), GFP_KERNEL);
+ if (!vg)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, vg);
+
+ dev_info(&pdev->dev, "found VX855 GPIO controller\n");
+ vg->io_gpi = res_gpi->start;
+ vg->io_gpo = res_gpo->start;
+ spin_lock_init(&vg->lock);
+
+ /*
+ * A single byte is used to control various GPIO ports on the VX855,
+ * and in the case of the OLPC XO-1.5, some of those ports are used
+ * for switches that are interpreted and exposed through ACPI. ACPI
+ * will have reserved the region, so our own reservation will not
+ * succeed. Ignore and continue.
+ */
+
+ if (!devm_request_region(&pdev->dev, res_gpi->start,
+ resource_size(res_gpi), MODULE_NAME "_gpi"))
+ dev_warn(&pdev->dev,
+ "GPI I/O resource busy, probably claimed by ACPI\n");
+
+ if (!devm_request_region(&pdev->dev, res_gpo->start,
+ resource_size(res_gpo), MODULE_NAME "_gpo"))
+ dev_warn(&pdev->dev,
+ "GPO I/O resource busy, probably claimed by ACPI\n");
+
+ vx855gpio_gpio_setup(vg);
+
+ return gpiochip_add(&vg->gpio);
+}
+
+static int vx855gpio_remove(struct platform_device *pdev)
+{
+ struct vx855_gpio *vg = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&vg->gpio);
+
+ return 0;
+}
+
+static struct platform_driver vx855gpio_driver = {
+ .driver = {
+ .name = MODULE_NAME,
+ },
+ .probe = vx855gpio_probe,
+ .remove = vx855gpio_remove,
+};
+
+module_platform_driver(vx855gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
+MODULE_DESCRIPTION("GPIO driver for the VIA VX855 chipset");
+MODULE_ALIAS("platform:vx855_gpio");
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
new file mode 100644
index 000000000..58ce75c18
--- /dev/null
+++ b/drivers/gpio/gpio-wm831x.c
@@ -0,0 +1,308 @@
+/*
+ * gpiolib support for Wolfson WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/gpio.h>
+#include <linux/mfd/wm831x/irq.h>
+
+struct wm831x_gpio {
+ struct wm831x *wm831x;
+ struct gpio_chip gpio_chip;
+};
+
+static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm831x_gpio, gpio_chip);
+}
+
+static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int val = WM831X_GPN_DIR;
+
+ if (wm831x->has_gpio_ena)
+ val |= WM831X_GPN_TRI;
+
+ return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
+ WM831X_GPN_DIR | WM831X_GPN_TRI |
+ WM831X_GPN_FN_MASK, val);
+}
+
+static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
+ if (ret < 0)
+ return ret;
+
+ if (ret & 1 << offset)
+ return 1;
+ else
+ return 0;
+}
+
+static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+
+ wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset,
+ value << offset);
+}
+
+static int wm831x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int val = 0;
+ int ret;
+
+ if (wm831x->has_gpio_ena)
+ val |= WM831X_GPN_TRI;
+
+ ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
+ WM831X_GPN_DIR | WM831X_GPN_TRI |
+ WM831X_GPN_FN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ /* Can only set GPIO state once it's in output mode */
+ wm831x_gpio_set(chip, offset, value);
+
+ return 0;
+}
+
+static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+
+ return irq_create_mapping(wm831x->irq_domain,
+ WM831X_IRQ_GPIO_1 + offset);
+}
+
+static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
+ unsigned debounce)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int reg = WM831X_GPIO1_CONTROL + offset;
+ int ret, fn;
+
+ ret = wm831x_reg_read(wm831x, reg);
+ if (ret < 0)
+ return ret;
+
+ switch (ret & WM831X_GPN_FN_MASK) {
+ case 0:
+ case 1:
+ break;
+ default:
+ /* Not in GPIO mode */
+ return -EBUSY;
+ }
+
+ if (debounce >= 32 && debounce <= 64)
+ fn = 0;
+ else if (debounce >= 4000 && debounce <= 8000)
+ fn = 1;
+ else
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int i, tristated;
+
+ for (i = 0; i < chip->ngpio; i++) {
+ int gpio = i + chip->base;
+ int reg;
+ const char *label, *pull, *powerdomain;
+
+ /* We report the GPIO even if it's not requested since
+ * we're also reporting things like alternate
+ * functions which apply even when the GPIO is not in
+ * use as a GPIO.
+ */
+ label = gpiochip_is_requested(chip, i);
+ if (!label)
+ label = "Unrequested";
+
+ seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label);
+
+ reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i);
+ if (reg < 0) {
+ dev_err(wm831x->dev,
+ "GPIO control %d read failed: %d\n",
+ gpio, reg);
+ seq_printf(s, "\n");
+ continue;
+ }
+
+ switch (reg & WM831X_GPN_PULL_MASK) {
+ case WM831X_GPIO_PULL_NONE:
+ pull = "nopull";
+ break;
+ case WM831X_GPIO_PULL_DOWN:
+ pull = "pulldown";
+ break;
+ case WM831X_GPIO_PULL_UP:
+ pull = "pullup";
+ break;
+ default:
+ pull = "INVALID PULL";
+ break;
+ }
+
+ switch (i + 1) {
+ case 1 ... 3:
+ case 7 ... 9:
+ if (reg & WM831X_GPN_PWR_DOM)
+ powerdomain = "VPMIC";
+ else
+ powerdomain = "DBVDD";
+ break;
+
+ case 4 ... 6:
+ case 10 ... 12:
+ if (reg & WM831X_GPN_PWR_DOM)
+ powerdomain = "SYSVDD";
+ else
+ powerdomain = "DBVDD";
+ break;
+
+ case 13 ... 16:
+ powerdomain = "TPVDD";
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ tristated = reg & WM831X_GPN_TRI;
+ if (wm831x->has_gpio_ena)
+ tristated = !tristated;
+
+ seq_printf(s, " %s %s %s %s%s\n"
+ " %s%s (0x%4x)\n",
+ reg & WM831X_GPN_DIR ? "in" : "out",
+ wm831x_gpio_get(chip, i) ? "high" : "low",
+ pull,
+ powerdomain,
+ reg & WM831X_GPN_POL ? "" : " inverted",
+ reg & WM831X_GPN_OD ? "open-drain" : "CMOS",
+ tristated ? " tristated" : "",
+ reg);
+ }
+}
+#else
+#define wm831x_gpio_dbg_show NULL
+#endif
+
+static struct gpio_chip template_chip = {
+ .label = "wm831x",
+ .owner = THIS_MODULE,
+ .direction_input = wm831x_gpio_direction_in,
+ .get = wm831x_gpio_get,
+ .direction_output = wm831x_gpio_direction_out,
+ .set = wm831x_gpio_set,
+ .to_irq = wm831x_gpio_to_irq,
+ .set_debounce = wm831x_gpio_set_debounce,
+ .dbg_show = wm831x_gpio_dbg_show,
+ .can_sleep = true,
+};
+
+static int wm831x_gpio_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
+ struct wm831x_gpio *wm831x_gpio;
+ int ret;
+
+ wm831x_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm831x_gpio),
+ GFP_KERNEL);
+ if (wm831x_gpio == NULL)
+ return -ENOMEM;
+
+ wm831x_gpio->wm831x = wm831x;
+ wm831x_gpio->gpio_chip = template_chip;
+ wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio;
+ wm831x_gpio->gpio_chip.dev = &pdev->dev;
+ if (pdata && pdata->gpio_base)
+ wm831x_gpio->gpio_chip.base = pdata->gpio_base;
+ else
+ wm831x_gpio->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm831x_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, wm831x_gpio);
+
+ return ret;
+}
+
+static int wm831x_gpio_remove(struct platform_device *pdev)
+{
+ struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&wm831x_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver wm831x_gpio_driver = {
+ .driver.name = "wm831x-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = wm831x_gpio_probe,
+ .remove = wm831x_gpio_remove,
+};
+
+static int __init wm831x_gpio_init(void)
+{
+ return platform_driver_register(&wm831x_gpio_driver);
+}
+subsys_initcall(wm831x_gpio_init);
+
+static void __exit wm831x_gpio_exit(void)
+{
+ platform_driver_unregister(&wm831x_gpio_driver);
+}
+module_exit(wm831x_gpio_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("GPIO interface for WM831x PMICs");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-gpio");
diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c
new file mode 100644
index 000000000..060b89303
--- /dev/null
+++ b/drivers/gpio/gpio-wm8350.c
@@ -0,0 +1,174 @@
+/*
+ * gpiolib support for Wolfson WM835x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/gpio.h>
+
+struct wm8350_gpio_data {
+ struct wm8350 *wm8350;
+ struct gpio_chip gpio_chip;
+};
+
+static inline struct wm8350_gpio_data *to_wm8350_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8350_gpio_data, gpio_chip);
+}
+
+static int wm8350_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
+ struct wm8350 *wm8350 = wm8350_gpio->wm8350;
+
+ return wm8350_set_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O,
+ 1 << offset);
+}
+
+static int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
+ struct wm8350 *wm8350 = wm8350_gpio->wm8350;
+ int ret;
+
+ ret = wm8350_reg_read(wm8350, WM8350_GPIO_LEVEL);
+ if (ret < 0)
+ return ret;
+
+ if (ret & (1 << offset))
+ return 1;
+ else
+ return 0;
+}
+
+static void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
+ struct wm8350 *wm8350 = wm8350_gpio->wm8350;
+
+ if (value)
+ wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset);
+ else
+ wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset);
+}
+
+static int wm8350_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
+ struct wm8350 *wm8350 = wm8350_gpio->wm8350;
+ int ret;
+
+ ret = wm8350_clear_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O,
+ 1 << offset);
+ if (ret < 0)
+ return ret;
+
+ /* Don't have an atomic direction/value setup */
+ wm8350_gpio_set(chip, offset, value);
+
+ return 0;
+}
+
+static int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
+ struct wm8350 *wm8350 = wm8350_gpio->wm8350;
+
+ if (!wm8350->irq_base)
+ return -EINVAL;
+
+ return wm8350->irq_base + WM8350_IRQ_GPIO(offset);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "wm8350",
+ .owner = THIS_MODULE,
+ .direction_input = wm8350_gpio_direction_in,
+ .get = wm8350_gpio_get,
+ .direction_output = wm8350_gpio_direction_out,
+ .set = wm8350_gpio_set,
+ .to_irq = wm8350_gpio_to_irq,
+ .can_sleep = true,
+};
+
+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);
+ if (wm8350_gpio == NULL)
+ return -ENOMEM;
+
+ wm8350_gpio->wm8350 = wm8350;
+ wm8350_gpio->gpio_chip = template_chip;
+ wm8350_gpio->gpio_chip.ngpio = 13;
+ wm8350_gpio->gpio_chip.dev = &pdev->dev;
+ if (pdata && pdata->gpio_base)
+ wm8350_gpio->gpio_chip.base = pdata->gpio_base;
+ else
+ wm8350_gpio->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8350_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, wm8350_gpio);
+
+ return ret;
+}
+
+static int wm8350_gpio_remove(struct platform_device *pdev)
+{
+ struct wm8350_gpio_data *wm8350_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&wm8350_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver wm8350_gpio_driver = {
+ .driver.name = "wm8350-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = wm8350_gpio_probe,
+ .remove = wm8350_gpio_remove,
+};
+
+static int __init wm8350_gpio_init(void)
+{
+ return platform_driver_register(&wm8350_gpio_driver);
+}
+subsys_initcall(wm8350_gpio_init);
+
+static void __exit wm8350_gpio_exit(void)
+{
+ platform_driver_unregister(&wm8350_gpio_driver);
+}
+module_exit(wm8350_gpio_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("GPIO interface for WM8350 PMICs");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-gpio");
diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c
new file mode 100644
index 000000000..6f5e42db4
--- /dev/null
+++ b/drivers/gpio/gpio-wm8994.c
@@ -0,0 +1,314 @@
+/*
+ * gpiolib support for Wolfson WM8994
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/gpio.h>
+#include <linux/mfd/wm8994/registers.h>
+
+struct wm8994_gpio {
+ struct wm8994 *wm8994;
+ struct gpio_chip gpio_chip;
+};
+
+static inline struct wm8994_gpio *to_wm8994_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8994_gpio, gpio_chip);
+}
+
+static int wm8994_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
+ struct wm8994 *wm8994 = wm8994_gpio->wm8994;
+
+ switch (wm8994->type) {
+ case WM8958:
+ switch (offset) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 6:
+ return -EINVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
+ struct wm8994 *wm8994 = wm8994_gpio->wm8994;
+
+ return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,
+ WM8994_GPN_DIR, WM8994_GPN_DIR);
+}
+
+static int wm8994_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
+ struct wm8994 *wm8994 = wm8994_gpio->wm8994;
+ int ret;
+
+ ret = wm8994_reg_read(wm8994, WM8994_GPIO_1 + offset);
+ if (ret < 0)
+ return ret;
+
+ if (ret & WM8994_GPN_LVL)
+ return 1;
+ else
+ return 0;
+}
+
+static int wm8994_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
+ struct wm8994 *wm8994 = wm8994_gpio->wm8994;
+
+ if (value)
+ value = WM8994_GPN_LVL;
+
+ return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,
+ WM8994_GPN_DIR | WM8994_GPN_LVL, value);
+}
+
+static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
+ struct wm8994 *wm8994 = wm8994_gpio->wm8994;
+
+ if (value)
+ value = WM8994_GPN_LVL;
+
+ wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value);
+}
+
+static int wm8994_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
+ struct wm8994 *wm8994 = wm8994_gpio->wm8994;
+
+ return regmap_irq_get_virq(wm8994->irq_data, offset);
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+static const char *wm8994_gpio_fn(u16 fn)
+{
+ switch (fn) {
+ case WM8994_GP_FN_PIN_SPECIFIC:
+ return "pin-specific";
+ case WM8994_GP_FN_GPIO:
+ return "GPIO";
+ case WM8994_GP_FN_SDOUT:
+ return "SDOUT";
+ case WM8994_GP_FN_IRQ:
+ return "IRQ";
+ case WM8994_GP_FN_TEMPERATURE:
+ return "Temperature";
+ case WM8994_GP_FN_MICBIAS1_DET:
+ return "MICBIAS1 detect";
+ case WM8994_GP_FN_MICBIAS1_SHORT:
+ return "MICBIAS1 short";
+ case WM8994_GP_FN_MICBIAS2_DET:
+ return "MICBIAS2 detect";
+ case WM8994_GP_FN_MICBIAS2_SHORT:
+ return "MICBIAS2 short";
+ case WM8994_GP_FN_FLL1_LOCK:
+ return "FLL1 lock";
+ case WM8994_GP_FN_FLL2_LOCK:
+ return "FLL2 lock";
+ case WM8994_GP_FN_SRC1_LOCK:
+ return "SRC1 lock";
+ case WM8994_GP_FN_SRC2_LOCK:
+ return "SRC2 lock";
+ case WM8994_GP_FN_DRC1_ACT:
+ return "DRC1 activity";
+ case WM8994_GP_FN_DRC2_ACT:
+ return "DRC2 activity";
+ case WM8994_GP_FN_DRC3_ACT:
+ return "DRC3 activity";
+ case WM8994_GP_FN_WSEQ_STATUS:
+ return "Write sequencer";
+ case WM8994_GP_FN_FIFO_ERROR:
+ return "FIFO error";
+ case WM8994_GP_FN_OPCLK:
+ return "OPCLK";
+ case WM8994_GP_FN_THW:
+ return "Thermal warning";
+ case WM8994_GP_FN_DCS_DONE:
+ return "DC servo";
+ case WM8994_GP_FN_FLL1_OUT:
+ return "FLL1 output";
+ case WM8994_GP_FN_FLL2_OUT:
+ return "FLL1 output";
+ default:
+ return "Unknown";
+ }
+}
+
+static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
+ struct wm8994 *wm8994 = wm8994_gpio->wm8994;
+ int i;
+
+ for (i = 0; i < chip->ngpio; i++) {
+ int gpio = i + chip->base;
+ int reg;
+ const char *label;
+
+ /* We report the GPIO even if it's not requested since
+ * we're also reporting things like alternate
+ * functions which apply even when the GPIO is not in
+ * use as a GPIO.
+ */
+ label = gpiochip_is_requested(chip, i);
+ if (!label)
+ label = "Unrequested";
+
+ seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label);
+
+ reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i);
+ if (reg < 0) {
+ dev_err(wm8994->dev,
+ "GPIO control %d read failed: %d\n",
+ gpio, reg);
+ seq_printf(s, "\n");
+ continue;
+ }
+
+ if (reg & WM8994_GPN_DIR)
+ seq_printf(s, "in ");
+ else
+ seq_printf(s, "out ");
+
+ if (reg & WM8994_GPN_PU)
+ seq_printf(s, "pull up ");
+
+ if (reg & WM8994_GPN_PD)
+ seq_printf(s, "pull down ");
+
+ if (reg & WM8994_GPN_POL)
+ seq_printf(s, "inverted ");
+ else
+ seq_printf(s, "noninverted ");
+
+ if (reg & WM8994_GPN_OP_CFG)
+ seq_printf(s, "open drain ");
+ else
+ seq_printf(s, "CMOS ");
+
+ seq_printf(s, "%s (%x)\n",
+ wm8994_gpio_fn(reg & WM8994_GPN_FN_MASK), reg);
+ }
+}
+#else
+#define wm8994_gpio_dbg_show NULL
+#endif
+
+static struct gpio_chip template_chip = {
+ .label = "wm8994",
+ .owner = THIS_MODULE,
+ .request = wm8994_gpio_request,
+ .direction_input = wm8994_gpio_direction_in,
+ .get = wm8994_gpio_get,
+ .direction_output = wm8994_gpio_direction_out,
+ .set = wm8994_gpio_set,
+ .to_irq = wm8994_gpio_to_irq,
+ .dbg_show = wm8994_gpio_dbg_show,
+ .can_sleep = true,
+};
+
+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);
+ if (wm8994_gpio == NULL)
+ return -ENOMEM;
+
+ wm8994_gpio->wm8994 = wm8994;
+ wm8994_gpio->gpio_chip = template_chip;
+ wm8994_gpio->gpio_chip.ngpio = WM8994_GPIO_MAX;
+ wm8994_gpio->gpio_chip.dev = &pdev->dev;
+ if (pdata && pdata->gpio_base)
+ wm8994_gpio->gpio_chip.base = pdata->gpio_base;
+ else
+ wm8994_gpio->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8994_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
+ ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, wm8994_gpio);
+
+ return ret;
+
+err:
+ return ret;
+}
+
+static int wm8994_gpio_remove(struct platform_device *pdev)
+{
+ struct wm8994_gpio *wm8994_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&wm8994_gpio->gpio_chip);
+ return 0;
+}
+
+static struct platform_driver wm8994_gpio_driver = {
+ .driver.name = "wm8994-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = wm8994_gpio_probe,
+ .remove = wm8994_gpio_remove,
+};
+
+static int __init wm8994_gpio_init(void)
+{
+ return platform_driver_register(&wm8994_gpio_driver);
+}
+subsys_initcall(wm8994_gpio_init);
+
+static void __exit wm8994_gpio_exit(void)
+{
+ platform_driver_unregister(&wm8994_gpio_driver);
+}
+module_exit(wm8994_gpio_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("GPIO interface for WM8994");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8994-gpio");
diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c
new file mode 100644
index 000000000..fb9d29a5d
--- /dev/null
+++ b/drivers/gpio/gpio-xgene-sb.c
@@ -0,0 +1,160 @@
+/*
+ * AppliedMicro X-Gene SoC GPIO-Standby Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tin Huynh <tnhuynh@apm.com>.
+ * Y Vo <yvo@apm.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define XGENE_MAX_GPIO_DS 22
+#define XGENE_MAX_GPIO_DS_IRQ 6
+
+#define GPIO_MASK(x) (1U << ((x) % 32))
+
+#define MPA_GPIO_INT_LVL 0x0290
+#define MPA_GPIO_OE_ADDR 0x029c
+#define MPA_GPIO_OUT_ADDR 0x02a0
+#define MPA_GPIO_IN_ADDR 0x02a4
+#define MPA_GPIO_SEL_LO 0x0294
+
+/**
+ * struct xgene_gpio_sb - GPIO-Standby private data structure.
+ * @bgc: memory-mapped GPIO controllers.
+ * @irq: Mapping GPIO pins and interrupt number
+ * nirq: Number of GPIO pins that supports interrupt
+ */
+struct xgene_gpio_sb {
+ struct bgpio_chip bgc;
+ u32 *irq;
+ u32 nirq;
+};
+
+static inline struct xgene_gpio_sb *to_xgene_gpio_sb(struct gpio_chip *gc)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ return container_of(bgc, struct xgene_gpio_sb, bgc);
+}
+
+static void xgene_gpio_set_bit(struct bgpio_chip *bgc, void __iomem *reg, u32 gpio, int val)
+{
+ u32 data;
+
+ data = bgc->read_reg(reg);
+ if (val)
+ data |= GPIO_MASK(gpio);
+ else
+ data &= ~GPIO_MASK(gpio);
+ bgc->write_reg(reg, data);
+}
+
+static int apm_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio)
+{
+ struct xgene_gpio_sb *priv = to_xgene_gpio_sb(gc);
+
+ if (priv->irq[gpio])
+ return priv->irq[gpio];
+
+ return -ENXIO;
+}
+
+static int xgene_gpio_sb_probe(struct platform_device *pdev)
+{
+ struct xgene_gpio_sb *priv;
+ u32 ret, i;
+ u32 default_lines[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D};
+ struct resource *res;
+ void __iomem *regs;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ ret = bgpio_init(&priv->bgc, &pdev->dev, 4,
+ regs + MPA_GPIO_IN_ADDR,
+ regs + MPA_GPIO_OUT_ADDR, NULL,
+ regs + MPA_GPIO_OE_ADDR, NULL, 0);
+ if (ret)
+ return ret;
+
+ priv->bgc.gc.to_irq = apm_gpio_sb_to_irq;
+ priv->bgc.gc.ngpio = XGENE_MAX_GPIO_DS;
+
+ priv->nirq = XGENE_MAX_GPIO_DS_IRQ;
+
+ priv->irq = devm_kzalloc(&pdev->dev, sizeof(u32) * XGENE_MAX_GPIO_DS,
+ GFP_KERNEL);
+ if (!priv->irq)
+ return -ENOMEM;
+ memset(priv->irq, 0, sizeof(u32) * XGENE_MAX_GPIO_DS);
+
+ for (i = 0; i < priv->nirq; i++) {
+ priv->irq[default_lines[i]] = platform_get_irq(pdev, i);
+ xgene_gpio_set_bit(&priv->bgc, regs + MPA_GPIO_SEL_LO,
+ default_lines[i] * 2, 1);
+ xgene_gpio_set_bit(&priv->bgc, regs + MPA_GPIO_INT_LVL, i, 1);
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = gpiochip_add(&priv->bgc.gc);
+ if (ret)
+ dev_err(&pdev->dev, "failed to register X-Gene GPIO Standby driver\n");
+ else
+ dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
+
+ return ret;
+}
+
+static int xgene_gpio_sb_remove(struct platform_device *pdev)
+{
+ struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);
+
+ return bgpio_remove(&priv->bgc);
+}
+
+static const struct of_device_id xgene_gpio_sb_of_match[] = {
+ {.compatible = "apm,xgene-gpio-sb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgene_gpio_sb_of_match);
+
+static struct platform_driver xgene_gpio_sb_driver = {
+ .driver = {
+ .name = "xgene-gpio-sb",
+ .of_match_table = xgene_gpio_sb_of_match,
+ },
+ .probe = xgene_gpio_sb_probe,
+ .remove = xgene_gpio_sb_remove,
+};
+module_platform_driver(xgene_gpio_sb_driver);
+
+MODULE_AUTHOR("AppliedMicro");
+MODULE_DESCRIPTION("APM X-Gene GPIO Standby driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c
new file mode 100644
index 000000000..18a8182d4
--- /dev/null
+++ b/drivers/gpio/gpio-xgene.c
@@ -0,0 +1,243 @@
+/*
+ * AppliedMicro X-Gene SoC GPIO Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Feng Kan <fkan@apm.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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/driver.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+#define GPIO_SET_DR_OFFSET 0x0C
+#define GPIO_DATA_OFFSET 0x14
+#define GPIO_BANK_STRIDE 0x0C
+
+#define XGENE_GPIOS_PER_BANK 16
+#define XGENE_MAX_GPIO_BANKS 3
+#define XGENE_MAX_GPIOS (XGENE_GPIOS_PER_BANK * XGENE_MAX_GPIO_BANKS)
+
+#define GPIO_BIT_OFFSET(x) (x % XGENE_GPIOS_PER_BANK)
+#define GPIO_BANK_OFFSET(x) ((x / XGENE_GPIOS_PER_BANK) * GPIO_BANK_STRIDE)
+
+struct xgene_gpio {
+ struct gpio_chip chip;
+ void __iomem *base;
+ spinlock_t lock;
+#ifdef CONFIG_PM
+ u32 set_dr_val[XGENE_MAX_GPIO_BANKS];
+#endif
+};
+
+static inline struct xgene_gpio *to_xgene_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct xgene_gpio, chip);
+}
+
+static int xgene_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long bank_offset;
+ u32 bit_offset;
+
+ bank_offset = GPIO_DATA_OFFSET + GPIO_BANK_OFFSET(offset);
+ bit_offset = GPIO_BIT_OFFSET(offset);
+ return !!(ioread32(chip->base + bank_offset) & BIT(bit_offset));
+}
+
+static void __xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long bank_offset;
+ u32 setval, bit_offset;
+
+ bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+ bit_offset = GPIO_BIT_OFFSET(offset) + XGENE_GPIOS_PER_BANK;
+
+ setval = ioread32(chip->base + bank_offset);
+ if (val)
+ setval |= BIT(bit_offset);
+ else
+ setval &= ~BIT(bit_offset);
+ iowrite32(setval, chip->base + bank_offset);
+}
+
+static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ __xgene_gpio_set(gc, offset, val);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int xgene_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long flags, bank_offset;
+ u32 dirval, bit_offset;
+
+ bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+ bit_offset = GPIO_BIT_OFFSET(offset);
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ dirval = ioread32(chip->base + bank_offset);
+ dirval |= BIT(bit_offset);
+ iowrite32(dirval, chip->base + bank_offset);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int xgene_gpio_dir_out(struct gpio_chip *gc,
+ unsigned int offset, int val)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long flags, bank_offset;
+ u32 dirval, bit_offset;
+
+ bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+ bit_offset = GPIO_BIT_OFFSET(offset);
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ dirval = ioread32(chip->base + bank_offset);
+ dirval &= ~BIT(bit_offset);
+ iowrite32(dirval, chip->base + bank_offset);
+ __xgene_gpio_set(gc, offset, val);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int xgene_gpio_suspend(struct device *dev)
+{
+ struct xgene_gpio *gpio = dev_get_drvdata(dev);
+ unsigned long bank_offset;
+ unsigned int bank;
+
+ for (bank = 0; bank < XGENE_MAX_GPIO_BANKS; bank++) {
+ bank_offset = GPIO_SET_DR_OFFSET + bank * GPIO_BANK_STRIDE;
+ gpio->set_dr_val[bank] = ioread32(gpio->base + bank_offset);
+ }
+ return 0;
+}
+
+static int xgene_gpio_resume(struct device *dev)
+{
+ struct xgene_gpio *gpio = dev_get_drvdata(dev);
+ unsigned long bank_offset;
+ unsigned int bank;
+
+ for (bank = 0; bank < XGENE_MAX_GPIO_BANKS; bank++) {
+ bank_offset = GPIO_SET_DR_OFFSET + bank * GPIO_BANK_STRIDE;
+ iowrite32(gpio->set_dr_val[bank], gpio->base + bank_offset);
+ }
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume);
+#define XGENE_GPIO_PM_OPS (&xgene_gpio_pm)
+#else
+#define XGENE_GPIO_PM_OPS NULL
+#endif
+
+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;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gpio->base = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (!gpio->base) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ gpio->chip.ngpio = XGENE_MAX_GPIOS;
+
+ spin_lock_init(&gpio->lock);
+ gpio->chip.dev = &pdev->dev;
+ gpio->chip.direction_input = xgene_gpio_dir_in;
+ gpio->chip.direction_output = xgene_gpio_dir_out;
+ gpio->chip.get = xgene_gpio_get;
+ gpio->chip.set = xgene_gpio_set;
+ gpio->chip.label = dev_name(&pdev->dev);
+ gpio->chip.base = -1;
+
+ platform_set_drvdata(pdev, gpio);
+
+ err = gpiochip_add(&gpio->chip);
+ 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;
+}
+
+static int xgene_gpio_remove(struct platform_device *pdev)
+{
+ struct xgene_gpio *gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gpio->chip);
+ return 0;
+}
+
+static const struct of_device_id xgene_gpio_of_match[] = {
+ { .compatible = "apm,xgene-gpio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgene_gpio_of_match);
+
+static struct platform_driver xgene_gpio_driver = {
+ .driver = {
+ .name = "xgene-gpio",
+ .of_match_table = xgene_gpio_of_match,
+ .pm = XGENE_GPIO_PM_OPS,
+ },
+ .probe = xgene_gpio_probe,
+ .remove = xgene_gpio_remove,
+};
+
+module_platform_driver(xgene_gpio_driver);
+
+MODULE_AUTHOR("Feng Kan <fkan@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
new file mode 100644
index 000000000..61243d177
--- /dev/null
+++ b/drivers/gpio/gpio-xilinx.c
@@ -0,0 +1,356 @@
+/*
+ * Xilinx gpio driver for xps/axi_gpio IP.
+ *
+ * Copyright 2008 - 2013 Xilinx, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/gpio.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
+
+/* Read/Write access to the GPIO registers */
+#if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_X86)
+# define xgpio_readreg(offset) readl(offset)
+# define xgpio_writereg(offset, val) writel(val, offset)
+#else
+# define xgpio_readreg(offset) __raw_readl(offset)
+# define xgpio_writereg(offset, val) __raw_writel(val, offset)
+#endif
+
+/**
+ * struct xgpio_instance - Stores information about GPIO device
+ * @mmchip: OF GPIO chip for memory mapped banks
+ * @gpio_state: GPIO state shadow register
+ * @gpio_dir: GPIO direction shadow register
+ * @gpio_lock: Lock used for synchronization
+ * @inited: True if the port has been inited
+ */
+struct xgpio_instance {
+ struct of_mm_gpio_chip mmchip;
+ unsigned int gpio_width[2];
+ u32 gpio_state[2];
+ u32 gpio_dir[2];
+ spinlock_t gpio_lock[2];
+};
+
+static inline int xgpio_index(struct xgpio_instance *chip, int gpio)
+{
+ if (gpio >= chip->gpio_width[0])
+ return 1;
+
+ return 0;
+}
+
+static inline int xgpio_regoffset(struct xgpio_instance *chip, int gpio)
+{
+ if (xgpio_index(chip, gpio))
+ return XGPIO_CHANNEL_OFFSET;
+
+ return 0;
+}
+
+static inline int xgpio_offset(struct xgpio_instance *chip, int gpio)
+{
+ if (xgpio_index(chip, gpio))
+ return gpio - chip->gpio_width[0];
+
+ return gpio;
+}
+
+/**
+ * xgpio_get - Read the specified signal of the GPIO device.
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ *
+ * This function reads the specified signal of the GPIO device.
+ *
+ * Return:
+ * 0 if direction of GPIO signals is set as input otherwise it
+ * returns negative error value.
+ */
+static int xgpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct xgpio_instance *chip =
+ container_of(mm_gc, struct xgpio_instance, mmchip);
+ u32 val;
+
+ val = xgpio_readreg(mm_gc->regs + XGPIO_DATA_OFFSET +
+ xgpio_regoffset(chip, gpio));
+
+ return !!(val & BIT(xgpio_offset(chip, gpio)));
+}
+
+/**
+ * xgpio_set - Write the specified signal of the GPIO device.
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ * @val: Value to be written to specified signal.
+ *
+ * This function writes the specified value in to the specified signal of the
+ * GPIO device.
+ */
+static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ unsigned long flags;
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct xgpio_instance *chip =
+ container_of(mm_gc, struct xgpio_instance, mmchip);
+ int index = xgpio_index(chip, gpio);
+ int offset = xgpio_offset(chip, gpio);
+
+ spin_lock_irqsave(&chip->gpio_lock[index], 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);
+
+ xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
+ xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
+
+ spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+}
+
+/**
+ * xgpio_dir_in - Set the direction of the specified GPIO signal as input.
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ *
+ * Return:
+ * 0 - if direction of GPIO signals is set as input
+ * otherwise it returns negative error value.
+ */
+static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ unsigned long flags;
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct xgpio_instance *chip =
+ container_of(mm_gc, struct xgpio_instance, mmchip);
+ int index = xgpio_index(chip, gpio);
+ int offset = xgpio_offset(chip, gpio);
+
+ spin_lock_irqsave(&chip->gpio_lock[index], flags);
+
+ /* Set the GPIO bit in shadow register and set direction as input */
+ chip->gpio_dir[index] |= BIT(offset);
+ xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET +
+ xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
+
+ spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+
+ return 0;
+}
+
+/**
+ * xgpio_dir_out - Set the direction of the specified GPIO signal as output.
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpio: GPIO signal number.
+ * @val: Value to be written to specified signal.
+ *
+ * This function sets the direction of specified GPIO signal as output.
+ *
+ * Return:
+ * If all GPIO signals of GPIO chip is configured as input then it returns
+ * error otherwise it returns 0.
+ */
+static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ unsigned long flags;
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct xgpio_instance *chip =
+ container_of(mm_gc, struct xgpio_instance, mmchip);
+ int index = xgpio_index(chip, gpio);
+ int offset = xgpio_offset(chip, gpio);
+
+ spin_lock_irqsave(&chip->gpio_lock[index], flags);
+
+ /* Write state of GPIO signal */
+ if (val)
+ chip->gpio_state[index] |= BIT(offset);
+ else
+ chip->gpio_state[index] &= ~BIT(offset);
+ xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
+ xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
+
+ /* Clear the GPIO bit in shadow register and set direction as output */
+ chip->gpio_dir[index] &= ~BIT(offset);
+ xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET +
+ xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
+
+ spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+
+ return 0;
+}
+
+/**
+ * xgpio_save_regs - Set initial values of GPIO pins
+ * @mm_gc: Pointer to memory mapped GPIO chip structure
+ */
+static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+ struct xgpio_instance *chip =
+ container_of(mm_gc, struct xgpio_instance, mmchip);
+
+ xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state[0]);
+ xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir[0]);
+
+ if (!chip->gpio_width[1])
+ return;
+
+ xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET + XGPIO_TRI_OFFSET,
+ chip->gpio_state[1]);
+ xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET + XGPIO_TRI_OFFSET,
+ chip->gpio_dir[1]);
+}
+
+/**
+ * xgpio_remove - Remove method for the GPIO device.
+ * @pdev: pointer to the platform device
+ *
+ * This function remove gpiochips and frees all the allocated resources.
+ */
+static int xgpio_remove(struct platform_device *pdev)
+{
+ struct xgpio_instance *chip = platform_get_drvdata(pdev);
+
+ of_mm_gpiochip_remove(&chip->mmchip);
+
+ return 0;
+}
+
+/**
+ * xgpio_of_probe - Probe method for the GPIO device.
+ * @pdev: pointer to the platform device
+ *
+ * Return:
+ * It returns 0, if the driver is bound to the GPIO device, or
+ * a negative value if there is an error.
+ */
+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;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, chip);
+
+ /* Update GPIO state shadow register with default value */
+ of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state[0]);
+
+ /* 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;
+
+ /*
+ * 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;
+
+ spin_lock_init(&chip->gpio_lock[0]);
+
+ if (of_property_read_u32(np, "xlnx,is-dual", &is_dual))
+ is_dual = 0;
+
+ if (is_dual) {
+ /* Update GPIO state shadow register with default value */
+ of_property_read_u32(np, "xlnx,dout-default-2",
+ &chip->gpio_state[1]);
+
+ /* 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;
+
+ /*
+ * 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;
+
+ spin_lock_init(&chip->gpio_lock[1]);
+ }
+
+ chip->mmchip.gc.ngpio = chip->gpio_width[0] + chip->gpio_width[1];
+ chip->mmchip.gc.dev = &pdev->dev;
+ chip->mmchip.gc.direction_input = xgpio_dir_in;
+ chip->mmchip.gc.direction_output = xgpio_dir_out;
+ chip->mmchip.gc.get = xgpio_get;
+ chip->mmchip.gc.set = xgpio_set;
+
+ chip->mmchip.save_regs = xgpio_save_regs;
+
+ /* Call the OF gpio helper to setup and register the GPIO device */
+ status = of_mm_gpiochip_add(np, &chip->mmchip);
+ if (status) {
+ pr_err("%s: error in probe function with status %d\n",
+ np->full_name, status);
+ return status;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id xgpio_of_match[] = {
+ { .compatible = "xlnx,xps-gpio-1.00.a", },
+ { /* end of list */ },
+};
+
+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,
+ },
+};
+
+static int __init xgpio_init(void)
+{
+ return platform_driver_register(&xgpio_plat_driver);
+}
+
+subsys_initcall(xgpio_init);
+
+static void __exit xgpio_exit(void)
+{
+ platform_driver_unregister(&xgpio_plat_driver);
+}
+module_exit(xgpio_exit);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c
new file mode 100644
index 000000000..93ec95df6
--- /dev/null
+++ b/drivers/gpio/gpio-xtensa.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013 TangoTec Ltd.
+ * Author: Baruch Siach <baruch@tkos.co.il>
+ *
+ * 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.
+ *
+ * Driver for the Xtensa LX4 GPIO32 Option
+ *
+ * Documentation: Xtensa LX4 Microprocessor Data Book, Section 2.22
+ *
+ * GPIO32 is a standard optional extension to the Xtensa architecture core that
+ * provides preconfigured output and input ports for intra SoC signaling. The
+ * GPIO32 option is implemented as 32bit Tensilica Instruction Extension (TIE)
+ * output state called EXPSTATE, and 32bit input wire called IMPWIRE. This
+ * driver treats input and output states as two distinct devices.
+ *
+ * Access to GPIO32 specific instructions is controlled by the CPENABLE
+ * (Coprocessor Enable Bits) register. By default Xtensa Linux startup code
+ * disables access to all coprocessors. This driver sets the CPENABLE bit
+ * corresponding to GPIO32 before any GPIO32 specific instruction, and restores
+ * CPENABLE state after that.
+ *
+ * This driver is currently incompatible with SMP. The GPIO32 extension is not
+ * guaranteed to be available in all cores. Moreover, each core controls a
+ * different set of IO wires. A theoretical SMP aware version of this driver
+ * would need to have a per core workqueue to do the actual GPIO manipulation.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+
+#include <asm/coprocessor.h> /* CPENABLE read/write macros */
+
+#ifndef XCHAL_CP_ID_XTIOP
+#error GPIO32 option is not enabled for your xtensa core variant
+#endif
+
+#if XCHAL_HAVE_CP
+
+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));
+
+ return flags;
+}
+
+static inline void disable_cp(unsigned long flags, unsigned long cpenable)
+{
+ WSR_CPENABLE(cpenable);
+ local_irq_restore(flags);
+}
+
+#else
+
+static inline unsigned long enable_cp(unsigned long *cpenable)
+{
+ *cpenable = 0; /* avoid uninitialized value warning */
+ return 0;
+}
+
+static inline void disable_cp(unsigned long flags, unsigned long cpenable)
+{
+}
+
+#endif /* XCHAL_HAVE_CP */
+
+static int xtensa_impwire_get_direction(struct gpio_chip *gc, unsigned offset)
+{
+ return 1; /* input only */
+}
+
+static int xtensa_impwire_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ unsigned long flags, saved_cpenable;
+ u32 impwire;
+
+ flags = enable_cp(&saved_cpenable);
+ __asm__ __volatile__("read_impwire %0" : "=a" (impwire));
+ disable_cp(flags, saved_cpenable);
+
+ return !!(impwire & BIT(offset));
+}
+
+static void xtensa_impwire_set_value(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ BUG(); /* output only; should never be called */
+}
+
+static int xtensa_expstate_get_direction(struct gpio_chip *gc, unsigned offset)
+{
+ return 0; /* output only */
+}
+
+static int xtensa_expstate_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ unsigned long flags, saved_cpenable;
+ u32 expstate;
+
+ flags = enable_cp(&saved_cpenable);
+ __asm__ __volatile__("rur.expstate %0" : "=a" (expstate));
+ disable_cp(flags, saved_cpenable);
+
+ return !!(expstate & BIT(offset));
+}
+
+static void xtensa_expstate_set_value(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ unsigned long flags, saved_cpenable;
+ u32 mask = BIT(offset);
+ u32 val = value ? BIT(offset) : 0;
+
+ flags = enable_cp(&saved_cpenable);
+ __asm__ __volatile__("wrmsk_expstate %0, %1"
+ :: "a" (val), "a" (mask));
+ disable_cp(flags, saved_cpenable);
+}
+
+static struct gpio_chip impwire_chip = {
+ .label = "impwire",
+ .base = -1,
+ .ngpio = 32,
+ .get_direction = xtensa_impwire_get_direction,
+ .get = xtensa_impwire_get_value,
+ .set = xtensa_impwire_set_value,
+};
+
+static struct gpio_chip expstate_chip = {
+ .label = "expstate",
+ .base = -1,
+ .ngpio = 32,
+ .get_direction = xtensa_expstate_get_direction,
+ .get = xtensa_expstate_get_value,
+ .set = xtensa_expstate_set_value,
+};
+
+static int xtensa_gpio_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = gpiochip_add(&impwire_chip);
+ if (ret)
+ return ret;
+ return gpiochip_add(&expstate_chip);
+}
+
+static struct platform_driver xtensa_gpio_driver = {
+ .driver = {
+ .name = "xtensa-gpio",
+ },
+ .probe = xtensa_gpio_probe,
+};
+
+static int __init xtensa_gpio_init(void)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_simple("xtensa-gpio", 0, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return platform_driver_register(&xtensa_gpio_driver);
+}
+device_initcall(xtensa_gpio_init);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Xtensa LX4 GPIO32 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c
new file mode 100644
index 000000000..6f02d7c4c
--- /dev/null
+++ b/drivers/gpio/gpio-zevio.c
@@ -0,0 +1,235 @@
+/*
+ * GPIO controller in LSI ZEVIO SoCs.
+ *
+ * Author: Fabian Vogt <fabian@ritter-vogt.de>
+ *
+ * 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.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#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.h>
+
+/*
+ * Memory layout:
+ * This chip has four gpio sections, each controls 8 GPIOs.
+ * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10.
+ * Disclaimer: Reverse engineered!
+ * For more information refer to:
+ * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29
+ *
+ * 0x00-0x3F: Section 0
+ * +0x00: Masked interrupt status (read-only)
+ * +0x04: R: Interrupt status W: Reset interrupt status
+ * +0x08: R: Interrupt mask W: Mask interrupt
+ * +0x0C: W: Unmask interrupt (write-only)
+ * +0x10: Direction: I/O=1/0
+ * +0x14: Output
+ * +0x18: Input (read-only)
+ * +0x20: R: Level interrupt W: Set as level interrupt
+ * 0x40-0x7F: Section 1
+ * 0x80-0xBF: Section 2
+ * 0xC0-0xFF: Section 3
+ */
+
+#define ZEVIO_GPIO_SECTION_SIZE 0x40
+
+/* Offsets to various registers */
+#define ZEVIO_GPIO_INT_MASKED_STATUS 0x00
+#define ZEVIO_GPIO_INT_STATUS 0x04
+#define ZEVIO_GPIO_INT_UNMASK 0x08
+#define ZEVIO_GPIO_INT_MASK 0x0C
+#define ZEVIO_GPIO_DIRECTION 0x10
+#define ZEVIO_GPIO_OUTPUT 0x14
+#define ZEVIO_GPIO_INPUT 0x18
+#define ZEVIO_GPIO_INT_STICKY 0x20
+
+#define to_zevio_gpio(chip) container_of(to_of_mm_gpio_chip(chip), \
+ struct zevio_gpio, chip)
+
+/* Bit number of GPIO in its section */
+#define ZEVIO_GPIO_BIT(gpio) (gpio&7)
+
+struct zevio_gpio {
+ spinlock_t lock;
+ struct of_mm_gpio_chip chip;
+};
+
+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));
+}
+
+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));
+}
+
+/* Functions for struct gpio_chip */
+static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin)
+{
+ struct zevio_gpio *controller = to_zevio_gpio(chip);
+ u32 val, dir;
+
+ spin_lock(&controller->lock);
+ dir = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
+ if (dir & BIT(ZEVIO_GPIO_BIT(pin)))
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT);
+ else
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
+ spin_unlock(&controller->lock);
+
+ return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1;
+}
+
+static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct zevio_gpio *controller = to_zevio_gpio(chip);
+ u32 val;
+
+ spin_lock(&controller->lock);
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
+ if (value)
+ val |= BIT(ZEVIO_GPIO_BIT(pin));
+ else
+ val &= ~BIT(ZEVIO_GPIO_BIT(pin));
+
+ zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);
+ spin_unlock(&controller->lock);
+}
+
+static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
+{
+ struct zevio_gpio *controller = to_zevio_gpio(chip);
+ u32 val;
+
+ spin_lock(&controller->lock);
+
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
+ val |= BIT(ZEVIO_GPIO_BIT(pin));
+ zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val);
+
+ spin_unlock(&controller->lock);
+
+ return 0;
+}
+
+static int zevio_gpio_direction_output(struct gpio_chip *chip,
+ unsigned pin, int value)
+{
+ struct zevio_gpio *controller = to_zevio_gpio(chip);
+ u32 val;
+
+ spin_lock(&controller->lock);
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
+ if (value)
+ val |= BIT(ZEVIO_GPIO_BIT(pin));
+ else
+ val &= ~BIT(ZEVIO_GPIO_BIT(pin));
+
+ zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
+ val &= ~BIT(ZEVIO_GPIO_BIT(pin));
+ zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val);
+
+ spin_unlock(&controller->lock);
+
+ return 0;
+}
+
+static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ /*
+ * TODO: Implement IRQs.
+ * Not implemented yet due to weird lockups
+ */
+
+ return -ENXIO;
+}
+
+static struct gpio_chip zevio_gpio_chip = {
+ .direction_input = zevio_gpio_direction_input,
+ .direction_output = zevio_gpio_direction_output,
+ .set = zevio_gpio_set,
+ .get = zevio_gpio_get,
+ .to_irq = zevio_gpio_to_irq,
+ .base = 0,
+ .owner = THIS_MODULE,
+ .ngpio = 32,
+ .of_gpio_n_cells = 2,
+};
+
+/* Initialization */
+static int zevio_gpio_probe(struct platform_device *pdev)
+{
+ struct zevio_gpio *controller;
+ int status, i;
+
+ controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, controller);
+
+ /* Copy our reference */
+ controller->chip.gc = zevio_gpio_chip;
+ controller->chip.gc.dev = &pdev->dev;
+
+ status = of_mm_gpiochip_add(pdev->dev.of_node, &(controller->chip));
+ if (status) {
+ dev_err(&pdev->dev, "failed to add gpiochip: %d\n", status);
+ return status;
+ }
+
+ spin_lock_init(&controller->lock);
+
+ /* Disable interrupts, they only cause errors */
+ for (i = 0; i < controller->chip.gc.ngpio; i += 8)
+ zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF);
+
+ dev_dbg(controller->chip.gc.dev, "ZEVIO GPIO controller set up!\n");
+
+ return 0;
+}
+
+static int zevio_gpio_remove(struct platform_device *pdev)
+{
+ struct zevio_gpio *controller = platform_get_drvdata(pdev);
+
+ of_mm_gpiochip_remove(&controller->chip);
+
+ return 0;
+}
+
+static const struct of_device_id zevio_gpio_of_match[] = {
+ { .compatible = "lsi,zevio-gpio", },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, zevio_gpio_of_match);
+
+static struct platform_driver zevio_gpio_driver = {
+ .driver = {
+ .name = "gpio-zevio",
+ .of_match_table = zevio_gpio_of_match,
+ },
+ .probe = zevio_gpio_probe,
+ .remove = zevio_gpio_remove,
+};
+module_platform_driver(zevio_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Fabian Vogt <fabian@ritter-vogt.de>");
+MODULE_DESCRIPTION("LSI ZEVIO SoC GPIO driver");
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
new file mode 100644
index 000000000..184c4b1b2
--- /dev/null
+++ b/drivers/gpio/gpio-zynq.c
@@ -0,0 +1,727 @@
+/*
+ * Xilinx Zynq GPIO device driver
+ *
+ * Copyright (C) 2009 - 2014 Xilinx, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#define DRIVER_NAME "zynq-gpio"
+
+/* Maximum banks */
+#define ZYNQ_GPIO_MAX_BANK 4
+
+#define ZYNQ_GPIO_BANK0_NGPIO 32
+#define ZYNQ_GPIO_BANK1_NGPIO 22
+#define ZYNQ_GPIO_BANK2_NGPIO 32
+#define ZYNQ_GPIO_BANK3_NGPIO 32
+
+#define ZYNQ_GPIO_NR_GPIOS (ZYNQ_GPIO_BANK0_NGPIO + \
+ ZYNQ_GPIO_BANK1_NGPIO + \
+ ZYNQ_GPIO_BANK2_NGPIO + \
+ ZYNQ_GPIO_BANK3_NGPIO)
+
+#define ZYNQ_GPIO_BANK0_PIN_MIN 0
+#define ZYNQ_GPIO_BANK0_PIN_MAX (ZYNQ_GPIO_BANK0_PIN_MIN + \
+ ZYNQ_GPIO_BANK0_NGPIO - 1)
+#define ZYNQ_GPIO_BANK1_PIN_MIN (ZYNQ_GPIO_BANK0_PIN_MAX + 1)
+#define ZYNQ_GPIO_BANK1_PIN_MAX (ZYNQ_GPIO_BANK1_PIN_MIN + \
+ ZYNQ_GPIO_BANK1_NGPIO - 1)
+#define ZYNQ_GPIO_BANK2_PIN_MIN (ZYNQ_GPIO_BANK1_PIN_MAX + 1)
+#define ZYNQ_GPIO_BANK2_PIN_MAX (ZYNQ_GPIO_BANK2_PIN_MIN + \
+ ZYNQ_GPIO_BANK2_NGPIO - 1)
+#define ZYNQ_GPIO_BANK3_PIN_MIN (ZYNQ_GPIO_BANK2_PIN_MAX + 1)
+#define ZYNQ_GPIO_BANK3_PIN_MAX (ZYNQ_GPIO_BANK3_PIN_MIN + \
+ ZYNQ_GPIO_BANK3_NGPIO - 1)
+
+
+/* Register offsets for the GPIO device */
+/* LSW Mask & Data -WO */
+#define ZYNQ_GPIO_DATA_LSW_OFFSET(BANK) (0x000 + (8 * BANK))
+/* MSW Mask & Data -WO */
+#define ZYNQ_GPIO_DATA_MSW_OFFSET(BANK) (0x004 + (8 * BANK))
+/* Data Register-RW */
+#define ZYNQ_GPIO_DATA_RO_OFFSET(BANK) (0x060 + (4 * BANK))
+/* Direction mode reg-RW */
+#define ZYNQ_GPIO_DIRM_OFFSET(BANK) (0x204 + (0x40 * BANK))
+/* Output enable reg-RW */
+#define ZYNQ_GPIO_OUTEN_OFFSET(BANK) (0x208 + (0x40 * BANK))
+/* Interrupt mask reg-RO */
+#define ZYNQ_GPIO_INTMASK_OFFSET(BANK) (0x20C + (0x40 * BANK))
+/* Interrupt enable reg-WO */
+#define ZYNQ_GPIO_INTEN_OFFSET(BANK) (0x210 + (0x40 * BANK))
+/* Interrupt disable reg-WO */
+#define ZYNQ_GPIO_INTDIS_OFFSET(BANK) (0x214 + (0x40 * BANK))
+/* Interrupt status reg-RO */
+#define ZYNQ_GPIO_INTSTS_OFFSET(BANK) (0x218 + (0x40 * BANK))
+/* Interrupt type reg-RW */
+#define ZYNQ_GPIO_INTTYPE_OFFSET(BANK) (0x21C + (0x40 * BANK))
+/* Interrupt polarity reg-RW */
+#define ZYNQ_GPIO_INTPOL_OFFSET(BANK) (0x220 + (0x40 * BANK))
+/* Interrupt on any, reg-RW */
+#define ZYNQ_GPIO_INTANY_OFFSET(BANK) (0x224 + (0x40 * BANK))
+
+/* Disable all interrupts mask */
+#define ZYNQ_GPIO_IXR_DISABLE_ALL 0xFFFFFFFF
+
+/* Mid pin number of a bank */
+#define ZYNQ_GPIO_MID_PIN_NUM 16
+
+/* GPIO upper 16 bit mask */
+#define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000
+
+/**
+ * struct zynq_gpio - gpio device private data structure
+ * @chip: instance of the gpio_chip
+ * @base_addr: base address of the GPIO device
+ * @clk: clock resource for this controller
+ * @irq: interrupt for the GPIO device
+ */
+struct zynq_gpio {
+ struct gpio_chip chip;
+ void __iomem *base_addr;
+ struct clk *clk;
+ int irq;
+};
+
+static struct irq_chip zynq_gpio_level_irqchip;
+static struct irq_chip zynq_gpio_edge_irqchip;
+/**
+ * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank
+ * for a given pin in the GPIO device
+ * @pin_num: gpio pin number within the device
+ * @bank_num: an output parameter used to return the bank number of the gpio
+ * pin
+ * @bank_pin_num: an output parameter used to return pin number within a bank
+ * for the given gpio pin
+ *
+ * Returns the bank number and pin offset within the bank.
+ */
+static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
+ unsigned int *bank_num,
+ unsigned int *bank_pin_num)
+{
+ switch (pin_num) {
+ case ZYNQ_GPIO_BANK0_PIN_MIN ... ZYNQ_GPIO_BANK0_PIN_MAX:
+ *bank_num = 0;
+ *bank_pin_num = pin_num;
+ break;
+ case ZYNQ_GPIO_BANK1_PIN_MIN ... ZYNQ_GPIO_BANK1_PIN_MAX:
+ *bank_num = 1;
+ *bank_pin_num = pin_num - ZYNQ_GPIO_BANK1_PIN_MIN;
+ break;
+ case ZYNQ_GPIO_BANK2_PIN_MIN ... ZYNQ_GPIO_BANK2_PIN_MAX:
+ *bank_num = 2;
+ *bank_pin_num = pin_num - ZYNQ_GPIO_BANK2_PIN_MIN;
+ break;
+ case ZYNQ_GPIO_BANK3_PIN_MIN ... ZYNQ_GPIO_BANK3_PIN_MAX:
+ *bank_num = 3;
+ *bank_pin_num = pin_num - ZYNQ_GPIO_BANK3_PIN_MIN;
+ break;
+ default:
+ WARN(true, "invalid GPIO pin number: %u", pin_num);
+ *bank_num = 0;
+ *bank_pin_num = 0;
+ break;
+ }
+}
+
+static const unsigned int zynq_gpio_bank_offset[] = {
+ ZYNQ_GPIO_BANK0_PIN_MIN,
+ ZYNQ_GPIO_BANK1_PIN_MIN,
+ ZYNQ_GPIO_BANK2_PIN_MIN,
+ ZYNQ_GPIO_BANK3_PIN_MIN,
+};
+
+/**
+ * zynq_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.
+ */
+static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin)
+{
+ u32 data;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
+
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+
+ data = readl_relaxed(gpio->base_addr +
+ ZYNQ_GPIO_DATA_RO_OFFSET(bank_num));
+
+ return (data >> bank_pin_num) & 1;
+}
+
+/**
+ * zynq_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 calculates the register offset (i.e to lower 16 bits or
+ * upper 16 bits) based on the given pin number and sets the state of a
+ * gpio pin to the specified value. The state is either 0 or non-zero.
+ */
+static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
+ int state)
+{
+ unsigned int reg_offset, bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
+
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+
+ if (bank_pin_num >= ZYNQ_GPIO_MID_PIN_NUM) {
+ /* only 16 data bits in bit maskable reg */
+ bank_pin_num -= ZYNQ_GPIO_MID_PIN_NUM;
+ reg_offset = ZYNQ_GPIO_DATA_MSW_OFFSET(bank_num);
+ } else {
+ reg_offset = ZYNQ_GPIO_DATA_LSW_OFFSET(bank_num);
+ }
+
+ /*
+ * get the 32 bit value to be written to the mask/data register where
+ * the upper 16 bits is the mask and lower 16 bits is the data
+ */
+ state = !!state;
+ state = ~(1 << (bank_pin_num + ZYNQ_GPIO_MID_PIN_NUM)) &
+ ((state << bank_pin_num) | ZYNQ_GPIO_UPPER_MASK);
+
+ writel_relaxed(state, gpio->base_addr + reg_offset);
+}
+
+/**
+ * zynq_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
+ *
+ * This function uses the read-modify-write sequence to set the direction of
+ * the gpio pin as input.
+ *
+ * Return: 0 always
+ */
+static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
+{
+ u32 reg;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
+
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+
+ /* bank 0 pins 7 and 8 are special and cannot be used as inputs */
+ if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8))
+ return -EINVAL;
+
+ /* clear the bit in direction mode reg to set the pin as input */
+ 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));
+
+ return 0;
+}
+
+/**
+ * zynq_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
+ *
+ * This function sets the direction of specified GPIO pin as output, configures
+ * the Output Enable register for the pin and uses zynq_gpio_set to set
+ * the state of the pin to the value specified.
+ *
+ * Return: 0 always
+ */
+static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
+ int state)
+{
+ u32 reg;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
+
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+
+ /* set the GPIO pin as output */
+ 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));
+
+ /* configure the output enable reg for the 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));
+
+ /* set the state of the pin */
+ zynq_gpio_set_value(chip, pin, state);
+ return 0;
+}
+
+/**
+ * zynq_gpio_irq_mask - Disable the interrupts for a gpio pin
+ * @irq_data: per irq and chip data passed down to chip functions
+ *
+ * This function calculates gpio pin number from irq number and sets the
+ * bit in the Interrupt Disable register of the corresponding bank to disable
+ * interrupts for that pin.
+ */
+static void zynq_gpio_irq_mask(struct irq_data *irq_data)
+{
+ unsigned int device_pin_num, bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
+
+ device_pin_num = irq_data->hwirq;
+ zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+ writel_relaxed(BIT(bank_pin_num),
+ gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
+}
+
+/**
+ * zynq_gpio_irq_unmask - Enable the interrupts for a gpio pin
+ * @irq_data: irq data containing irq number of gpio pin for the interrupt
+ * to enable
+ *
+ * This function calculates the gpio pin number from irq number and sets the
+ * bit in the Interrupt Enable register of the corresponding bank to enable
+ * interrupts for that pin.
+ */
+static void zynq_gpio_irq_unmask(struct irq_data *irq_data)
+{
+ unsigned int device_pin_num, bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
+
+ device_pin_num = irq_data->hwirq;
+ zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+ writel_relaxed(BIT(bank_pin_num),
+ gpio->base_addr + ZYNQ_GPIO_INTEN_OFFSET(bank_num));
+}
+
+/**
+ * zynq_gpio_irq_ack - Acknowledge the interrupt of a gpio pin
+ * @irq_data: irq data containing irq number of gpio pin for the interrupt
+ * to ack
+ *
+ * This function calculates gpio pin number from irq number and sets the bit
+ * in the Interrupt Status Register of the corresponding bank, to ACK the irq.
+ */
+static void zynq_gpio_irq_ack(struct irq_data *irq_data)
+{
+ unsigned int device_pin_num, bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
+
+ device_pin_num = irq_data->hwirq;
+ zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+ writel_relaxed(BIT(bank_pin_num),
+ gpio->base_addr + ZYNQ_GPIO_INTSTS_OFFSET(bank_num));
+}
+
+/**
+ * zynq_gpio_irq_enable - Enable the interrupts for a gpio pin
+ * @irq_data: irq data containing irq number of gpio pin for the interrupt
+ * to enable
+ *
+ * Clears the INTSTS bit and unmasks the given interrrupt.
+ */
+static void zynq_gpio_irq_enable(struct irq_data *irq_data)
+{
+ /*
+ * The Zynq GPIO controller does not disable interrupt detection when
+ * the interrupt is masked and only disables the propagation of the
+ * interrupt. This means when the controller detects an interrupt
+ * condition while the interrupt is logically disabled it will propagate
+ * that interrupt event once the interrupt is enabled. This will cause
+ * the interrupt consumer to see spurious interrupts to prevent this
+ * first make sure that the interrupt is not asserted and then enable
+ * it.
+ */
+ zynq_gpio_irq_ack(irq_data);
+ zynq_gpio_irq_unmask(irq_data);
+}
+
+/**
+ * zynq_gpio_set_irq_type - Set the irq type for a gpio pin
+ * @irq_data: irq data containing irq number of gpio pin
+ * @type: interrupt type that is to be set for the gpio pin
+ *
+ * This function gets the gpio pin number and its bank from the gpio pin number
+ * and configures the INT_TYPE, INT_POLARITY and INT_ANY registers.
+ *
+ * Return: 0, negative error otherwise.
+ * TYPE-EDGE_RISING, INT_TYPE - 1, INT_POLARITY - 1, INT_ANY - 0;
+ * TYPE-EDGE_FALLING, INT_TYPE - 1, INT_POLARITY - 0, INT_ANY - 0;
+ * TYPE-EDGE_BOTH, INT_TYPE - 1, INT_POLARITY - NA, INT_ANY - 1;
+ * TYPE-LEVEL_HIGH, INT_TYPE - 0, INT_POLARITY - 1, INT_ANY - NA;
+ * TYPE-LEVEL_LOW, INT_TYPE - 0, INT_POLARITY - 0, INT_ANY - NA
+ */
+static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
+{
+ u32 int_type, int_pol, int_any;
+ unsigned int device_pin_num, bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
+
+ device_pin_num = irq_data->hwirq;
+ zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+
+ int_type = readl_relaxed(gpio->base_addr +
+ ZYNQ_GPIO_INTTYPE_OFFSET(bank_num));
+ int_pol = readl_relaxed(gpio->base_addr +
+ ZYNQ_GPIO_INTPOL_OFFSET(bank_num));
+ int_any = readl_relaxed(gpio->base_addr +
+ ZYNQ_GPIO_INTANY_OFFSET(bank_num));
+
+ /*
+ * based on the type requested, configure the INT_TYPE, INT_POLARITY
+ * and INT_ANY registers
+ */
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ int_type |= BIT(bank_pin_num);
+ int_pol |= BIT(bank_pin_num);
+ int_any &= ~BIT(bank_pin_num);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ int_type |= BIT(bank_pin_num);
+ int_pol &= ~BIT(bank_pin_num);
+ int_any &= ~BIT(bank_pin_num);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ int_type |= BIT(bank_pin_num);
+ int_any |= BIT(bank_pin_num);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ int_type &= ~BIT(bank_pin_num);
+ int_pol |= BIT(bank_pin_num);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ int_type &= ~BIT(bank_pin_num);
+ int_pol &= ~BIT(bank_pin_num);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ writel_relaxed(int_type,
+ gpio->base_addr + ZYNQ_GPIO_INTTYPE_OFFSET(bank_num));
+ writel_relaxed(int_pol,
+ gpio->base_addr + ZYNQ_GPIO_INTPOL_OFFSET(bank_num));
+ writel_relaxed(int_any,
+ gpio->base_addr + ZYNQ_GPIO_INTANY_OFFSET(bank_num));
+
+ if (type & IRQ_TYPE_LEVEL_MASK) {
+ __irq_set_chip_handler_name_locked(irq_data->irq,
+ &zynq_gpio_level_irqchip, handle_fasteoi_irq, NULL);
+ } else {
+ __irq_set_chip_handler_name_locked(irq_data->irq,
+ &zynq_gpio_edge_irqchip, handle_level_irq, NULL);
+ }
+
+ return 0;
+}
+
+static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct zynq_gpio *gpio = irq_data_get_irq_chip_data(data);
+
+ irq_set_irq_wake(gpio->irq, on);
+
+ return 0;
+}
+
+/* irq chip descriptor */
+static struct irq_chip zynq_gpio_level_irqchip = {
+ .name = DRIVER_NAME,
+ .irq_enable = zynq_gpio_irq_enable,
+ .irq_eoi = zynq_gpio_irq_ack,
+ .irq_mask = zynq_gpio_irq_mask,
+ .irq_unmask = zynq_gpio_irq_unmask,
+ .irq_set_type = zynq_gpio_set_irq_type,
+ .irq_set_wake = zynq_gpio_set_wake,
+ .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED |
+ IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static struct irq_chip zynq_gpio_edge_irqchip = {
+ .name = DRIVER_NAME,
+ .irq_enable = zynq_gpio_irq_enable,
+ .irq_ack = zynq_gpio_irq_ack,
+ .irq_mask = zynq_gpio_irq_mask,
+ .irq_unmask = zynq_gpio_irq_unmask,
+ .irq_set_type = zynq_gpio_set_irq_type,
+ .irq_set_wake = zynq_gpio_set_wake,
+ .flags = IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio,
+ unsigned int bank_num,
+ unsigned long pending)
+{
+ unsigned int bank_offset = zynq_gpio_bank_offset[bank_num];
+ struct irq_domain *irqdomain = gpio->chip.irqdomain;
+ int offset;
+
+ 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);
+ }
+}
+
+/**
+ * zynq_gpio_irqhandler - IRQ handler for the gpio banks of a gpio device
+ * @irq: irq number of the gpio bank where interrupt has occurred
+ * @desc: irq descriptor instance of the 'irq'
+ *
+ * This function reads the Interrupt Status Register of each bank to get the
+ * gpio pin number which has triggered an interrupt. It then acks the triggered
+ * interrupt and calls the pin specific handler set by the higher layer
+ * application for that pin.
+ * Note: A bug is reported if no handler is set for the gpio pin.
+ */
+static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc)
+{
+ u32 int_sts, int_enb;
+ unsigned int bank_num;
+ struct zynq_gpio *gpio = irq_get_handler_data(irq);
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(irqchip, desc);
+
+ for (bank_num = 0; bank_num < ZYNQ_GPIO_MAX_BANK; bank_num++) {
+ int_sts = readl_relaxed(gpio->base_addr +
+ ZYNQ_GPIO_INTSTS_OFFSET(bank_num));
+ 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);
+ }
+
+ chained_irq_exit(irqchip, desc);
+}
+
+static int __maybe_unused zynq_gpio_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int irq = platform_get_irq(pdev, 0);
+ struct irq_data *data = irq_get_irq_data(irq);
+
+ if (!irqd_is_wakeup_set(data))
+ return pm_runtime_force_suspend(dev);
+
+ return 0;
+}
+
+static int __maybe_unused zynq_gpio_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int irq = platform_get_irq(pdev, 0);
+ struct irq_data *data = irq_get_irq_data(irq);
+
+ if (!irqd_is_wakeup_set(data))
+ return pm_runtime_force_resume(dev);
+
+ return 0;
+}
+
+static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct zynq_gpio *gpio = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(gpio->clk);
+
+ return 0;
+}
+
+static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct zynq_gpio *gpio = platform_get_drvdata(pdev);
+
+ return clk_prepare_enable(gpio->clk);
+}
+
+static int zynq_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ int ret;
+
+ ret = pm_runtime_get_sync(chip->dev);
+
+ /*
+ * 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 zynq_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pm_runtime_put(chip->dev);
+}
+
+static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume)
+ SET_RUNTIME_PM_OPS(zynq_gpio_runtime_suspend,
+ zynq_gpio_runtime_resume, NULL)
+};
+
+/**
+ * zynq_gpio_probe - Initialization method for a zynq_gpio device
+ * @pdev: platform device instance
+ *
+ * This function allocates memory resources for the gpio device and registers
+ * all the banks of the device. It will also set up interrupts for the gpio
+ * pins.
+ * Note: Interrupts are disabled for all the banks during initialization.
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+static int zynq_gpio_probe(struct platform_device *pdev)
+{
+ int ret, bank_num;
+ struct zynq_gpio *gpio;
+ struct gpio_chip *chip;
+ struct resource *res;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, gpio);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gpio->base_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpio->base_addr))
+ return PTR_ERR(gpio->base_addr);
+
+ gpio->irq = platform_get_irq(pdev, 0);
+ if (gpio->irq < 0) {
+ dev_err(&pdev->dev, "invalid IRQ\n");
+ return gpio->irq;
+ }
+
+ /* configure the gpio chip */
+ chip = &gpio->chip;
+ chip->label = "zynq_gpio";
+ chip->owner = THIS_MODULE;
+ chip->dev = &pdev->dev;
+ chip->get = zynq_gpio_get_value;
+ chip->set = zynq_gpio_set_value;
+ chip->request = zynq_gpio_request;
+ chip->free = zynq_gpio_free;
+ chip->direction_input = zynq_gpio_dir_in;
+ chip->direction_output = zynq_gpio_dir_out;
+ chip->base = -1;
+ chip->ngpio = ZYNQ_GPIO_NR_GPIOS;
+
+ /* Enable 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);
+ }
+ ret = clk_prepare_enable(gpio->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to enable clock.\n");
+ return ret;
+ }
+
+ /* report a bug if gpio chip registration fails */
+ ret = gpiochip_add(chip);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add gpio chip\n");
+ goto err_disable_clk;
+ }
+
+ /* disable interrupts for all banks */
+ for (bank_num = 0; bank_num < ZYNQ_GPIO_MAX_BANK; bank_num++)
+ writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr +
+ ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
+
+ 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;
+ }
+
+ gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, gpio->irq,
+ zynq_gpio_irqhandler);
+
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+err_rm_gpiochip:
+ gpiochip_remove(chip);
+err_disable_clk:
+ clk_disable_unprepare(gpio->clk);
+
+ return ret;
+}
+
+/**
+ * zynq_gpio_remove - Driver removal function
+ * @pdev: platform device instance
+ *
+ * Return: 0 always
+ */
+static int zynq_gpio_remove(struct platform_device *pdev)
+{
+ struct zynq_gpio *gpio = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ gpiochip_remove(&gpio->chip);
+ clk_disable_unprepare(gpio->clk);
+ device_set_wakeup_capable(&pdev->dev, 0);
+ return 0;
+}
+
+static struct of_device_id zynq_gpio_of_match[] = {
+ { .compatible = "xlnx,zynq-gpio-1.0", },
+ { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
+
+static struct platform_driver zynq_gpio_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = &zynq_gpio_dev_pm_ops,
+ .of_match_table = zynq_gpio_of_match,
+ },
+ .probe = zynq_gpio_probe,
+ .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);
+
+MODULE_AUTHOR("Xilinx Inc.");
+MODULE_DESCRIPTION("Zynq GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
new file mode 100644
index 000000000..725d16138
--- /dev/null
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -0,0 +1,808 @@
+/*
+ * ACPI helpers for GPIO API
+ *
+ * Copyright (C) 2012, Intel Corporation
+ * Authors: Mathias Nyman <mathias.nyman@linux.intel.com>
+ * Mika Westerberg <mika.westerberg@linux.intel.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.
+ */
+
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/export.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "gpiolib.h"
+
+struct acpi_gpio_event {
+ struct list_head node;
+ acpi_handle handle;
+ unsigned int pin;
+ unsigned int irq;
+ struct gpio_desc *desc;
+};
+
+struct acpi_gpio_connection {
+ struct list_head node;
+ unsigned int pin;
+ struct gpio_desc *desc;
+};
+
+struct acpi_gpio_chip {
+ /*
+ * ACPICA requires that the first field of the context parameter
+ * passed to acpi_install_address_space_handler() is large enough
+ * to hold struct acpi_connection_info.
+ */
+ struct acpi_connection_info conn_info;
+ struct list_head conns;
+ struct mutex conn_lock;
+ struct gpio_chip *chip;
+ struct list_head events;
+};
+
+static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
+{
+ if (!gc->dev)
+ return false;
+
+ return ACPI_HANDLE(gc->dev) == data;
+}
+
+#ifdef CONFIG_PINCTRL
+/**
+ * acpi_gpiochip_pin_to_gpio_offset() - translates ACPI GPIO to Linux GPIO
+ * @chip: GPIO chip
+ * @pin: ACPI GPIO pin number from GpioIo/GpioInt resource
+ *
+ * Function takes ACPI GpioIo/GpioInt pin number as a parameter and
+ * translates it to a corresponding offset suitable to be passed to a
+ * GPIO controller driver.
+ *
+ * Typically the returned offset is same as @pin, but if the GPIO
+ * controller uses pin controller and the mapping is not contigous the
+ * offset might be different.
+ */
+static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, int pin)
+{
+ struct gpio_pin_range *pin_range;
+
+ /* If there are no ranges in this chip, use 1:1 mapping */
+ if (list_empty(&chip->pin_ranges))
+ return pin;
+
+ list_for_each_entry(pin_range, &chip->pin_ranges, node) {
+ const struct pinctrl_gpio_range *range = &pin_range->range;
+ int i;
+
+ if (range->pins) {
+ for (i = 0; i < range->npins; i++) {
+ if (range->pins[i] == pin)
+ return range->base + i - chip->base;
+ }
+ } else {
+ if (pin >= range->pin_base &&
+ pin < range->pin_base + range->npins) {
+ unsigned gpio_base;
+
+ gpio_base = range->base - chip->base;
+ return gpio_base + pin - range->pin_base;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+#else
+static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip,
+ int pin)
+{
+ return pin;
+}
+#endif
+
+/**
+ * acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API
+ * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
+ * @pin: ACPI GPIO pin number (0-based, controller-relative)
+ *
+ * Returns GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR
+ * error value
+ */
+
+static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
+{
+ struct gpio_chip *chip;
+ acpi_handle handle;
+ acpi_status status;
+ int offset;
+
+ status = acpi_get_handle(NULL, path, &handle);
+ if (ACPI_FAILURE(status))
+ return ERR_PTR(-ENODEV);
+
+ chip = gpiochip_find(handle, acpi_gpiochip_find);
+ if (!chip)
+ return ERR_PTR(-ENODEV);
+
+ offset = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ if (offset < 0)
+ return ERR_PTR(offset);
+
+ return gpiochip_get_desc(chip, offset);
+}
+
+static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
+{
+ struct acpi_gpio_event *event = data;
+
+ acpi_evaluate_object(event->handle, NULL, NULL, NULL);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data)
+{
+ struct acpi_gpio_event *event = data;
+
+ acpi_execute_simple_method(event->handle, NULL, event->pin);
+
+ return IRQ_HANDLED;
+}
+
+static void acpi_gpio_chip_dh(acpi_handle handle, void *data)
+{
+ /* The address of this function is used as a key. */
+}
+
+static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
+ void *context)
+{
+ struct acpi_gpio_chip *acpi_gpio = context;
+ struct gpio_chip *chip = acpi_gpio->chip;
+ struct acpi_resource_gpio *agpio;
+ acpi_handle handle, evt_handle;
+ struct acpi_gpio_event *event;
+ irq_handler_t handler = NULL;
+ struct gpio_desc *desc;
+ unsigned long irqflags;
+ int ret, pin, irq;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
+ return AE_OK;
+
+ agpio = &ares->data.gpio;
+ if (agpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT)
+ return AE_OK;
+
+ handle = ACPI_HANDLE(chip->dev);
+ pin = agpio->pin_table[0];
+
+ if (pin <= 255) {
+ char ev_name[5];
+ sprintf(ev_name, "_%c%02X",
+ agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L',
+ pin);
+ if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle)))
+ handler = acpi_gpio_irq_handler;
+ }
+ if (!handler) {
+ if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle)))
+ handler = acpi_gpio_irq_handler_evt;
+ }
+ if (!handler)
+ return AE_BAD_PARAMETER;
+
+ pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ if (pin < 0)
+ return AE_BAD_PARAMETER;
+
+ desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event");
+ if (IS_ERR(desc)) {
+ dev_err(chip->dev, "Failed to request GPIO\n");
+ return AE_ERROR;
+ }
+
+ gpiod_direction_input(desc);
+
+ ret = gpiochip_lock_as_irq(chip, pin);
+ if (ret) {
+ dev_err(chip->dev, "Failed to lock GPIO as interrupt\n");
+ goto fail_free_desc;
+ }
+
+ irq = gpiod_to_irq(desc);
+ if (irq < 0) {
+ dev_err(chip->dev, "Failed to translate GPIO to IRQ\n");
+ goto fail_unlock_irq;
+ }
+
+ irqflags = IRQF_ONESHOT;
+ if (agpio->triggering == ACPI_LEVEL_SENSITIVE) {
+ if (agpio->polarity == ACPI_ACTIVE_HIGH)
+ irqflags |= IRQF_TRIGGER_HIGH;
+ else
+ irqflags |= IRQF_TRIGGER_LOW;
+ } else {
+ switch (agpio->polarity) {
+ case ACPI_ACTIVE_HIGH:
+ irqflags |= IRQF_TRIGGER_RISING;
+ break;
+ case ACPI_ACTIVE_LOW:
+ irqflags |= IRQF_TRIGGER_FALLING;
+ break;
+ default:
+ irqflags |= IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING;
+ break;
+ }
+ }
+
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ goto fail_unlock_irq;
+
+ event->handle = evt_handle;
+ event->irq = irq;
+ event->pin = pin;
+ event->desc = desc;
+
+ ret = request_threaded_irq(event->irq, NULL, handler, irqflags,
+ "ACPI:Event", event);
+ if (ret) {
+ dev_err(chip->dev, "Failed to setup interrupt handler for %d\n",
+ event->irq);
+ goto fail_free_event;
+ }
+
+ list_add_tail(&event->node, &acpi_gpio->events);
+ return AE_OK;
+
+fail_free_event:
+ kfree(event);
+fail_unlock_irq:
+ gpiochip_unlock_as_irq(chip, pin);
+fail_free_desc:
+ gpiochip_free_own_desc(desc);
+
+ return AE_ERROR;
+}
+
+/**
+ * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events
+ * @chip: GPIO chip
+ *
+ * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are
+ * handled by ACPI event methods which need to be called from the GPIO
+ * chip's interrupt handler. acpi_gpiochip_request_interrupts finds out which
+ * gpio pins have acpi event methods and assigns interrupt handlers that calls
+ * the acpi event methods for those pins.
+ */
+void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
+{
+ struct acpi_gpio_chip *acpi_gpio;
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!chip->dev || !chip->to_irq)
+ return;
+
+ handle = ACPI_HANDLE(chip->dev);
+ if (!handle)
+ return;
+
+ status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio);
+ if (ACPI_FAILURE(status))
+ return;
+
+ INIT_LIST_HEAD(&acpi_gpio->events);
+ acpi_walk_resources(handle, "_AEI",
+ acpi_gpiochip_request_interrupt, acpi_gpio);
+}
+
+/**
+ * acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts.
+ * @chip: GPIO chip
+ *
+ * Free interrupts associated with GPIO ACPI event method for the given
+ * GPIO chip.
+ */
+void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
+{
+ struct acpi_gpio_chip *acpi_gpio;
+ struct acpi_gpio_event *event, *ep;
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!chip->dev || !chip->to_irq)
+ return;
+
+ handle = ACPI_HANDLE(chip->dev);
+ if (!handle)
+ return;
+
+ status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio);
+ if (ACPI_FAILURE(status))
+ return;
+
+ list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) {
+ struct gpio_desc *desc;
+
+ free_irq(event->irq, event);
+ desc = event->desc;
+ if (WARN_ON(IS_ERR(desc)))
+ continue;
+ gpiochip_unlock_as_irq(chip, event->pin);
+ gpiochip_free_own_desc(desc);
+ list_del(&event->node);
+ kfree(event);
+ }
+}
+
+int acpi_dev_add_driver_gpios(struct acpi_device *adev,
+ const struct acpi_gpio_mapping *gpios)
+{
+ if (adev && gpios) {
+ adev->driver_gpios = gpios;
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios);
+
+static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
+ const char *name, int index,
+ struct acpi_reference_args *args)
+{
+ const struct acpi_gpio_mapping *gm;
+
+ if (!adev->driver_gpios)
+ return false;
+
+ for (gm = adev->driver_gpios; gm->name; gm++)
+ if (!strcmp(name, gm->name) && gm->data && index < gm->size) {
+ const struct acpi_gpio_params *par = gm->data + index;
+
+ args->adev = adev;
+ args->args[0] = par->crs_entry_index;
+ args->args[1] = par->line_index;
+ args->args[2] = par->active_low;
+ args->nargs = 3;
+ return true;
+ }
+
+ return false;
+}
+
+struct acpi_gpio_lookup {
+ struct acpi_gpio_info info;
+ int index;
+ int pin_index;
+ struct gpio_desc *desc;
+ int n;
+};
+
+static int acpi_find_gpio(struct acpi_resource *ares, void *data)
+{
+ struct acpi_gpio_lookup *lookup = data;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
+ return 1;
+
+ if (lookup->n++ == lookup->index && !lookup->desc) {
+ const struct acpi_resource_gpio *agpio = &ares->data.gpio;
+ int pin_index = lookup->pin_index;
+
+ if (pin_index >= agpio->pin_table_length)
+ return 1;
+
+ lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
+ agpio->pin_table[pin_index]);
+ lookup->info.gpioint =
+ agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
+
+ /*
+ * ActiveLow is only specified for GpioInt resource. If
+ * GpioIo is used then the only way to set the flag is
+ * to use _DSD "gpios" property.
+ */
+ if (lookup->info.gpioint)
+ lookup->info.active_low =
+ agpio->polarity == ACPI_ACTIVE_LOW;
+ }
+
+ return 1;
+}
+
+/**
+ * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources
+ * @adev: pointer to a ACPI device to get GPIO from
+ * @propname: Property name of the GPIO (optional)
+ * @index: index of GpioIo/GpioInt resource (starting from %0)
+ * @info: info pointer to fill in (optional)
+ *
+ * Function goes through ACPI resources for @adev and based on @index looks
+ * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor,
+ * and returns it. @index matches GpioIo/GpioInt resources only so if there
+ * are total %3 GPIO resources, the index goes from %0 to %2.
+ *
+ * If @propname is specified the GPIO is looked using device property. In
+ * that case @index is used to select the GPIO entry in the property value
+ * (in case of multiple).
+ *
+ * If the GPIO cannot be translated or there is an error an ERR_PTR is
+ * returned.
+ *
+ * Note: if the GPIO resource has multiple entries in the pin list, this
+ * function only returns the first.
+ */
+struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
+ const char *propname, int index,
+ struct acpi_gpio_info *info)
+{
+ struct acpi_gpio_lookup lookup;
+ struct list_head resource_list;
+ bool active_low = false;
+ int ret;
+
+ if (!adev)
+ return ERR_PTR(-ENODEV);
+
+ memset(&lookup, 0, sizeof(lookup));
+ lookup.index = index;
+
+ if (propname) {
+ struct acpi_reference_args args;
+
+ dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname);
+
+ memset(&args, 0, sizeof(args));
+ ret = acpi_dev_get_property_reference(adev, propname,
+ index, &args);
+ if (ret) {
+ bool found = acpi_get_driver_gpio_data(adev, propname,
+ index, &args);
+ if (!found)
+ return ERR_PTR(ret);
+ }
+
+ /*
+ * The property was found and resolved so need to
+ * lookup the GPIO based on returned args instead.
+ */
+ adev = args.adev;
+ if (args.nargs >= 2) {
+ lookup.index = args.args[0];
+ lookup.pin_index = args.args[1];
+ /*
+ * 3rd argument, if present is used to
+ * specify active_low.
+ */
+ if (args.nargs >= 3)
+ active_low = !!args.args[2];
+ }
+
+ dev_dbg(&adev->dev, "GPIO: _DSD returned %s %zd %llu %llu %llu\n",
+ dev_name(&adev->dev), args.nargs,
+ args.args[0], args.args[1], args.args[2]);
+ } else {
+ dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index);
+ }
+
+ INIT_LIST_HEAD(&resource_list);
+ ret = acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio,
+ &lookup);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ if (lookup.desc && info) {
+ *info = lookup.info;
+ if (active_low)
+ info->active_low = active_low;
+ }
+
+ return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT);
+}
+
+static acpi_status
+acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
+ u32 bits, u64 *value, void *handler_context,
+ void *region_context)
+{
+ struct acpi_gpio_chip *achip = region_context;
+ struct gpio_chip *chip = achip->chip;
+ struct acpi_resource_gpio *agpio;
+ struct acpi_resource *ares;
+ int pin_index = (int)address;
+ acpi_status status;
+ bool pull_up;
+ int length;
+ int i;
+
+ status = acpi_buffer_to_resource(achip->conn_info.connection,
+ achip->conn_info.length, &ares);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) {
+ ACPI_FREE(ares);
+ return AE_BAD_PARAMETER;
+ }
+
+ agpio = &ares->data.gpio;
+ pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP;
+
+ if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
+ function == ACPI_WRITE)) {
+ ACPI_FREE(ares);
+ return AE_BAD_PARAMETER;
+ }
+
+ length = min(agpio->pin_table_length, (u16)(pin_index + bits));
+ for (i = pin_index; i < length; ++i) {
+ int pin = agpio->pin_table[i];
+ struct acpi_gpio_connection *conn;
+ struct gpio_desc *desc;
+ bool found;
+
+ pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ if (pin < 0) {
+ status = AE_BAD_PARAMETER;
+ goto out;
+ }
+
+ mutex_lock(&achip->conn_lock);
+
+ found = false;
+ list_for_each_entry(conn, &achip->conns, node) {
+ if (conn->pin == pin) {
+ found = true;
+ desc = conn->desc;
+ break;
+ }
+ }
+ if (!found) {
+ desc = gpiochip_request_own_desc(chip, pin,
+ "ACPI:OpRegion");
+ if (IS_ERR(desc)) {
+ status = AE_ERROR;
+ mutex_unlock(&achip->conn_lock);
+ goto out;
+ }
+
+ switch (agpio->io_restriction) {
+ case ACPI_IO_RESTRICT_INPUT:
+ gpiod_direction_input(desc);
+ break;
+ 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, otherwise
+ * low.
+ */
+ gpiod_direction_output(desc, pull_up);
+ break;
+ default:
+ /*
+ * Assume that the BIOS has configured the
+ * direction and pull accordingly.
+ */
+ break;
+ }
+
+ conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+ if (!conn) {
+ status = AE_NO_MEMORY;
+ gpiochip_free_own_desc(desc);
+ mutex_unlock(&achip->conn_lock);
+ goto out;
+ }
+
+ conn->pin = pin;
+ conn->desc = desc;
+ list_add_tail(&conn->node, &achip->conns);
+ }
+
+ mutex_unlock(&achip->conn_lock);
+
+ if (function == ACPI_WRITE)
+ gpiod_set_raw_value_cansleep(desc,
+ !!((1 << i) & *value));
+ else
+ *value |= (u64)gpiod_get_raw_value_cansleep(desc) << i;
+ }
+
+out:
+ ACPI_FREE(ares);
+ return status;
+}
+
+static void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip)
+{
+ struct gpio_chip *chip = achip->chip;
+ acpi_handle handle = ACPI_HANDLE(chip->dev);
+ acpi_status status;
+
+ INIT_LIST_HEAD(&achip->conns);
+ mutex_init(&achip->conn_lock);
+ status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
+ acpi_gpio_adr_space_handler,
+ NULL, achip);
+ if (ACPI_FAILURE(status))
+ dev_err(chip->dev, "Failed to install GPIO OpRegion handler\n");
+}
+
+static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
+{
+ struct gpio_chip *chip = achip->chip;
+ acpi_handle handle = ACPI_HANDLE(chip->dev);
+ struct acpi_gpio_connection *conn, *tmp;
+ acpi_status status;
+
+ status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
+ acpi_gpio_adr_space_handler);
+ if (ACPI_FAILURE(status)) {
+ dev_err(chip->dev, "Failed to remove GPIO OpRegion handler\n");
+ return;
+ }
+
+ list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) {
+ gpiochip_free_own_desc(conn->desc);
+ list_del(&conn->node);
+ kfree(conn);
+ }
+}
+
+void acpi_gpiochip_add(struct gpio_chip *chip)
+{
+ struct acpi_gpio_chip *acpi_gpio;
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!chip || !chip->dev)
+ return;
+
+ handle = ACPI_HANDLE(chip->dev);
+ if (!handle)
+ return;
+
+ acpi_gpio = kzalloc(sizeof(*acpi_gpio), GFP_KERNEL);
+ if (!acpi_gpio) {
+ dev_err(chip->dev,
+ "Failed to allocate memory for ACPI GPIO chip\n");
+ return;
+ }
+
+ acpi_gpio->chip = chip;
+
+ status = acpi_attach_data(handle, acpi_gpio_chip_dh, acpi_gpio);
+ if (ACPI_FAILURE(status)) {
+ dev_err(chip->dev, "Failed to attach ACPI GPIO chip\n");
+ kfree(acpi_gpio);
+ return;
+ }
+
+ acpi_gpiochip_request_regions(acpi_gpio);
+}
+
+void acpi_gpiochip_remove(struct gpio_chip *chip)
+{
+ struct acpi_gpio_chip *acpi_gpio;
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!chip || !chip->dev)
+ return;
+
+ handle = ACPI_HANDLE(chip->dev);
+ if (!handle)
+ return;
+
+ status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(chip->dev, "Failed to retrieve ACPI GPIO chip\n");
+ return;
+ }
+
+ acpi_gpiochip_free_regions(acpi_gpio);
+
+ acpi_detach_data(handle, acpi_gpio_chip_dh);
+ kfree(acpi_gpio);
+}
+
+static unsigned int acpi_gpio_package_count(const union acpi_object *obj)
+{
+ const union acpi_object *element = obj->package.elements;
+ const union acpi_object *end = element + obj->package.count;
+ unsigned int count = 0;
+
+ while (element < end) {
+ if (element->type == ACPI_TYPE_LOCAL_REFERENCE)
+ count++;
+
+ element++;
+ }
+ return count;
+}
+
+static int acpi_find_gpio_count(struct acpi_resource *ares, void *data)
+{
+ unsigned int *count = data;
+
+ if (ares->type == ACPI_RESOURCE_TYPE_GPIO)
+ *count += ares->data.gpio.pin_table_length;
+
+ return 1;
+}
+
+/**
+ * acpi_gpio_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
+ * @con_id: function within the GPIO consumer
+ */
+int acpi_gpio_count(struct device *dev, const char *con_id)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ const union acpi_object *obj;
+ const struct acpi_gpio_mapping *gm;
+ int count = -ENOENT;
+ int ret;
+ char propname[32];
+ unsigned int i;
+
+ /* Try first from _DSD */
+ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
+ if (con_id && strcmp(con_id, "gpios"))
+ snprintf(propname, sizeof(propname), "%s-%s",
+ con_id, gpio_suffixes[i]);
+ else
+ snprintf(propname, sizeof(propname), "%s",
+ gpio_suffixes[i]);
+
+ ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY,
+ &obj);
+ if (ret == 0) {
+ if (obj->type == ACPI_TYPE_LOCAL_REFERENCE)
+ count = 1;
+ else if (obj->type == ACPI_TYPE_PACKAGE)
+ count = acpi_gpio_package_count(obj);
+ } else if (adev->driver_gpios) {
+ for (gm = adev->driver_gpios; gm->name; gm++)
+ if (strcmp(propname, gm->name) == 0) {
+ count = gm->size;
+ break;
+ }
+ }
+ if (count >= 0)
+ break;
+ }
+
+ /* Then from plain _CRS GPIOs */
+ if (count < 0) {
+ struct list_head resource_list;
+ unsigned int crs_count = 0;
+
+ INIT_LIST_HEAD(&resource_list);
+ acpi_dev_get_resources(adev, &resource_list,
+ acpi_find_gpio_count, &crs_count);
+ acpi_dev_free_resource_list(&resource_list);
+ if (crs_count > 0)
+ count = crs_count;
+ }
+ return count;
+}
diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c
new file mode 100644
index 000000000..8b830996f
--- /dev/null
+++ b/drivers/gpio/gpiolib-legacy.c
@@ -0,0 +1,112 @@
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+
+#include <linux/gpio.h>
+
+#include "gpiolib.h"
+
+void gpio_free(unsigned gpio)
+{
+ gpiod_free(gpio_to_desc(gpio));
+}
+EXPORT_SYMBOL_GPL(gpio_free);
+
+/**
+ * gpio_request_one - request a single GPIO with initial configuration
+ * @gpio: the GPIO number
+ * @flags: GPIO configuration as specified by GPIOF_*
+ * @label: a literal description string of this GPIO
+ */
+int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
+{
+ struct gpio_desc *desc;
+ int err;
+
+ desc = gpio_to_desc(gpio);
+
+ /* Compatibility: assume unavailable "valid" GPIOs will appear later */
+ if (!desc && gpio_is_valid(gpio))
+ return -EPROBE_DEFER;
+
+ err = gpiod_request(desc, label);
+ if (err)
+ return err;
+
+ if (flags & GPIOF_OPEN_DRAIN)
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+
+ if (flags & GPIOF_OPEN_SOURCE)
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+
+ if (flags & GPIOF_ACTIVE_LOW)
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+
+ if (flags & GPIOF_DIR_IN)
+ err = gpiod_direction_input(desc);
+ else
+ err = gpiod_direction_output_raw(desc,
+ (flags & GPIOF_INIT_HIGH) ? 1 : 0);
+
+ if (err)
+ goto free_gpio;
+
+ if (flags & GPIOF_EXPORT) {
+ err = gpiod_export(desc, flags & GPIOF_EXPORT_CHANGEABLE);
+ if (err)
+ goto free_gpio;
+ }
+
+ return 0;
+
+ free_gpio:
+ gpiod_free(desc);
+ return err;
+}
+EXPORT_SYMBOL_GPL(gpio_request_one);
+
+int gpio_request(unsigned gpio, const char *label)
+{
+ struct gpio_desc *desc = gpio_to_desc(gpio);
+
+ /* Compatibility: assume unavailable "valid" GPIOs will appear later */
+ if (!desc && gpio_is_valid(gpio))
+ return -EPROBE_DEFER;
+
+ return gpiod_request(desc, label);
+}
+EXPORT_SYMBOL_GPL(gpio_request);
+
+/**
+ * gpio_request_array - request multiple GPIOs in a single call
+ * @array: array of the 'struct gpio'
+ * @num: how many GPIOs in the array
+ */
+int gpio_request_array(const struct gpio *array, size_t num)
+{
+ int i, err;
+
+ for (i = 0; i < num; i++, array++) {
+ err = gpio_request_one(array->gpio, array->flags, array->label);
+ if (err)
+ goto err_free;
+ }
+ return 0;
+
+err_free:
+ while (i--)
+ gpio_free((--array)->gpio);
+ return err;
+}
+EXPORT_SYMBOL_GPL(gpio_request_array);
+
+/**
+ * gpio_free_array - release multiple GPIOs in a single call
+ * @array: array of the 'struct gpio'
+ * @num: how many GPIOs in the array
+ */
+void gpio_free_array(const struct gpio *array, size_t num)
+{
+ while (num--)
+ gpio_free((array++)->gpio);
+}
+EXPORT_SYMBOL_GPL(gpio_free_array);
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
new file mode 100644
index 000000000..a6c67c6b4
--- /dev/null
+++ b/drivers/gpio/gpiolib-of.c
@@ -0,0 +1,446 @@
+/*
+ * OF helpers for the GPIO API
+ *
+ * Copyright (c) 2007-2008 MontaVista Software, Inc.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/slab.h>
+#include <linux/gpio/machine.h>
+
+#include "gpiolib.h"
+
+/* Private data structure for of_gpiochip_find_and_xlate */
+struct gg_data {
+ enum of_gpio_flags *flags;
+ struct of_phandle_args gpiospec;
+
+ struct gpio_desc *out_gpio;
+};
+
+/* Private function for resolving node pointer to gpio_chip */
+static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
+{
+ struct gg_data *gg_data = data;
+ int ret;
+
+ if ((gc->of_node != gg_data->gpiospec.np) ||
+ (gc->of_gpio_n_cells != gg_data->gpiospec.args_count) ||
+ (!gc->of_xlate))
+ return false;
+
+ ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags);
+ if (ret < 0) {
+ /* We've found a gpio chip, but the translation failed.
+ * Store translation error in out_gpio.
+ * Return false to keep looking, as more than one gpio chip
+ * could be registered per of-node.
+ */
+ gg_data->out_gpio = ERR_PTR(ret);
+ return false;
+ }
+
+ gg_data->out_gpio = gpiochip_get_desc(gc, ret);
+ return true;
+}
+
+/**
+ * of_get_named_gpiod_flags() - Get a GPIO descriptor and flags for GPIO API
+ * @np: device node to get GPIO from
+ * @propname: property name containing gpio specifier(s)
+ * @index: index of the GPIO
+ * @flags: a flags pointer to fill in
+ *
+ * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno
+ * 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,
+ const char *propname, int index, enum of_gpio_flags *flags)
+{
+ /* Return -EPROBE_DEFER to support probe() functions to be called
+ * later when the GPIO actually becomes available
+ */
+ struct gg_data gg_data = {
+ .flags = flags,
+ .out_gpio = ERR_PTR(-EPROBE_DEFER)
+ };
+ int ret;
+
+ /* .of_xlate might decide to not fill in the flags, so clear it. */
+ if (flags)
+ *flags = 0;
+
+ ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
+ &gg_data.gpiospec);
+ if (ret) {
+ pr_debug("%s: can't parse '%s' property of node '%s[%d]'\n",
+ __func__, propname, np->full_name, index);
+ return ERR_PTR(ret);
+ }
+
+ gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
+
+ of_node_put(gg_data.gpiospec.np);
+ pr_debug("%s: parsed '%s' property of node '%s[%d]' - status (%d)\n",
+ __func__, propname, np->full_name, index,
+ PTR_ERR_OR_ZERO(gg_data.out_gpio));
+ return gg_data.out_gpio;
+}
+
+int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
+ int index, enum of_gpio_flags *flags)
+{
+ struct gpio_desc *desc;
+
+ desc = of_get_named_gpiod_flags(np, list_name, index, flags);
+
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+ else
+ return desc_to_gpio(desc);
+}
+EXPORT_SYMBOL(of_get_named_gpio_flags);
+
+/**
+ * of_get_gpio_hog() - Get a GPIO hog descriptor, names and flags for GPIO API
+ * @np: device node to get GPIO from
+ * @name: GPIO line name
+ * @lflags: gpio_lookup_flags - returned from of_find_gpio() or
+ * of_get_gpio_hog()
+ * @dflags: gpiod_flags - optional GPIO initialization flags
+ *
+ * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno
+ * value on the error condition.
+ */
+static struct gpio_desc *of_get_gpio_hog(struct device_node *np,
+ const char **name,
+ enum gpio_lookup_flags *lflags,
+ enum gpiod_flags *dflags)
+{
+ struct device_node *chip_np;
+ enum of_gpio_flags xlate_flags;
+ struct gpio_desc *desc;
+ struct gg_data gg_data = {
+ .flags = &xlate_flags,
+ };
+ u32 tmp;
+ int i, ret;
+
+ chip_np = np->parent;
+ if (!chip_np)
+ return ERR_PTR(-EINVAL);
+
+ xlate_flags = 0;
+ *lflags = 0;
+ *dflags = 0;
+
+ ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (tmp > MAX_PHANDLE_ARGS)
+ return ERR_PTR(-EINVAL);
+
+ gg_data.gpiospec.args_count = tmp;
+ gg_data.gpiospec.np = chip_np;
+ for (i = 0; i < tmp; i++) {
+ ret = of_property_read_u32_index(np, "gpios", i,
+ &gg_data.gpiospec.args[i]);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
+ if (!gg_data.out_gpio) {
+ if (np->parent == np)
+ return ERR_PTR(-ENXIO);
+ else
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (xlate_flags & OF_GPIO_ACTIVE_LOW)
+ *lflags |= GPIO_ACTIVE_LOW;
+
+ if (of_property_read_bool(np, "input"))
+ *dflags |= GPIOD_IN;
+ else if (of_property_read_bool(np, "output-low"))
+ *dflags |= GPIOD_OUT_LOW;
+ else if (of_property_read_bool(np, "output-high"))
+ *dflags |= GPIOD_OUT_HIGH;
+ else {
+ pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n",
+ desc_to_gpio(gg_data.out_gpio), np->name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (name && of_property_read_string(np, "line-name", name))
+ *name = np->name;
+
+ desc = gg_data.out_gpio;
+
+ return desc;
+}
+
+/**
+ * of_gpiochip_scan_hogs - Scan gpio-controller and apply GPIO hog as requested
+ * @chip: gpio chip to act on
+ *
+ * This is only used by of_gpiochip_add to request/set GPIO initial
+ * configuration.
+ */
+static void of_gpiochip_scan_hogs(struct gpio_chip *chip)
+{
+ struct gpio_desc *desc = NULL;
+ struct device_node *np;
+ const char *name;
+ enum gpio_lookup_flags lflags;
+ enum gpiod_flags dflags;
+
+ for_each_child_of_node(chip->of_node, np) {
+ if (!of_property_read_bool(np, "gpio-hog"))
+ continue;
+
+ desc = of_get_gpio_hog(np, &name, &lflags, &dflags);
+ if (IS_ERR(desc))
+ continue;
+
+ if (gpiod_hog(desc, name, lflags, dflags))
+ continue;
+ }
+}
+
+/**
+ * of_gpio_simple_xlate - translate gpio_spec to the GPIO number and flags
+ * @gc: pointer to the gpio_chip structure
+ * @np: device node of the GPIO chip
+ * @gpio_spec: gpio specifier as found in the device tree
+ * @flags: a flags pointer to fill in
+ *
+ * This is simple translation function, suitable for the most 1:1 mapped
+ * 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)
+{
+ /*
+ * We're discouraging gpio_cells < 2, since that way you'll have to
+ * write your own xlate function (that will have to retrive the GPIO
+ * number and the flags from a single gpio cell -- this is possible,
+ * but not recommended).
+ */
+ if (gc->of_gpio_n_cells < 2) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+ return -EINVAL;
+
+ if (gpiospec->args[0] >= gc->ngpio)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return gpiospec->args[0];
+}
+EXPORT_SYMBOL(of_gpio_simple_xlate);
+
+/**
+ * of_mm_gpiochip_add - Add memory mapped GPIO chip (bank)
+ * @np: device node of the GPIO chip
+ * @mm_gc: pointer to the of_mm_gpio_chip allocated structure
+ *
+ * To use this function you should allocate and fill mm_gc with:
+ *
+ * 1) In the gpio_chip structure:
+ * - all the callbacks
+ * - of_gpio_n_cells
+ * - of_xlate callback (optional)
+ *
+ * 3) In the of_mm_gpio_chip structure:
+ * - save_regs callback (optional)
+ *
+ * If succeeded, this function will map bank's memory and will
+ * do all necessary work for you. Then you'll able to use .regs
+ * to manage GPIOs from the callbacks.
+ */
+int of_mm_gpiochip_add(struct device_node *np,
+ struct of_mm_gpio_chip *mm_gc)
+{
+ int ret = -ENOMEM;
+ struct gpio_chip *gc = &mm_gc->gc;
+
+ gc->label = kstrdup(np->full_name, GFP_KERNEL);
+ if (!gc->label)
+ goto err0;
+
+ mm_gc->regs = of_iomap(np, 0);
+ if (!mm_gc->regs)
+ goto err1;
+
+ gc->base = -1;
+
+ if (mm_gc->save_regs)
+ mm_gc->save_regs(mm_gc);
+
+ mm_gc->gc.of_node = np;
+
+ ret = gpiochip_add(gc);
+ if (ret)
+ goto err2;
+
+ return 0;
+err2:
+ iounmap(mm_gc->regs);
+err1:
+ kfree(gc->label);
+err0:
+ pr_err("%s: GPIO chip registration failed with status %d\n",
+ np->full_name, ret);
+ return ret;
+}
+EXPORT_SYMBOL(of_mm_gpiochip_add);
+
+/**
+ * of_mm_gpiochip_remove - Remove memory mapped GPIO chip (bank)
+ * @mm_gc: pointer to the of_mm_gpio_chip allocated structure
+ */
+void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
+{
+ struct gpio_chip *gc = &mm_gc->gc;
+
+ if (!mm_gc)
+ return;
+
+ gpiochip_remove(gc);
+ iounmap(mm_gc->regs);
+ kfree(gc->label);
+}
+EXPORT_SYMBOL(of_mm_gpiochip_remove);
+
+#ifdef CONFIG_PINCTRL
+static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
+{
+ struct device_node *np = chip->of_node;
+ struct of_phandle_args pinspec;
+ struct pinctrl_dev *pctldev;
+ int index = 0, ret;
+ const char *name;
+ static const char group_names_propname[] = "gpio-ranges-group-names";
+ struct property *group_names;
+
+ if (!np)
+ return;
+
+ group_names = of_find_property(np, group_names_propname, NULL);
+
+ for (;; index++) {
+ ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3,
+ index, &pinspec);
+ if (ret)
+ break;
+
+ pctldev = of_pinctrl_get(pinspec.np);
+ if (!pctldev)
+ break;
+
+ if (pinspec.args[2]) {
+ if (group_names) {
+ ret = of_property_read_string_index(np,
+ group_names_propname,
+ index, &name);
+ if (strlen(name)) {
+ pr_err("%s: Group name of numeric GPIO ranges must be the empty string.\n",
+ np->full_name);
+ break;
+ }
+ }
+ /* npins != 0: linear range */
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_devname(pctldev),
+ pinspec.args[0],
+ pinspec.args[1],
+ pinspec.args[2]);
+ if (ret)
+ break;
+ } else {
+ /* npins == 0: special range */
+ if (pinspec.args[1]) {
+ pr_err("%s: Illegal gpio-range format.\n",
+ np->full_name);
+ break;
+ }
+
+ if (!group_names) {
+ pr_err("%s: GPIO group range requested but no %s property.\n",
+ np->full_name, group_names_propname);
+ break;
+ }
+
+ ret = of_property_read_string_index(np,
+ group_names_propname,
+ index, &name);
+ if (ret)
+ break;
+
+ if (!strlen(name)) {
+ pr_err("%s: Group name of GPIO group range cannot be the empty string.\n",
+ np->full_name);
+ break;
+ }
+
+ ret = gpiochip_add_pingroup_range(chip, pctldev,
+ pinspec.args[0], name);
+ if (ret)
+ break;
+ }
+ }
+}
+
+#else
+static void of_gpiochip_add_pin_range(struct gpio_chip *chip) {}
+#endif
+
+void of_gpiochip_add(struct gpio_chip *chip)
+{
+ if ((!chip->of_node) && (chip->dev))
+ chip->of_node = chip->dev->of_node;
+
+ if (!chip->of_node)
+ return;
+
+ if (!chip->of_xlate) {
+ chip->of_gpio_n_cells = 2;
+ chip->of_xlate = of_gpio_simple_xlate;
+ }
+
+ of_gpiochip_add_pin_range(chip);
+ of_node_get(chip->of_node);
+
+ of_gpiochip_scan_hogs(chip);
+}
+
+void of_gpiochip_remove(struct gpio_chip *chip)
+{
+ gpiochip_remove_pin_ranges(chip);
+ of_node_put(chip->of_node);
+}
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
new file mode 100644
index 000000000..af3bc7a80
--- /dev/null
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -0,0 +1,859 @@
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+
+#include "gpiolib.h"
+
+static DEFINE_IDR(dirent_idr);
+
+
+/* lock protects against unexport_gpio() being called while
+ * sysfs files are active.
+ */
+static DEFINE_MUTEX(sysfs_lock);
+
+/*
+ * /sys/class/gpio/gpioN... only for GPIOs that are exported
+ * /direction
+ * * MAY BE OMITTED if kernel won't allow direction changes
+ * * is read/write as "in" or "out"
+ * * may also be written as "high" or "low", initializing
+ * output value as specified ("out" implies "low")
+ * /value
+ * * always readable, subject to hardware behavior
+ * * may be writable, as zero/nonzero
+ * /edge
+ * * configures behavior of poll(2) on /value
+ * * available only if pin can generate IRQs on input
+ * * is read/write as "none", "falling", "rising", or "both"
+ * /active_low
+ * * configures polarity of /value
+ * * is read/write as zero/nonzero
+ * * also affects existing and subsequent "falling" and "rising"
+ * /edge configuration
+ */
+
+static ssize_t gpio_direction_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags)) {
+ status = -EIO;
+ } else {
+ gpiod_get_direction(desc);
+ status = sprintf(buf, "%s\n",
+ test_bit(FLAG_IS_OUT, &desc->flags)
+ ? "out" : "in");
+ }
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t gpio_direction_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else if (sysfs_streq(buf, "high"))
+ status = gpiod_direction_output_raw(desc, 1);
+ else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low"))
+ status = gpiod_direction_output_raw(desc, 0);
+ else if (sysfs_streq(buf, "in"))
+ status = gpiod_direction_input(desc);
+ else
+ status = -EINVAL;
+
+ mutex_unlock(&sysfs_lock);
+ return status ? : size;
+}
+
+static /* const */ DEVICE_ATTR(direction, 0644,
+ gpio_direction_show, gpio_direction_store);
+
+static ssize_t gpio_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc));
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t gpio_value_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else if (!test_bit(FLAG_IS_OUT, &desc->flags))
+ status = -EPERM;
+ else {
+ long value;
+
+ status = kstrtol(buf, 0, &value);
+ if (status == 0) {
+ gpiod_set_value_cansleep(desc, value);
+ status = size;
+ }
+ }
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static DEVICE_ATTR(value, 0644,
+ gpio_value_show, gpio_value_store);
+
+static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
+{
+ struct kernfs_node *value_sd = priv;
+
+ sysfs_notify_dirent(value_sd);
+ return IRQ_HANDLED;
+}
+
+static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
+ unsigned long gpio_flags)
+{
+ struct kernfs_node *value_sd;
+ unsigned long irq_flags;
+ int ret, irq, id;
+
+ if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags)
+ return 0;
+
+ irq = gpiod_to_irq(desc);
+ if (irq < 0)
+ return -EIO;
+
+ id = desc->flags >> ID_SHIFT;
+ value_sd = idr_find(&dirent_idr, id);
+ if (value_sd)
+ free_irq(irq, value_sd);
+
+ desc->flags &= ~GPIO_TRIGGER_MASK;
+
+ if (!gpio_flags) {
+ gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+ ret = 0;
+ goto free_id;
+ }
+
+ irq_flags = IRQF_SHARED;
+ if (test_bit(FLAG_TRIG_FALL, &gpio_flags))
+ irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ if (test_bit(FLAG_TRIG_RISE, &gpio_flags))
+ irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+
+ if (!value_sd) {
+ value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
+ if (!value_sd) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ ret = idr_alloc(&dirent_idr, value_sd, 1, 0, GFP_KERNEL);
+ if (ret < 0)
+ goto free_sd;
+ id = ret;
+
+ desc->flags &= GPIO_FLAGS_MASK;
+ desc->flags |= (unsigned long)id << ID_SHIFT;
+
+ if (desc->flags >> ID_SHIFT != id) {
+ ret = -ERANGE;
+ goto free_id;
+ }
+ }
+
+ ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags,
+ "gpiolib", value_sd);
+ if (ret < 0)
+ goto free_id;
+
+ ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+ if (ret < 0) {
+ gpiod_warn(desc, "failed to flag the GPIO for IRQ\n");
+ goto free_id;
+ }
+
+ desc->flags |= gpio_flags;
+ return 0;
+
+free_id:
+ idr_remove(&dirent_idr, id);
+ desc->flags &= GPIO_FLAGS_MASK;
+free_sd:
+ if (value_sd)
+ sysfs_put(value_sd);
+err_out:
+ return ret;
+}
+
+static const struct {
+ const char *name;
+ unsigned long flags;
+} trigger_types[] = {
+ { "none", 0 },
+ { "falling", BIT(FLAG_TRIG_FALL) },
+ { "rising", BIT(FLAG_TRIG_RISE) },
+ { "both", BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) },
+};
+
+static ssize_t gpio_edge_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else {
+ int i;
+
+ status = 0;
+ for (i = 0; i < ARRAY_SIZE(trigger_types); i++)
+ if ((desc->flags & GPIO_TRIGGER_MASK)
+ == trigger_types[i].flags) {
+ status = sprintf(buf, "%s\n",
+ trigger_types[i].name);
+ break;
+ }
+ }
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t gpio_edge_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(trigger_types); i++)
+ if (sysfs_streq(trigger_types[i].name, buf))
+ goto found;
+ return -EINVAL;
+
+found:
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else {
+ status = gpio_setup_irq(desc, dev, trigger_types[i].flags);
+ if (!status)
+ status = size;
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+ return status;
+}
+
+static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);
+
+static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev,
+ int value)
+{
+ int status = 0;
+
+ 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);
+
+ /* reconfigure poll(2) support if enabled on one edge only */
+ if (dev != NULL && (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^
+ !!test_bit(FLAG_TRIG_FALL, &desc->flags))) {
+ unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK;
+
+ gpio_setup_irq(desc, dev, 0);
+ status = gpio_setup_irq(desc, dev, trigger_flags);
+ }
+
+ return status;
+}
+
+static ssize_t gpio_active_low_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%d\n",
+ !!test_bit(FLAG_ACTIVE_LOW, &desc->flags));
+
+ mutex_unlock(&sysfs_lock);
+
+ return status;
+}
+
+static ssize_t gpio_active_low_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags)) {
+ status = -EIO;
+ } else {
+ long value;
+
+ status = kstrtol(buf, 0, &value);
+ if (status == 0)
+ status = sysfs_set_active_low(desc, dev, value != 0);
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+ return status ? : size;
+}
+
+static DEVICE_ATTR(active_low, 0644,
+ gpio_active_low_show, gpio_active_low_store);
+
+static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
+ int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct gpio_desc *desc = dev_get_drvdata(dev);
+ umode_t mode = attr->mode;
+ bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags);
+
+ if (attr == &dev_attr_direction.attr) {
+ if (!show_direction)
+ mode = 0;
+ } else if (attr == &dev_attr_edge.attr) {
+ if (gpiod_to_irq(desc) < 0)
+ mode = 0;
+ if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags))
+ mode = 0;
+ }
+
+ return mode;
+}
+
+static struct attribute *gpio_attrs[] = {
+ &dev_attr_direction.attr,
+ &dev_attr_edge.attr,
+ &dev_attr_value.attr,
+ &dev_attr_active_low.attr,
+ NULL,
+};
+
+static const struct attribute_group gpio_group = {
+ .attrs = gpio_attrs,
+ .is_visible = gpio_is_visible,
+};
+
+static const struct attribute_group *gpio_groups[] = {
+ &gpio_group,
+ NULL
+};
+
+/*
+ * /sys/class/gpio/gpiochipN/
+ * /base ... matching gpio_chip.base (N)
+ * /label ... matching gpio_chip.label
+ * /ngpio ... matching gpio_chip.ngpio
+ */
+
+static ssize_t chip_base_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", chip->base);
+}
+static DEVICE_ATTR(base, 0444, chip_base_show, NULL);
+
+static ssize_t chip_label_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", chip->label ? : "");
+}
+static DEVICE_ATTR(label, 0444, chip_label_show, NULL);
+
+static ssize_t chip_ngpio_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", chip->ngpio);
+}
+static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);
+
+static struct attribute *gpiochip_attrs[] = {
+ &dev_attr_base.attr,
+ &dev_attr_label.attr,
+ &dev_attr_ngpio.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(gpiochip);
+
+/*
+ * /sys/class/gpio/export ... write-only
+ * integer N ... number of GPIO to export (full access)
+ * /sys/class/gpio/unexport ... write-only
+ * integer N ... number of GPIO to unexport
+ */
+static ssize_t export_store(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t len)
+{
+ long gpio;
+ struct gpio_desc *desc;
+ int status;
+
+ status = kstrtol(buf, 0, &gpio);
+ if (status < 0)
+ goto done;
+
+ desc = gpio_to_desc(gpio);
+ /* reject invalid GPIOs */
+ if (!desc) {
+ pr_warn("%s: invalid GPIO %ld\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;
+ goto done;
+ }
+ status = gpiod_export(desc, true);
+ if (status < 0)
+ gpiod_free(desc);
+ else
+ set_bit(FLAG_SYSFS, &desc->flags);
+
+done:
+ if (status)
+ pr_debug("%s: status %d\n", __func__, status);
+ return status ? : len;
+}
+
+static ssize_t unexport_store(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t len)
+{
+ long gpio;
+ struct gpio_desc *desc;
+ int status;
+
+ status = kstrtol(buf, 0, &gpio);
+ if (status < 0)
+ goto done;
+
+ desc = gpio_to_desc(gpio);
+ /* reject bogus commands (gpio_unexport ignores them) */
+ if (!desc) {
+ pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
+ return -EINVAL;
+ }
+
+ status = -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.
+ */
+ if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) {
+ status = 0;
+ gpiod_free(desc);
+ }
+done:
+ if (status)
+ pr_debug("%s: status %d\n", __func__, status);
+ return status ? : len;
+}
+
+static struct class_attribute gpio_class_attrs[] = {
+ __ATTR(export, 0200, NULL, export_store),
+ __ATTR(unexport, 0200, NULL, unexport_store),
+ __ATTR_NULL,
+};
+
+static struct class gpio_class = {
+ .name = "gpio",
+ .owner = THIS_MODULE,
+
+ .class_attrs = gpio_class_attrs,
+};
+
+
+/**
+ * gpiod_export - export a GPIO through sysfs
+ * @gpio: gpio to make available, already requested
+ * @direction_may_change: true if userspace may change gpio direction
+ * Context: arch_initcall or later
+ *
+ * When drivers want to make a GPIO accessible to userspace after they
+ * have requested it -- perhaps while debugging, or as part of their
+ * public interface -- they may use this routine. If the GPIO can
+ * change direction (some can't) and the caller allows it, userspace
+ * will see "direction" sysfs attribute which may be used to change
+ * the gpio's direction. A "value" attribute will always be provided.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
+{
+ struct gpio_chip *chip;
+ unsigned long flags;
+ int status;
+ const char *ioname = NULL;
+ struct device *dev;
+ int offset;
+
+ /* can't export until sysfs is available ... */
+ if (!gpio_class.p) {
+ pr_debug("%s: called too early!\n", __func__);
+ return -ENOENT;
+ }
+
+ if (!desc) {
+ pr_debug("%s: invalid gpio descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ chip = desc->chip;
+
+ mutex_lock(&sysfs_lock);
+
+ /* check if chip is being removed */
+ if (!chip || !chip->exported) {
+ status = -ENODEV;
+ goto fail_unlock;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+ test_bit(FLAG_EXPORT, &desc->flags)) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
+ __func__,
+ test_bit(FLAG_REQUESTED, &desc->flags),
+ test_bit(FLAG_EXPORT, &desc->flags));
+ status = -EPERM;
+ goto fail_unlock;
+ }
+
+ if (desc->chip->direction_input && desc->chip->direction_output &&
+ direction_may_change) {
+ set_bit(FLAG_SYSFS_DIR, &desc->flags);
+ }
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ offset = gpio_chip_hwgpio(desc);
+ if (desc->chip->names && desc->chip->names[offset])
+ ioname = desc->chip->names[offset];
+
+ dev = device_create_with_groups(&gpio_class, desc->chip->dev,
+ MKDEV(0, 0), desc, gpio_groups,
+ ioname ? ioname : "gpio%u",
+ desc_to_gpio(desc));
+ if (IS_ERR(dev)) {
+ status = PTR_ERR(dev);
+ goto fail_unlock;
+ }
+
+ set_bit(FLAG_EXPORT, &desc->flags);
+ mutex_unlock(&sysfs_lock);
+ return 0;
+
+fail_unlock:
+ mutex_unlock(&sysfs_lock);
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_export);
+
+static int match_export(struct device *dev, const void *data)
+{
+ return dev_get_drvdata(dev) == data;
+}
+
+/**
+ * gpiod_export_link - create a sysfs link to an exported GPIO node
+ * @dev: device under which to create symlink
+ * @name: name of the symlink
+ * @gpio: gpio to create symlink to, already exported
+ *
+ * Set up a symlink from /sys/.../dev/name to /sys/class/gpio/gpioN
+ * node. Caller is responsible for unlinking.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpiod_export_link(struct device *dev, const char *name,
+ struct gpio_desc *desc)
+{
+ int status = -EINVAL;
+
+ if (!desc) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&sysfs_lock);
+
+ if (test_bit(FLAG_EXPORT, &desc->flags)) {
+ struct device *tdev;
+
+ tdev = class_find_device(&gpio_class, NULL, desc, match_export);
+ if (tdev != NULL) {
+ status = sysfs_create_link(&dev->kobj, &tdev->kobj,
+ name);
+ put_device(tdev);
+ } else {
+ status = -ENODEV;
+ }
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+ if (status)
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_export_link);
+
+/**
+ * gpiod_sysfs_set_active_low - set the polarity of gpio sysfs value
+ * @gpio: gpio to change
+ * @value: non-zero to use active low, i.e. inverted values
+ *
+ * Set the polarity of /sys/class/gpio/gpioN/value sysfs attribute.
+ * The GPIO does not have to be exported yet. If poll(2) support has
+ * been enabled for either rising or falling edge, it will be
+ * reconfigured to follow the new polarity.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
+{
+ struct device *dev = NULL;
+ int status = -EINVAL;
+
+ if (!desc) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&sysfs_lock);
+
+ if (test_bit(FLAG_EXPORT, &desc->flags)) {
+ dev = class_find_device(&gpio_class, NULL, desc, match_export);
+ if (dev == NULL) {
+ status = -ENODEV;
+ goto unlock;
+ }
+ }
+
+ status = sysfs_set_active_low(desc, dev, value);
+ put_device(dev);
+unlock:
+ mutex_unlock(&sysfs_lock);
+
+ if (status)
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low);
+
+/**
+ * gpiod_unexport - reverse effect of gpio_export()
+ * @gpio: gpio to make unavailable
+ *
+ * This is implicit on gpio_free().
+ */
+void gpiod_unexport(struct gpio_desc *desc)
+{
+ int status = 0;
+ struct device *dev = NULL;
+
+ if (!desc) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return;
+ }
+
+ mutex_lock(&sysfs_lock);
+
+ if (test_bit(FLAG_EXPORT, &desc->flags)) {
+
+ dev = class_find_device(&gpio_class, NULL, desc, match_export);
+ if (dev) {
+ gpio_setup_irq(desc, dev, 0);
+ clear_bit(FLAG_SYSFS_DIR, &desc->flags);
+ clear_bit(FLAG_EXPORT, &desc->flags);
+ } else
+ status = -ENODEV;
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+ if (dev) {
+ device_unregister(dev);
+ put_device(dev);
+ }
+
+ if (status)
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+}
+EXPORT_SYMBOL_GPL(gpiod_unexport);
+
+int gpiochip_export(struct gpio_chip *chip)
+{
+ int status;
+ struct device *dev;
+
+ /* Many systems register gpio chips for SOC support very early,
+ * before driver model support is available. In those cases we
+ * export this later, in gpiolib_sysfs_init() ... here we just
+ * verify that _some_ field of gpio_class got initialized.
+ */
+ if (!gpio_class.p)
+ return 0;
+
+ /* use chip->base for the ID; it's already known to be unique */
+ mutex_lock(&sysfs_lock);
+ dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0),
+ chip, gpiochip_groups,
+ "gpiochip%d", chip->base);
+ if (IS_ERR(dev))
+ status = PTR_ERR(dev);
+ else
+ status = 0;
+ chip->exported = (status == 0);
+ mutex_unlock(&sysfs_lock);
+
+ if (status)
+ chip_dbg(chip, "%s: status %d\n", __func__, status);
+
+ return status;
+}
+
+void gpiochip_unexport(struct gpio_chip *chip)
+{
+ int status;
+ struct device *dev;
+ struct gpio_desc *desc;
+ unsigned int i;
+
+ mutex_lock(&sysfs_lock);
+ dev = class_find_device(&gpio_class, NULL, chip, match_export);
+ if (dev) {
+ put_device(dev);
+ device_unregister(dev);
+ /* prevent further gpiod exports */
+ chip->exported = false;
+ status = 0;
+ } else
+ status = -ENODEV;
+ mutex_unlock(&sysfs_lock);
+
+ if (status)
+ chip_dbg(chip, "%s: status %d\n", __func__, status);
+
+ /* unregister gpiod class devices owned by sysfs */
+ for (i = 0; i < chip->ngpio; i++) {
+ desc = &chip->desc[i];
+ if (test_and_clear_bit(FLAG_SYSFS, &desc->flags))
+ gpiod_free(desc);
+ }
+}
+
+static int __init gpiolib_sysfs_init(void)
+{
+ int status;
+ unsigned long flags;
+ struct gpio_chip *chip;
+
+ status = class_register(&gpio_class);
+ if (status < 0)
+ return status;
+
+ /* Scan and register the gpio_chips which registered very
+ * early (e.g. before the class_register above was called).
+ *
+ * We run before arch_initcall() so chip->dev nodes can have
+ * registered, and so arch_initcall() can always gpio_export().
+ */
+ spin_lock_irqsave(&gpio_lock, flags);
+ list_for_each_entry(chip, &gpio_chips, list) {
+ if (chip->exported)
+ continue;
+
+ /*
+ * TODO we yield gpio_lock here because gpiochip_export()
+ * acquires a mutex. This is unsafe and needs to be fixed.
+ *
+ * Also it would be nice to use gpiochip_find() here so we
+ * can keep gpio_chips local to gpiolib.c, but the yield of
+ * gpio_lock prevents us from doing this.
+ */
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ status = gpiochip_export(chip);
+ spin_lock_irqsave(&gpio_lock, flags);
+ }
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+
+ return status;
+}
+postcore_initcall(gpiolib_sysfs_init);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
new file mode 100644
index 000000000..6bc612b8a
--- /dev/null
+++ b/drivers/gpio/gpiolib.c
@@ -0,0 +1,2377 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#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 "gpiolib.h"
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/gpio.h>
+
+/* Implementation infrastructure for GPIO interfaces.
+ *
+ * The GPIO programming interface allows for inlining speed-critical
+ * get/set operations for common cases, so that access to SOC-integrated
+ * GPIOs can sometimes cost only an instruction or two per bit.
+ */
+
+
+/* When debugging, extend minimal trust to callers and platform code.
+ * Also emit diagnostic messages that may help initial bringup, when
+ * board setup or driver bugs are most common.
+ *
+ * Otherwise, minimize overhead in what may be bitbanging codepaths.
+ */
+#ifdef DEBUG
+#define extra_checks 1
+#else
+#define extra_checks 0
+#endif
+
+/* gpio_lock prevents conflicts during gpio_desc[] table updates.
+ * While any GPIO is requested, its gpio_chip is not removable;
+ * each GPIO's "requested" flag serves as a lock and refcount.
+ */
+DEFINE_SPINLOCK(gpio_lock);
+
+#define GPIO_OFFSET_VALID(chip, offset) (offset >= 0 && offset < chip->ngpio)
+
+static DEFINE_MUTEX(gpio_lookup_lock);
+static LIST_HEAD(gpio_lookup_list);
+LIST_HEAD(gpio_chips);
+
+
+static void gpiochip_free_hogs(struct gpio_chip *chip);
+static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
+
+
+static inline void desc_set_label(struct gpio_desc *d, const char *label)
+{
+ d->label = label;
+}
+
+/**
+ * Convert a GPIO number to its descriptor
+ */
+struct gpio_desc *gpio_to_desc(unsigned gpio)
+{
+ struct gpio_chip *chip;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ list_for_each_entry(chip, &gpio_chips, list) {
+ if (chip->base <= gpio && chip->base + chip->ngpio > gpio) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return &chip->desc[gpio - chip->base];
+ }
+ }
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ if (!gpio_is_valid(gpio))
+ WARN(1, "invalid GPIO %d\n", gpio);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gpio_to_desc);
+
+/**
+ * Get the GPIO descriptor corresponding to the given hw number for this chip.
+ */
+struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
+ u16 hwnum)
+{
+ if (hwnum >= chip->ngpio)
+ return ERR_PTR(-EINVAL);
+
+ return &chip->desc[hwnum];
+}
+
+/**
+ * Convert a GPIO descriptor to the integer namespace.
+ * This should disappear in the future but is needed since we still
+ * use GPIO numbers for error messages and sysfs nodes
+ */
+int desc_to_gpio(const struct gpio_desc *desc)
+{
+ return desc->chip->base + (desc - &desc->chip->desc[0]);
+}
+EXPORT_SYMBOL_GPL(desc_to_gpio);
+
+
+/**
+ * gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs
+ * @desc: descriptor to return the chip of
+ */
+struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
+{
+ return desc ? desc->chip : NULL;
+}
+EXPORT_SYMBOL_GPL(gpiod_to_chip);
+
+/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
+static int gpiochip_find_base(int ngpio)
+{
+ struct gpio_chip *chip;
+ int base = ARCH_NR_GPIOS - ngpio;
+
+ list_for_each_entry_reverse(chip, &gpio_chips, list) {
+ /* found a free space? */
+ if (chip->base + chip->ngpio <= base)
+ break;
+ else
+ /* nope, check the space right before the chip */
+ base = chip->base - ngpio;
+ }
+
+ if (gpio_is_valid(base)) {
+ pr_debug("%s: found new base at %d\n", __func__, base);
+ return base;
+ } else {
+ pr_err("%s: cannot find free range\n", __func__);
+ return -ENOSPC;
+ }
+}
+
+/**
+ * gpiod_get_direction - return the current direction of a GPIO
+ * @desc: GPIO to get the direction of
+ *
+ * Return GPIOF_DIR_IN or GPIOF_DIR_OUT, or an error code in case of error.
+ *
+ * This function may sleep if gpiod_cansleep() is true.
+ */
+int gpiod_get_direction(struct gpio_desc *desc)
+{
+ struct gpio_chip *chip;
+ unsigned offset;
+ int status = -EINVAL;
+
+ chip = gpiod_to_chip(desc);
+ offset = gpio_chip_hwgpio(desc);
+
+ if (!chip->get_direction)
+ return status;
+
+ 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;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_direction);
+
+/*
+ * Add a new chip to the global chips list, keeping the list of chips sorted
+ * by base order.
+ *
+ * Return -EBUSY if the new chip overlaps with some other chip's integer
+ * space.
+ */
+static int gpiochip_add_to_list(struct gpio_chip *chip)
+{
+ struct list_head *pos = &gpio_chips;
+ struct gpio_chip *_chip;
+ int err = 0;
+
+ /* find where to insert our chip */
+ list_for_each(pos, &gpio_chips) {
+ _chip = list_entry(pos, struct gpio_chip, list);
+ /* shall we insert before _chip? */
+ if (_chip->base >= chip->base + chip->ngpio)
+ break;
+ }
+
+ /* are we stepping on the chip right before? */
+ if (pos != &gpio_chips && pos->prev != &gpio_chips) {
+ _chip = list_entry(pos->prev, struct gpio_chip, list);
+ if (_chip->base + _chip->ngpio > chip->base) {
+ dev_err(chip->dev,
+ "GPIO integer space overlap, cannot add chip\n");
+ err = -EBUSY;
+ }
+ }
+
+ if (!err)
+ list_add_tail(&chip->list, pos);
+
+ return err;
+}
+
+/**
+ * gpiochip_add() - register a gpio_chip
+ * @chip: the chip to register, with chip->base initialized
+ * Context: potentially before irqs will work
+ *
+ * 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.
+ *
+ * When gpiochip_add() is called very early during boot, so that GPIOs
+ * can be freely used, the chip->dev device must be registered before
+ * the gpio framework's arch_initcall(). Otherwise sysfs initialization
+ * for GPIOs will fail rudely.
+ *
+ * If chip->base is negative, this requests dynamic assignment of
+ * a range of valid GPIOs.
+ */
+int gpiochip_add(struct gpio_chip *chip)
+{
+ unsigned long flags;
+ int status = 0;
+ unsigned id;
+ int base = chip->base;
+ struct gpio_desc *descs;
+
+ descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
+ if (!descs)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if (base < 0) {
+ base = gpiochip_find_base(chip->ngpio);
+ if (base < 0) {
+ status = base;
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ goto err_free_descs;
+ }
+ chip->base = base;
+ }
+
+ status = gpiochip_add_to_list(chip);
+ if (status) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ goto err_free_descs;
+ }
+
+ for (id = 0; id < chip->ngpio; id++) {
+ struct gpio_desc *desc = &descs[id];
+
+ desc->chip = chip;
+
+ /* REVISIT: most hardware initializes GPIOs as inputs (often
+ * with pullups enabled) so power usage is minimized. Linux
+ * code should set the gpio direction first thing; but until
+ * it does, and in case chip->get_direction is not set, we may
+ * expose the wrong direction in sysfs.
+ */
+ desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
+ }
+
+ chip->desc = descs;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+#ifdef CONFIG_PINCTRL
+ INIT_LIST_HEAD(&chip->pin_ranges);
+#endif
+
+ of_gpiochip_add(chip);
+ acpi_gpiochip_add(chip);
+
+ status = gpiochip_export(chip);
+ if (status)
+ goto err_remove_chip;
+
+ pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
+ chip->base, chip->base + chip->ngpio - 1,
+ chip->label ? : "generic");
+
+ return 0;
+
+err_remove_chip:
+ acpi_gpiochip_remove(chip);
+ gpiochip_free_hogs(chip);
+ of_gpiochip_remove(chip);
+ spin_lock_irqsave(&gpio_lock, flags);
+ list_del(&chip->list);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ chip->desc = NULL;
+err_free_descs:
+ kfree(descs);
+
+ /* failures here can mean systems won't boot... */
+ pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
+ chip->base, chip->base + chip->ngpio - 1,
+ chip->label ? : "generic");
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add);
+
+/**
+ * gpiochip_remove() - unregister a gpio_chip
+ * @chip: the chip to unregister
+ *
+ * A gpio_chip with any GPIOs still requested may not be removed.
+ */
+void gpiochip_remove(struct gpio_chip *chip)
+{
+ unsigned long flags;
+ unsigned id;
+
+ gpiochip_unexport(chip);
+
+ gpiochip_irqchip_remove(chip);
+
+ acpi_gpiochip_remove(chip);
+ gpiochip_remove_pin_ranges(chip);
+ gpiochip_free_hogs(chip);
+ of_gpiochip_remove(chip);
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ for (id = 0; id < chip->ngpio; id++) {
+ if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags))
+ dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
+ }
+ for (id = 0; id < chip->ngpio; id++)
+ chip->desc[id].chip = NULL;
+
+ list_del(&chip->list);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ kfree(chip->desc);
+ chip->desc = NULL;
+}
+EXPORT_SYMBOL_GPL(gpiochip_remove);
+
+/**
+ * gpiochip_find() - iterator for locating a specific gpio_chip
+ * @data: data to pass to match function
+ * @callback: Callback function to check gpio_chip
+ *
+ * Similar to bus_find_device. It returns a reference to a gpio_chip as
+ * determined by a user supplied @match callback. The callback should return
+ * 0 if the device doesn't match and non-zero if it does. If the callback is
+ * non-zero, this function will return to the caller and not iterate over any
+ * more gpio_chips.
+ */
+struct gpio_chip *gpiochip_find(void *data,
+ int (*match)(struct gpio_chip *chip,
+ void *data))
+{
+ struct gpio_chip *chip;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ list_for_each_entry(chip, &gpio_chips, list)
+ if (match(chip, data))
+ break;
+
+ /* No match? */
+ if (&chip->list == &gpio_chips)
+ chip = NULL;
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return chip;
+}
+EXPORT_SYMBOL_GPL(gpiochip_find);
+
+static int gpiochip_match_name(struct gpio_chip *chip, void *data)
+{
+ const char *name = data;
+
+ return !strcmp(chip->label, name);
+}
+
+static struct gpio_chip *find_chip_by_name(const char *name)
+{
+ return gpiochip_find((void *)name, gpiochip_match_name);
+}
+
+#ifdef CONFIG_GPIOLIB_IRQCHIP
+
+/*
+ * The following is irqchip helper code for gpiochips.
+ */
+
+/**
+ * gpiochip_set_chained_irqchip() - sets 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. If the interrupt is nested rather than
+ * cascaded, pass NULL in this handler argument
+ */
+void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ int parent_irq,
+ irq_flow_handler_t parent_handler)
+{
+ unsigned int offset;
+
+ if (!gpiochip->irqdomain) {
+ chip_err(gpiochip, "called %s before setting up irqchip\n",
+ __func__);
+ return;
+ }
+
+ if (parent_handler) {
+ if (gpiochip->can_sleep) {
+ chip_err(gpiochip,
+ "you cannot have chained interrupts on a "
+ "chip that may sleep\n");
+ return;
+ }
+ /*
+ * The parent irqchip is already using the chip_data for this
+ * irqchip, so our callbacks simply use the handler_data.
+ */
+ irq_set_handler_data(parent_irq, gpiochip);
+ irq_set_chained_handler(parent_irq, parent_handler);
+ }
+
+ /* Set the parent IRQ for all affected IRQs */
+ for (offset = 0; offset < gpiochip->ngpio; offset++)
+ irq_set_parent(irq_find_mapping(gpiochip->irqdomain, offset),
+ parent_irq);
+}
+EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
+
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpiochip_irq_lock_class;
+
+/**
+ * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip
+ * @d: the irqdomain used by this irqchip
+ * @irq: the global irq number used by this GPIO irqchip irq
+ * @hwirq: the local IRQ/GPIO line offset on this gpiochip
+ *
+ * This function will set up the mapping for a certain IRQ line on a
+ * gpiochip by assigning the gpiochip as chip data, and using the irqchip
+ * stored inside the gpiochip.
+ */
+static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct gpio_chip *chip = d->host_data;
+
+ irq_set_chip_data(irq, chip);
+ irq_set_lockdep_class(irq, &gpiochip_irq_lock_class);
+ irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler);
+ /* Chips that can sleep need nested thread handlers */
+ if (chip->can_sleep && !chip->irq_not_threaded)
+ irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ /*
+ * 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);
+
+ return 0;
+}
+
+static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+ struct gpio_chip *chip = d->host_data;
+
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ if (chip->can_sleep)
+ irq_set_nested_thread(irq, 0);
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops gpiochip_domain_ops = {
+ .map = gpiochip_irq_map,
+ .unmap = gpiochip_irq_unmap,
+ /* Virtually all GPIO irqchips are twocell:ed */
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int gpiochip_irq_reqres(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ if (gpiochip_lock_as_irq(chip, d->hwirq)) {
+ chip_err(chip,
+ "unable to lock HW IRQ %lu for IRQ\n",
+ d->hwirq);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void gpiochip_irq_relres(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ gpiochip_unlock_as_irq(chip, d->hwirq);
+}
+
+static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return irq_find_mapping(chip->irqdomain, offset);
+}
+
+/**
+ * gpiochip_irqchip_remove() - removes an irqchip added to a gpiochip
+ * @gpiochip: the gpiochip to remove the irqchip from
+ *
+ * This is called only from gpiochip_remove()
+ */
+static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
+{
+ unsigned int offset;
+
+ acpi_gpiochip_free_interrupts(gpiochip);
+
+ /* Remove all IRQ mappings and delete the domain */
+ if (gpiochip->irqdomain) {
+ for (offset = 0; offset < gpiochip->ngpio; offset++)
+ irq_dispose_mapping(
+ irq_find_mapping(gpiochip->irqdomain, offset));
+ irq_domain_remove(gpiochip->irqdomain);
+ }
+
+ if (gpiochip->irqchip) {
+ gpiochip->irqchip->irq_request_resources = NULL;
+ gpiochip->irqchip->irq_release_resources = NULL;
+ gpiochip->irqchip = NULL;
+ }
+}
+
+/**
+ * gpiochip_irqchip_add() - 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.
+ *
+ * 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 container_of() 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.
+ */
+int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ unsigned int first_irq,
+ irq_flow_handler_t handler,
+ unsigned int type)
+{
+ struct device_node *of_node;
+ unsigned int offset;
+ unsigned irq_base = 0;
+
+ if (!gpiochip || !irqchip)
+ return -EINVAL;
+
+ if (!gpiochip->dev) {
+ pr_err("missing gpiochip .dev parent pointer\n");
+ return -EINVAL;
+ }
+ of_node = gpiochip->dev->of_node;
+#ifdef CONFIG_OF_GPIO
+ /*
+ * If the gpiochip has an assigned OF node this takes precendence
+ * FIXME: get rid of this and use gpiochip->dev->of_node everywhere
+ */
+ if (gpiochip->of_node)
+ of_node = gpiochip->of_node;
+#endif
+ gpiochip->irqchip = irqchip;
+ gpiochip->irq_handler = handler;
+ gpiochip->irq_default_type = type;
+ gpiochip->to_irq = gpiochip_to_irq;
+ gpiochip->irqdomain = irq_domain_add_simple(of_node,
+ gpiochip->ngpio, first_irq,
+ &gpiochip_domain_ops, gpiochip);
+ if (!gpiochip->irqdomain) {
+ gpiochip->irqchip = NULL;
+ return -EINVAL;
+ }
+ irqchip->irq_request_resources = gpiochip_irq_reqres;
+ irqchip->irq_release_resources = gpiochip_irq_relres;
+
+ /*
+ * Prepare the mapping since the irqchip shall be orthogonal to
+ * any gpiochip calls. If the first_irq was zero, this is
+ * necessary to allocate descriptors for all IRQs.
+ */
+ for (offset = 0; offset < gpiochip->ngpio; offset++) {
+ irq_base = irq_create_mapping(gpiochip->irqdomain, offset);
+ if (offset == 0)
+ /*
+ * Store the base into the gpiochip to be used when
+ * unmapping the irqs.
+ */
+ gpiochip->irq_base = irq_base;
+ }
+
+ acpi_gpiochip_request_interrupts(gpiochip);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_irqchip_add);
+
+#else /* CONFIG_GPIOLIB_IRQCHIP */
+
+static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
+
+#endif /* CONFIG_GPIOLIB_IRQCHIP */
+
+#ifdef CONFIG_PINCTRL
+
+/**
+ * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
+ * @chip: the gpiochip to add the range for
+ * @pinctrl: the dev_name() of 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
+ */
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ chip_err(chip, "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.base = chip->base + gpio_offset;
+ pin_range->pctldev = pctldev;
+
+ ret = pinctrl_get_group_pins(pctldev, pin_group,
+ &pin_range->range.pins,
+ &pin_range->range.npins);
+ if (ret < 0) {
+ kfree(pin_range);
+ return ret;
+ }
+
+ pinctrl_add_gpio_range(pctldev, &pin_range->range);
+
+ chip_dbg(chip, "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);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+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
+ * @pinctrl_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
+ * @npins: the number of pins from the offset of each pin space (GPIO and
+ * pin controller) to accumulate in this range
+ */
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int gpio_offset, unsigned int pin_offset,
+ unsigned int npins)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ chip_err(chip, "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.base = chip->base + gpio_offset;
+ pin_range->range.pin_base = pin_offset;
+ pin_range->range.npins = npins;
+ pin_range->pctldev = pinctrl_find_and_add_gpio_range(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");
+ kfree(pin_range);
+ return ret;
+ }
+ chip_dbg(chip, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
+ gpio_offset, gpio_offset + npins - 1,
+ pinctl_name,
+ pin_offset, pin_offset + npins - 1);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+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
+ */
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+ struct gpio_pin_range *pin_range, *tmp;
+
+ list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
+ list_del(&pin_range->node);
+ pinctrl_remove_gpio_range(pin_range->pctldev,
+ &pin_range->range);
+ kfree(pin_range);
+ }
+}
+EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
+
+#endif /* CONFIG_PINCTRL */
+
+/* These "optional" allocation calls help prevent drivers from stomping
+ * on each other, and help provide better diagnostics in debugfs.
+ * They're called even less than the "set direction" calls.
+ */
+static int __gpiod_request(struct gpio_desc *desc, const char *label)
+{
+ struct gpio_chip *chip = desc->chip;
+ int status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* NOTE: gpio_request() can be called in early boot,
+ * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
+ */
+
+ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
+ desc_set_label(desc, label ? : "?");
+ status = 0;
+ } else {
+ status = -EBUSY;
+ goto done;
+ }
+
+ if (chip->request) {
+ /* chip->request may sleep */
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ status = chip->request(chip, gpio_chip_hwgpio(desc));
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if (status < 0) {
+ desc_set_label(desc, NULL);
+ clear_bit(FLAG_REQUESTED, &desc->flags);
+ goto done;
+ }
+ }
+ if (chip->get_direction) {
+ /* chip->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;
+}
+
+int gpiod_request(struct gpio_desc *desc, const char *label)
+{
+ int status = -EPROBE_DEFER;
+ struct gpio_chip *chip;
+
+ if (!desc) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+
+ chip = desc->chip;
+ if (!chip)
+ goto done;
+
+ if (try_module_get(chip->owner)) {
+ status = __gpiod_request(desc, label);
+ if (status < 0)
+ module_put(chip->owner);
+ }
+
+done:
+ if (status)
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+
+ return status;
+}
+
+static bool __gpiod_free(struct gpio_desc *desc)
+{
+ bool ret = false;
+ unsigned long flags;
+ struct gpio_chip *chip;
+
+ might_sleep();
+
+ gpiod_unexport(desc);
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ chip = desc->chip;
+ if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
+ if (chip->free) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ might_sleep_if(chip->can_sleep);
+ chip->free(chip, gpio_chip_hwgpio(desc));
+ spin_lock_irqsave(&gpio_lock, flags);
+ }
+ desc_set_label(desc, NULL);
+ clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ clear_bit(FLAG_REQUESTED, &desc->flags);
+ clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
+ clear_bit(FLAG_IS_HOGGED, &desc->flags);
+ ret = true;
+ }
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return ret;
+}
+
+void gpiod_free(struct gpio_desc *desc)
+{
+ if (desc && __gpiod_free(desc))
+ module_put(desc->chip->owner);
+ else
+ WARN_ON(extra_checks);
+}
+
+/**
+ * gpiochip_is_requested - return string iff signal was requested
+ * @chip: 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.
+ * The string returned is the label passed to gpio_request(); if none has been
+ * passed it is a meaningless, non-NULL constant.
+ *
+ * This function is for use by GPIO controller drivers. The label can
+ * help with diagnostics, and knowing that the signal is used as a GPIO
+ * can help avoid accidentally multiplexing it to another controller.
+ */
+const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_desc *desc;
+
+ if (!GPIO_OFFSET_VALID(chip, offset))
+ return NULL;
+
+ desc = &chip->desc[offset];
+
+ if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
+ return NULL;
+ return desc->label;
+}
+EXPORT_SYMBOL_GPL(gpiochip_is_requested);
+
+/**
+ * gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor
+ * @desc: GPIO descriptor to request
+ * @label: label for the GPIO
+ *
+ * Function allows GPIO chip drivers to request and use their own GPIO
+ * descriptors via gpiolib API. Difference to gpiod_request() is that this
+ * function will not increase reference count of the GPIO chip module. This
+ * allows the GPIO chip module to be unloaded as needed (we assume that the
+ * GPIO chip driver handles freeing the GPIOs it has requested).
+ */
+struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
+ const char *label)
+{
+ struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
+ int err;
+
+ if (IS_ERR(desc)) {
+ chip_err(chip, "failed to get GPIO descriptor\n");
+ return desc;
+ }
+
+ err = __gpiod_request(desc, label);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return desc;
+}
+EXPORT_SYMBOL_GPL(gpiochip_request_own_desc);
+
+/**
+ * gpiochip_free_own_desc - Free GPIO requested by the chip driver
+ * @desc: GPIO descriptor to free
+ *
+ * Function frees the given GPIO requested previously with
+ * gpiochip_request_own_desc().
+ */
+void gpiochip_free_own_desc(struct gpio_desc *desc)
+{
+ if (desc)
+ __gpiod_free(desc);
+}
+EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
+
+/* Drivers MUST set GPIO direction before making get/set calls. In
+ * some cases this is done in early boot, before IRQs are enabled.
+ *
+ * As a rule these aren't called more than once (except for drivers
+ * using the open-drain emulation idiom) so these are natural places
+ * to accumulate extra debugging checks. Note that we can't (yet)
+ * rely on gpio_request() having been called beforehand.
+ */
+
+/**
+ * gpiod_direction_input - set the GPIO direction to input
+ * @desc: GPIO to set to input
+ *
+ * Set the direction of the passed GPIO to input, such as gpiod_get_value() can
+ * be called safely on it.
+ *
+ * Return 0 in case of success, else an error code.
+ */
+int gpiod_direction_input(struct gpio_desc *desc)
+{
+ struct gpio_chip *chip;
+ int status = -EINVAL;
+
+ if (!desc || !desc->chip) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+
+ chip = desc->chip;
+ if (!chip->get || !chip->direction_input) {
+ gpiod_warn(desc,
+ "%s: missing get() or direction_input() operations\n",
+ __func__);
+ return -EIO;
+ }
+
+ status = chip->direction_input(chip, gpio_chip_hwgpio(desc));
+ if (status == 0)
+ clear_bit(FLAG_IS_OUT, &desc->flags);
+
+ trace_gpio_direction(desc_to_gpio(desc), 1, status);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_input);
+
+static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
+{
+ struct gpio_chip *chip;
+ int status = -EINVAL;
+
+ /* GPIOs used for IRQs shall not be set as output */
+ if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
+ gpiod_err(desc,
+ "%s: tried to set a GPIO tied to an IRQ as output\n",
+ __func__);
+ return -EIO;
+ }
+
+ /* Open drain pin should not be driven to 1 */
+ if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags))
+ return gpiod_direction_input(desc);
+
+ /* Open source pin should not be driven to 0 */
+ if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags))
+ return gpiod_direction_input(desc);
+
+ chip = desc->chip;
+ if (!chip->set || !chip->direction_output) {
+ gpiod_warn(desc,
+ "%s: missing set() or direction_output() operations\n",
+ __func__);
+ return -EIO;
+ }
+
+ status = chip->direction_output(chip, gpio_chip_hwgpio(desc), value);
+ if (status == 0)
+ set_bit(FLAG_IS_OUT, &desc->flags);
+ trace_gpio_value(desc_to_gpio(desc), 0, value);
+ trace_gpio_direction(desc_to_gpio(desc), 0, status);
+ return status;
+}
+
+/**
+ * gpiod_direction_output_raw - set the GPIO direction to output
+ * @desc: GPIO to set to output
+ * @value: initial output value of the GPIO
+ *
+ * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
+ * be called safely on it. The initial value of the output must be specified
+ * as raw value on the physical line without regard for the ACTIVE_LOW status.
+ *
+ * Return 0 in case of success, else an error code.
+ */
+int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
+{
+ if (!desc || !desc->chip) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+ return _gpiod_direction_output_raw(desc, value);
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
+
+/**
+ * gpiod_direction_output - set the GPIO direction to output
+ * @desc: GPIO to set to output
+ * @value: initial output value of the GPIO
+ *
+ * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
+ * be called safely on it. The initial value of the output must be specified
+ * as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
+ * account.
+ *
+ * Return 0 in case of success, else an error code.
+ */
+int gpiod_direction_output(struct gpio_desc *desc, int value)
+{
+ if (!desc || !desc->chip) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
+ return _gpiod_direction_output_raw(desc, value);
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_output);
+
+/**
+ * gpiod_set_debounce - sets @debounce time for a @gpio
+ * @gpio: the gpio to set debounce time
+ * @debounce: debounce time is microseconds
+ *
+ * returns -ENOTSUPP if the controller does not support setting
+ * debounce.
+ */
+int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
+{
+ struct gpio_chip *chip;
+
+ if (!desc || !desc->chip) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+
+ chip = desc->chip;
+ if (!chip->set || !chip->set_debounce) {
+ gpiod_dbg(desc,
+ "%s: missing set() or set_debounce() operations\n",
+ __func__);
+ return -ENOTSUPP;
+ }
+
+ return chip->set_debounce(chip, gpio_chip_hwgpio(desc), debounce);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_debounce);
+
+/**
+ * gpiod_is_active_low - test whether a GPIO is active-low or not
+ * @desc: the gpio descriptor to test
+ *
+ * Returns 1 if the GPIO is active-low, 0 otherwise.
+ */
+int gpiod_is_active_low(const struct gpio_desc *desc)
+{
+ return test_bit(FLAG_ACTIVE_LOW, &desc->flags);
+}
+EXPORT_SYMBOL_GPL(gpiod_is_active_low);
+
+/* I/O calls are only valid after configuration completed; the relevant
+ * "is this a valid GPIO" error checks should already have been done.
+ *
+ * "Get" operations are often inlinable as reading a pin value register,
+ * and masking the relevant bit in that register.
+ *
+ * When "set" operations are inlinable, they involve writing that mask to
+ * one register to set a low value, or a different register to set it high.
+ * Otherwise locking is needed, so there may be little value to inlining.
+ *
+ *------------------------------------------------------------------------
+ *
+ * IMPORTANT!!! The hot paths -- get/set value -- assume that callers
+ * have requested the GPIO. That can include implicit requesting by
+ * a direction setting call. Marking a gpio as requested locks its chip
+ * in memory, guaranteeing that these table lookups need no more locking
+ * and that gpiochip_remove() will fail.
+ *
+ * REVISIT when debugging, consider adding some instrumentation to ensure
+ * that the GPIO was actually requested.
+ */
+
+static bool _gpiod_get_raw_value(const struct gpio_desc *desc)
+{
+ struct gpio_chip *chip;
+ bool value;
+ int offset;
+
+ chip = desc->chip;
+ offset = gpio_chip_hwgpio(desc);
+ value = chip->get ? chip->get(chip, offset) : false;
+ trace_gpio_value(desc_to_gpio(desc), 1, value);
+ return value;
+}
+
+/**
+ * gpiod_get_raw_value() - return a gpio's raw value
+ * @desc: gpio whose value will be returned
+ *
+ * Return the GPIO's raw value, i.e. the value of the physical line disregarding
+ * its ACTIVE_LOW status.
+ *
+ * This function should be called from contexts where we cannot sleep, and will
+ * complain if the GPIO chip functions potentially sleep.
+ */
+int gpiod_get_raw_value(const struct gpio_desc *desc)
+{
+ if (!desc)
+ return 0;
+ /* Should be using gpio_get_value_cansleep() */
+ WARN_ON(desc->chip->can_sleep);
+ return _gpiod_get_raw_value(desc);
+}
+EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
+
+/**
+ * gpiod_get_value() - return a gpio's value
+ * @desc: gpio whose value will be returned
+ *
+ * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into
+ * account.
+ *
+ * This function should be called from contexts where we cannot sleep, and will
+ * complain if the GPIO chip functions potentially sleep.
+ */
+int gpiod_get_value(const struct gpio_desc *desc)
+{
+ int value;
+ if (!desc)
+ return 0;
+ /* Should be using gpio_get_value_cansleep() */
+ WARN_ON(desc->chip->can_sleep);
+
+ value = _gpiod_get_raw_value(desc);
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
+
+ return value;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_value);
+
+/*
+ * _gpio_set_open_drain_value() - Set the open drain gpio's value.
+ * @desc: gpio descriptor whose state need to be set.
+ * @value: Non-zero for setting it HIGH otherise it will set to LOW.
+ */
+static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
+{
+ int err = 0;
+ struct gpio_chip *chip = desc->chip;
+ int offset = gpio_chip_hwgpio(desc);
+
+ if (value) {
+ err = chip->direction_input(chip, offset);
+ if (!err)
+ clear_bit(FLAG_IS_OUT, &desc->flags);
+ } else {
+ err = chip->direction_output(chip, offset, 0);
+ if (!err)
+ set_bit(FLAG_IS_OUT, &desc->flags);
+ }
+ trace_gpio_direction(desc_to_gpio(desc), value, err);
+ if (err < 0)
+ gpiod_err(desc,
+ "%s: Error in set_value for open drain err %d\n",
+ __func__, err);
+}
+
+/*
+ * _gpio_set_open_source_value() - Set the open source gpio's value.
+ * @desc: gpio descriptor whose state need to be set.
+ * @value: Non-zero for setting it HIGH otherise it will set to LOW.
+ */
+static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value)
+{
+ int err = 0;
+ struct gpio_chip *chip = desc->chip;
+ int offset = gpio_chip_hwgpio(desc);
+
+ if (value) {
+ err = chip->direction_output(chip, offset, 1);
+ if (!err)
+ set_bit(FLAG_IS_OUT, &desc->flags);
+ } else {
+ err = chip->direction_input(chip, offset);
+ if (!err)
+ clear_bit(FLAG_IS_OUT, &desc->flags);
+ }
+ trace_gpio_direction(desc_to_gpio(desc), !value, err);
+ if (err < 0)
+ gpiod_err(desc,
+ "%s: Error in set_value for open source err %d\n",
+ __func__, err);
+}
+
+static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
+{
+ struct gpio_chip *chip;
+
+ chip = desc->chip;
+ trace_gpio_value(desc_to_gpio(desc), 0, value);
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
+ _gpio_set_open_drain_value(desc, value);
+ else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
+ _gpio_set_open_source_value(desc, value);
+ else
+ chip->set(chip, 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;
+ * @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,
+ unsigned long *mask, unsigned long *bits)
+{
+ if (chip->set_multiple) {
+ chip->set_multiple(chip, mask, bits);
+ } else {
+ int i;
+ for (i = 0; i < chip->ngpio; i++) {
+ if (mask[BIT_WORD(i)] == 0) {
+ /* no more set bits in this mask word;
+ * skip ahead to the next word */
+ i = (BIT_WORD(i) + 1) * BITS_PER_LONG - 1;
+ continue;
+ }
+ /* set outputs if the corresponding mask bit is set */
+ if (__test_and_clear_bit(i, mask)) {
+ chip->set(chip, i, test_bit(i, bits));
+ }
+ }
+ }
+}
+
+static void gpiod_set_array_priv(bool raw, bool can_sleep,
+ unsigned int array_size,
+ struct gpio_desc **desc_array,
+ int *value_array)
+{
+ int i = 0;
+
+ while (i < array_size) {
+ struct gpio_chip *chip = desc_array[i]->chip;
+ unsigned long mask[BITS_TO_LONGS(chip->ngpio)];
+ unsigned long bits[BITS_TO_LONGS(chip->ngpio)];
+ int count = 0;
+
+ if (!can_sleep) {
+ WARN_ON(chip->can_sleep);
+ }
+ memset(mask, 0, sizeof(mask));
+ do {
+ struct gpio_desc *desc = desc_array[i];
+ int hwgpio = gpio_chip_hwgpio(desc);
+ int value = value_array[i];
+
+ if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
+ trace_gpio_value(desc_to_gpio(desc), 0, value);
+ /*
+ * collect all normal outputs belonging to the same chip
+ * open drain and open source outputs are set individually
+ */
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
+ _gpio_set_open_drain_value(desc,value);
+ } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
+ _gpio_set_open_source_value(desc, value);
+ } else {
+ __set_bit(hwgpio, mask);
+ if (value) {
+ __set_bit(hwgpio, bits);
+ } else {
+ __clear_bit(hwgpio, bits);
+ }
+ count++;
+ }
+ i++;
+ } while ((i < array_size) && (desc_array[i]->chip == chip));
+ /* push collected bits to outputs */
+ if (count != 0) {
+ gpio_chip_set_multiple(chip, mask, bits);
+ }
+ }
+}
+
+/**
+ * gpiod_set_raw_value() - assign a gpio's raw value
+ * @desc: gpio whose value will be assigned
+ * @value: value to assign
+ *
+ * Set the raw value of the GPIO, i.e. the value of its physical line without
+ * regard for its ACTIVE_LOW status.
+ *
+ * This function should be called from contexts where we cannot sleep, and will
+ * complain if the GPIO chip functions potentially sleep.
+ */
+void gpiod_set_raw_value(struct gpio_desc *desc, int value)
+{
+ if (!desc)
+ return;
+ /* Should be using gpio_set_value_cansleep() */
+ WARN_ON(desc->chip->can_sleep);
+ _gpiod_set_raw_value(desc, value);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
+
+/**
+ * gpiod_set_value() - assign a gpio's value
+ * @desc: gpio whose value will be assigned
+ * @value: value to assign
+ *
+ * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
+ * account
+ *
+ * This function should be called from contexts where we cannot sleep, and will
+ * complain if the GPIO chip functions potentially sleep.
+ */
+void gpiod_set_value(struct gpio_desc *desc, int value)
+{
+ if (!desc)
+ return;
+ /* Should be using gpio_set_value_cansleep() */
+ WARN_ON(desc->chip->can_sleep);
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
+ _gpiod_set_raw_value(desc, value);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_value);
+
+/**
+ * gpiod_set_raw_array() - assign values to an array of GPIOs
+ * @array_size: number of elements in the descriptor / value arrays
+ * @desc_array: array of GPIO descriptors whose values will be assigned
+ * @value_array: array of values to assign
+ *
+ * Set the raw values of the GPIOs, i.e. the values of the physical lines
+ * without regard for their ACTIVE_LOW status.
+ *
+ * This function should be called from contexts where we cannot sleep, and will
+ * complain if the GPIO chip functions potentially sleep.
+ */
+void gpiod_set_raw_array(unsigned int array_size,
+ struct gpio_desc **desc_array, int *value_array)
+{
+ if (!desc_array)
+ return;
+ gpiod_set_array_priv(true, false, array_size, desc_array, value_array);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_raw_array);
+
+/**
+ * gpiod_set_array() - assign values to an array of GPIOs
+ * @array_size: number of elements in the descriptor / value arrays
+ * @desc_array: array of GPIO descriptors whose values will be assigned
+ * @value_array: array of values to assign
+ *
+ * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
+ * into account.
+ *
+ * This function should be called from contexts where we cannot sleep, and will
+ * complain if the GPIO chip functions potentially sleep.
+ */
+void gpiod_set_array(unsigned int array_size,
+ struct gpio_desc **desc_array, int *value_array)
+{
+ if (!desc_array)
+ return;
+ gpiod_set_array_priv(false, false, array_size, desc_array, value_array);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_array);
+
+/**
+ * gpiod_cansleep() - report whether gpio value access may sleep
+ * @desc: gpio to check
+ *
+ */
+int gpiod_cansleep(const struct gpio_desc *desc)
+{
+ if (!desc)
+ return 0;
+ return desc->chip->can_sleep;
+}
+EXPORT_SYMBOL_GPL(gpiod_cansleep);
+
+/**
+ * gpiod_to_irq() - return the IRQ corresponding to a GPIO
+ * @desc: gpio whose IRQ will be returned (already requested)
+ *
+ * Return the IRQ corresponding to the passed GPIO, or an error code in case of
+ * error.
+ */
+int gpiod_to_irq(const struct gpio_desc *desc)
+{
+ struct gpio_chip *chip;
+ int offset;
+
+ if (!desc)
+ return -EINVAL;
+ chip = desc->chip;
+ offset = gpio_chip_hwgpio(desc);
+ return chip->to_irq ? chip->to_irq(chip, offset) : -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
+ * @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)
+{
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ if (test_bit(FLAG_IS_OUT, &chip->desc[offset].flags)) {
+ chip_err(chip,
+ "%s: tried to flag a GPIO set as output for IRQ\n",
+ __func__);
+ return -EIO;
+ }
+
+ set_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
+ return 0;
+}
+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
+ * @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)
+{
+ if (offset >= chip->ngpio)
+ return;
+
+ clear_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
+}
+EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
+
+/**
+ * gpiod_get_raw_value_cansleep() - return a gpio's raw value
+ * @desc: gpio whose value will be returned
+ *
+ * Return the GPIO's raw value, i.e. the value of the physical line disregarding
+ * its ACTIVE_LOW status.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
+{
+ might_sleep_if(extra_checks);
+ if (!desc)
+ return 0;
+ return _gpiod_get_raw_value(desc);
+}
+EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep);
+
+/**
+ * gpiod_get_value_cansleep() - return a gpio's value
+ * @desc: gpio whose value will be returned
+ *
+ * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into
+ * account.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+int gpiod_get_value_cansleep(const struct gpio_desc *desc)
+{
+ int value;
+
+ might_sleep_if(extra_checks);
+ if (!desc)
+ return 0;
+
+ value = _gpiod_get_raw_value(desc);
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
+
+ return value;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
+
+/**
+ * gpiod_set_raw_value_cansleep() - assign a gpio's raw value
+ * @desc: gpio whose value will be assigned
+ * @value: value to assign
+ *
+ * Set the raw value of the GPIO, i.e. the value of its physical line without
+ * regard for its ACTIVE_LOW status.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
+{
+ might_sleep_if(extra_checks);
+ if (!desc)
+ return;
+ _gpiod_set_raw_value(desc, value);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
+
+/**
+ * gpiod_set_value_cansleep() - assign a gpio's value
+ * @desc: gpio whose value will be assigned
+ * @value: value to assign
+ *
+ * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
+ * account
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
+{
+ might_sleep_if(extra_checks);
+ if (!desc)
+ return;
+
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
+ _gpiod_set_raw_value(desc, value);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
+
+/**
+ * gpiod_set_raw_array_cansleep() - assign values to an array of GPIOs
+ * @array_size: number of elements in the descriptor / value arrays
+ * @desc_array: array of GPIO descriptors whose values will be assigned
+ * @value_array: array of values to assign
+ *
+ * Set the raw values of the GPIOs, i.e. the values of the physical lines
+ * without regard for their ACTIVE_LOW status.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+void gpiod_set_raw_array_cansleep(unsigned int array_size,
+ struct gpio_desc **desc_array,
+ int *value_array)
+{
+ might_sleep_if(extra_checks);
+ if (!desc_array)
+ return;
+ gpiod_set_array_priv(true, true, array_size, desc_array, value_array);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_raw_array_cansleep);
+
+/**
+ * gpiod_set_array_cansleep() - assign values to an array of GPIOs
+ * @array_size: number of elements in the descriptor / value arrays
+ * @desc_array: array of GPIO descriptors whose values will be assigned
+ * @value_array: array of values to assign
+ *
+ * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
+ * into account.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+void gpiod_set_array_cansleep(unsigned int array_size,
+ struct gpio_desc **desc_array,
+ int *value_array)
+{
+ might_sleep_if(extra_checks);
+ if (!desc_array)
+ return;
+ gpiod_set_array_priv(false, true, array_size, desc_array, value_array);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_array_cansleep);
+
+/**
+ * gpiod_add_lookup_table() - register GPIO device consumers
+ * @table: table of consumers to register
+ */
+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);
+}
+
+static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
+ unsigned int idx,
+ enum gpio_lookup_flags *flags)
+{
+ char prop_name[32]; /* 32 is max size of property name */
+ enum of_gpio_flags of_flags;
+ struct gpio_desc *desc;
+ 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 = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
+ &of_flags);
+ if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
+ break;
+ }
+
+ if (IS_ERR(desc))
+ return desc;
+
+ if (of_flags & OF_GPIO_ACTIVE_LOW)
+ *flags |= GPIO_ACTIVE_LOW;
+
+ return desc;
+}
+
+static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
+ unsigned int idx,
+ enum gpio_lookup_flags *flags)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct acpi_gpio_info info;
+ struct gpio_desc *desc;
+ char propname[32];
+ int i;
+
+ /* Try first from _DSD */
+ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
+ if (con_id && strcmp(con_id, "gpios")) {
+ snprintf(propname, sizeof(propname), "%s-%s",
+ con_id, gpio_suffixes[i]);
+ } else {
+ snprintf(propname, sizeof(propname), "%s",
+ gpio_suffixes[i]);
+ }
+
+ desc = acpi_get_gpiod_by_index(adev, propname, idx, &info);
+ if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
+ break;
+ }
+
+ /* Then from plain _CRS GPIOs */
+ if (IS_ERR(desc)) {
+ desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
+ if (IS_ERR(desc))
+ return desc;
+ }
+
+ if (info.active_low)
+ *flags |= GPIO_ACTIVE_LOW;
+
+ return desc;
+}
+
+static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev)
+{
+ const char *dev_id = dev ? dev_name(dev) : NULL;
+ struct gpiod_lookup_table *table;
+
+ mutex_lock(&gpio_lookup_lock);
+
+ list_for_each_entry(table, &gpio_lookup_list, list) {
+ if (table->dev_id && dev_id) {
+ /*
+ * Valid strings on both ends, must be identical to have
+ * a match
+ */
+ if (!strcmp(table->dev_id, dev_id))
+ goto found;
+ } else {
+ /*
+ * One of the pointers is NULL, so both must be to have
+ * a match
+ */
+ if (dev_id == table->dev_id)
+ goto found;
+ }
+ }
+ table = NULL;
+
+found:
+ mutex_unlock(&gpio_lookup_lock);
+ return table;
+}
+
+static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
+ unsigned int idx,
+ enum gpio_lookup_flags *flags)
+{
+ struct gpio_desc *desc = ERR_PTR(-ENOENT);
+ struct gpiod_lookup_table *table;
+ struct gpiod_lookup *p;
+
+ table = gpiod_find_lookup_table(dev);
+ if (!table)
+ return desc;
+
+ for (p = &table->table[0]; p->chip_label; p++) {
+ struct gpio_chip *chip;
+
+ /* idx must always match exactly */
+ if (p->idx != idx)
+ continue;
+
+ /* If the lookup entry has a con_id, require exact match */
+ if (p->con_id && (!con_id || strcmp(p->con_id, con_id)))
+ continue;
+
+ chip = find_chip_by_name(p->chip_label);
+
+ if (!chip) {
+ dev_err(dev, "cannot find GPIO chip %s\n",
+ p->chip_label);
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (chip->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);
+ return ERR_PTR(-EINVAL);
+ }
+
+ desc = gpiochip_get_desc(chip, p->chip_hwnum);
+ *flags = p->flags;
+
+ return desc;
+ }
+
+ 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;
+}
+
+static int platform_gpio_count(struct device *dev, const char *con_id)
+{
+ struct gpiod_lookup_table *table;
+ struct gpiod_lookup *p;
+ unsigned int count = 0;
+
+ table = gpiod_find_lookup_table(dev);
+ if (!table)
+ return -ENOENT;
+
+ for (p = &table->table[0]; p->chip_label; p++) {
+ if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) ||
+ (!con_id && !p->con_id))
+ count++;
+ }
+ if (!count)
+ return -ENOENT;
+
+ return count;
+}
+
+/**
+ * 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
+ * @con_id: function within the GPIO consumer
+ */
+int gpiod_count(struct device *dev, const char *con_id)
+{
+ 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))
+ count = acpi_gpio_count(dev, con_id);
+
+ if (count < 0)
+ count = platform_gpio_count(dev, con_id);
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(gpiod_count);
+
+/**
+ * gpiod_get - obtain a GPIO for a given GPIO function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Return the GPIO descriptor corresponding to the function con_id of device
+ * dev, -ENOENT if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occured while trying to acquire the GPIO.
+ */
+struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id,
+ enum gpiod_flags flags)
+{
+ return gpiod_get_index(dev, con_id, 0, flags);
+}
+EXPORT_SYMBOL_GPL(__gpiod_get);
+
+/**
+ * gpiod_get_optional - obtain an optional GPIO for a given GPIO function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * This is equivalent to gpiod_get(), except that when no GPIO was assigned to
+ * the requested function it will return NULL. This is convenient for drivers
+ * that need to handle optional GPIOs.
+ */
+struct gpio_desc *__must_check __gpiod_get_optional(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ return gpiod_get_index_optional(dev, con_id, 0, flags);
+}
+EXPORT_SYMBOL_GPL(__gpiod_get_optional);
+
+
+/**
+ * gpiod_configure_flags - helper function to configure a given GPIO
+ * @desc: gpio whose value will be assigned
+ * @con_id: function within the GPIO consumer
+ * @lflags: gpio_lookup_flags - returned from of_find_gpio() or
+ * of_get_gpio_hog()
+ * @dflags: gpiod_flags - optional GPIO initialization flags
+ *
+ * Return 0 on success, -ENOENT if no GPIO has been assigned to the
+ * requested function and/or index, or another IS_ERR() code if an error
+ * occurred while trying to acquire the GPIO.
+ */
+static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
+ unsigned long lflags, enum gpiod_flags dflags)
+{
+ int status;
+
+ if (lflags & GPIO_ACTIVE_LOW)
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ if (lflags & GPIO_OPEN_DRAIN)
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ if (lflags & GPIO_OPEN_SOURCE)
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+
+ /* No particular flag request, return here... */
+ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
+ pr_debug("no flags found for %s\n", con_id);
+ return 0;
+ }
+
+ /* Process flags */
+ if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)
+ status = gpiod_direction_output(desc,
+ dflags & GPIOD_FLAGS_BIT_DIR_VAL);
+ else
+ status = gpiod_direction_input(desc);
+
+ return status;
+}
+
+/**
+ * gpiod_get_index - obtain a GPIO from a multi-index GPIO function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @idx: index of the GPIO to obtain in the consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * This variant of gpiod_get() allows to access GPIOs other than the first
+ * defined one for functions that define several GPIOs.
+ *
+ * Return a valid GPIO descriptor, -ENOENT if no GPIO has been assigned to the
+ * requested function and/or index, or another IS_ERR() code if an error
+ * occured while trying to acquire the GPIO.
+ */
+struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
+ const char *con_id,
+ unsigned int idx,
+ enum gpiod_flags flags)
+{
+ struct gpio_desc *desc = NULL;
+ int status;
+ enum gpio_lookup_flags lookupflags = 0;
+
+ 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, &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)) {
+ dev_dbg(dev, "using lookup tables for GPIO lookup\n");
+ desc = gpiod_find(dev, con_id, idx, &lookupflags);
+ }
+
+ if (IS_ERR(desc)) {
+ dev_dbg(dev, "lookup for GPIO %s failed\n", con_id);
+ return desc;
+ }
+
+ status = gpiod_request(desc, con_id);
+ if (status < 0)
+ 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);
+
+/**
+ * fwnode_get_named_gpiod - obtain a GPIO from firmware node
+ * @fwnode: handle of the firmware node
+ * @propname: name of the firmware property representing the GPIO
+ *
+ * This function can be used for drivers that get their configuration
+ * from firmware.
+ *
+ * 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.
+ *
+ * In case of error an ERR_PTR() is returned.
+ */
+struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
+ const char *propname)
+{
+ struct gpio_desc *desc = ERR_PTR(-ENODEV);
+ bool active_low = false;
+ int ret;
+
+ if (!fwnode)
+ return ERR_PTR(-EINVAL);
+
+ if (is_of_node(fwnode)) {
+ enum of_gpio_flags flags;
+
+ desc = of_get_named_gpiod_flags(of_node(fwnode), propname, 0,
+ &flags);
+ if (!IS_ERR(desc))
+ active_low = flags & OF_GPIO_ACTIVE_LOW;
+ } else if (is_acpi_node(fwnode)) {
+ struct acpi_gpio_info info;
+
+ desc = acpi_get_gpiod_by_index(acpi_node(fwnode), propname, 0,
+ &info);
+ if (!IS_ERR(desc))
+ active_low = info.active_low;
+ }
+
+ if (IS_ERR(desc))
+ return desc;
+
+ ret = gpiod_request(desc, NULL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Only value flag can be set from both DT and ACPI is active_low */
+ if (active_low)
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+
+ return desc;
+}
+EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
+
+/**
+ * gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO
+ * function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @index: index of the GPIO to obtain in the consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * This is equivalent to gpiod_get_index(), except that when no GPIO with the
+ * specified index was assigned to the requested function it will return NULL.
+ * This is convenient for drivers that need to handle optional GPIOs.
+ */
+struct gpio_desc *__must_check __gpiod_get_index_optional(struct device *dev,
+ const char *con_id,
+ unsigned int index,
+ enum gpiod_flags flags)
+{
+ struct gpio_desc *desc;
+
+ desc = gpiod_get_index(dev, con_id, index, flags);
+ if (IS_ERR(desc)) {
+ if (PTR_ERR(desc) == -ENOENT)
+ return NULL;
+ }
+
+ return desc;
+}
+EXPORT_SYMBOL_GPL(__gpiod_get_index_optional);
+
+/**
+ * gpiod_hog - Hog the specified GPIO desc given the provided flags
+ * @desc: gpio whose value will be assigned
+ * @name: gpio line name
+ * @lflags: gpio_lookup_flags - returned from of_find_gpio() or
+ * of_get_gpio_hog()
+ * @dflags: gpiod_flags - optional GPIO initialization flags
+ */
+int gpiod_hog(struct gpio_desc *desc, const char *name,
+ unsigned long lflags, enum gpiod_flags dflags)
+{
+ struct gpio_chip *chip;
+ struct gpio_desc *local_desc;
+ int hwnum;
+ int status;
+
+ chip = gpiod_to_chip(desc);
+ hwnum = gpio_chip_hwgpio(desc);
+
+ local_desc = gpiochip_request_own_desc(chip, hwnum, name);
+ if (IS_ERR(local_desc)) {
+ pr_debug("requesting own GPIO %s failed\n", name);
+ return PTR_ERR(local_desc);
+ }
+
+ status = gpiod_configure_flags(desc, name, lflags, dflags);
+ if (status < 0) {
+ pr_debug("setup of GPIO %s failed\n", name);
+ gpiochip_free_own_desc(desc);
+ return status;
+ }
+
+ /* 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":"");
+
+ return 0;
+}
+
+/**
+ * gpiochip_free_hogs - Scan gpio-controller chip and release GPIO hog
+ * @chip: gpio chip to act on
+ *
+ * This is only used by of_gpiochip_remove to free hogged gpios
+ */
+static void gpiochip_free_hogs(struct gpio_chip *chip)
+{
+ int id;
+
+ for (id = 0; id < chip->ngpio; id++) {
+ if (test_bit(FLAG_IS_HOGGED, &chip->desc[id].flags))
+ gpiochip_free_own_desc(&chip->desc[id]);
+ }
+}
+
+/**
+ * gpiod_get_array - obtain multiple GPIOs from a multi-index GPIO function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * This function acquires all the GPIOs defined under a given function.
+ *
+ * Return a struct gpio_descs containing an array of descriptors, -ENOENT if
+ * no GPIO has been assigned to the requested function, or another IS_ERR()
+ * code if an error occurred while trying to acquire the GPIOs.
+ */
+struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ struct gpio_desc *desc;
+ struct gpio_descs *descs;
+ int count;
+
+ count = gpiod_count(dev, con_id);
+ if (count < 0)
+ return ERR_PTR(count);
+
+ descs = kzalloc(sizeof(*descs) + sizeof(descs->desc[0]) * count,
+ GFP_KERNEL);
+ if (!descs)
+ return ERR_PTR(-ENOMEM);
+
+ for (descs->ndescs = 0; descs->ndescs < count; ) {
+ desc = gpiod_get_index(dev, con_id, descs->ndescs, flags);
+ if (IS_ERR(desc)) {
+ gpiod_put_array(descs);
+ return ERR_CAST(desc);
+ }
+ descs->desc[descs->ndescs] = desc;
+ descs->ndescs++;
+ }
+ return descs;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_array);
+
+/**
+ * gpiod_get_array_optional - obtain multiple GPIOs from a multi-index GPIO
+ * function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * This is equivalent to gpiod_get_array(), except that when no GPIO was
+ * assigned to the requested function it will return NULL.
+ */
+struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ struct gpio_descs *descs;
+
+ descs = gpiod_get_array(dev, con_id, flags);
+ if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT))
+ return NULL;
+
+ return descs;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_array_optional);
+
+/**
+ * gpiod_put - dispose of a GPIO descriptor
+ * @desc: GPIO descriptor to dispose of
+ *
+ * No descriptor can be used after gpiod_put() has been called on it.
+ */
+void gpiod_put(struct gpio_desc *desc)
+{
+ gpiod_free(desc);
+}
+EXPORT_SYMBOL_GPL(gpiod_put);
+
+/**
+ * gpiod_put_array - dispose of multiple GPIO descriptors
+ * @descs: struct gpio_descs containing an array of descriptors
+ */
+void gpiod_put_array(struct gpio_descs *descs)
+{
+ unsigned int i;
+
+ for (i = 0; i < descs->ndescs; i++)
+ gpiod_put(descs->desc[i]);
+
+ kfree(descs);
+}
+EXPORT_SYMBOL_GPL(gpiod_put_array);
+
+#ifdef CONFIG_DEBUG_FS
+
+static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ unsigned i;
+ unsigned gpio = chip->base;
+ struct gpio_desc *gdesc = &chip->desc[0];
+ int is_out;
+ int is_irq;
+
+ for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
+ if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
+ continue;
+
+ gpiod_get_direction(gdesc);
+ is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
+ is_irq = test_bit(FLAG_USED_AS_IRQ, &gdesc->flags);
+ seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s",
+ gpio, gdesc->label,
+ is_out ? "out" : "in ",
+ chip->get
+ ? (chip->get(chip, i) ? "hi" : "lo")
+ : "? ",
+ is_irq ? "IRQ" : " ");
+ seq_printf(s, "\n");
+ }
+}
+
+static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
+{
+ unsigned long flags;
+ struct gpio_chip *chip = NULL;
+ loff_t index = *pos;
+
+ s->private = "";
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ list_for_each_entry(chip, &gpio_chips, list)
+ if (index-- == 0) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return chip;
+ }
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return NULL;
+}
+
+static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ unsigned long flags;
+ struct gpio_chip *chip = v;
+ void *ret = NULL;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ if (list_is_last(&chip->list, &gpio_chips))
+ ret = NULL;
+ else
+ ret = list_entry(chip->list.next, struct gpio_chip, list);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ s->private = "\n";
+ ++*pos;
+
+ return ret;
+}
+
+static void gpiolib_seq_stop(struct seq_file *s, void *v)
+{
+}
+
+static int gpiolib_seq_show(struct seq_file *s, void *v)
+{
+ struct gpio_chip *chip = v;
+ struct device *dev;
+
+ seq_printf(s, "%sGPIOs %d-%d", (char *)s->private,
+ chip->base, chip->base + chip->ngpio - 1);
+ dev = chip->dev;
+ if (dev)
+ seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus",
+ dev_name(dev));
+ if (chip->label)
+ seq_printf(s, ", %s", chip->label);
+ if (chip->can_sleep)
+ seq_printf(s, ", can sleep");
+ seq_printf(s, ":\n");
+
+ if (chip->dbg_show)
+ chip->dbg_show(s, chip);
+ else
+ gpiolib_dbg_show(s, chip);
+
+ return 0;
+}
+
+static const struct seq_operations gpiolib_seq_ops = {
+ .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,
+};
+
+static int __init gpiolib_debugfs_init(void)
+{
+ /* /sys/kernel/debug/gpio */
+ (void) debugfs_create_file("gpio", S_IFREG | S_IRUGO,
+ NULL, NULL, &gpiolib_operations);
+ return 0;
+}
+subsys_initcall(gpiolib_debugfs_init);
+
+#endif /* DEBUG_FS */
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
new file mode 100644
index 000000000..594b1798c
--- /dev/null
+++ b/drivers/gpio/gpiolib.h
@@ -0,0 +1,170 @@
+/*
+ * Internal GPIO functions.
+ *
+ * Copyright (C) 2013, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.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.
+ */
+
+#ifndef GPIOLIB_H
+#define GPIOLIB_H
+
+#include <linux/err.h>
+#include <linux/device.h>
+
+enum of_gpio_flags;
+
+struct acpi_device;
+
+/**
+ * struct acpi_gpio_info - ACPI GPIO specific information
+ * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
+ * @active_low: in case of @gpioint, the pin is active low
+ */
+struct acpi_gpio_info {
+ bool gpioint;
+ bool active_low;
+};
+
+/* gpio suffixes used for ACPI and device tree lookup */
+static const char * const gpio_suffixes[] = { "gpios", "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);
+
+struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
+ 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_gpiochip_request_interrupts(struct gpio_chip *chip) { }
+
+static inline void
+acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
+
+static inline struct gpio_desc *
+acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname,
+ int index, struct acpi_gpio_info *info)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline int acpi_gpio_count(struct device *dev, const char *con_id)
+{
+ return -ENODEV;
+}
+#endif
+
+struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
+ const char *list_name, int index, enum of_gpio_flags *flags);
+
+struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
+
+extern struct spinlock gpio_lock;
+extern struct list_head gpio_chips;
+
+struct gpio_desc {
+ struct gpio_chip *chip;
+ unsigned long flags;
+/* flag symbols are bit numbers */
+#define FLAG_REQUESTED 0
+#define FLAG_IS_OUT 1
+#define FLAG_EXPORT 2 /* protected by sysfs_lock */
+#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
+#define FLAG_TRIG_FALL 4 /* trigger on falling edge */
+#define FLAG_TRIG_RISE 5 /* trigger on rising edge */
+#define FLAG_ACTIVE_LOW 6 /* value has active low */
+#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
+#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
+#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
+#define FLAG_SYSFS_DIR 10 /* show sysfs direction attribute */
+#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
+
+#define ID_SHIFT 16 /* add new flags before this one */
+
+#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
+#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
+
+ const char *label;
+};
+
+int gpiod_request(struct gpio_desc *desc, const char *label);
+void gpiod_free(struct gpio_desc *desc);
+int gpiod_hog(struct gpio_desc *desc, const char *name,
+ unsigned long lflags, enum gpiod_flags dflags);
+
+/*
+ * Return the GPIO number of the passed descriptor relative to its chip
+ */
+static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
+{
+ return desc - &desc->chip->desc[0];
+}
+
+/* With descriptor prefix */
+
+#define gpiod_emerg(desc, fmt, ...) \
+ pr_emerg("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\
+ ##__VA_ARGS__)
+#define gpiod_crit(desc, fmt, ...) \
+ pr_crit("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
+ ##__VA_ARGS__)
+#define gpiod_err(desc, fmt, ...) \
+ pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
+ ##__VA_ARGS__)
+#define gpiod_warn(desc, fmt, ...) \
+ pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
+ ##__VA_ARGS__)
+#define gpiod_info(desc, fmt, ...) \
+ pr_info("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
+ ##__VA_ARGS__)
+#define gpiod_dbg(desc, fmt, ...) \
+ pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\
+ ##__VA_ARGS__)
+
+/* With chip prefix */
+
+#define chip_emerg(chip, fmt, ...) \
+ pr_emerg("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+#define chip_crit(chip, fmt, ...) \
+ pr_crit("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+#define chip_err(chip, fmt, ...) \
+ pr_err("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+#define chip_warn(chip, fmt, ...) \
+ pr_warn("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+#define chip_info(chip, fmt, ...) \
+ pr_info("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+#define chip_dbg(chip, fmt, ...) \
+ pr_debug("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+
+#ifdef CONFIG_GPIO_SYSFS
+
+int gpiochip_export(struct gpio_chip *chip);
+void gpiochip_unexport(struct gpio_chip *chip);
+
+#else
+
+static inline int gpiochip_export(struct gpio_chip *chip)
+{
+ return 0;
+}
+
+static inline void gpiochip_unexport(struct gpio_chip *chip)
+{
+}
+
+#endif /* CONFIG_GPIO_SYSFS */
+
+#endif /* GPIOLIB_H */