diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-04 18:34:05 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-04 18:34:05 -0700 |
commit | 37644cac6e8297d0908aef054caabb439c467c7d (patch) | |
tree | a17d3eec2bc18d3ad9d40165936dc9971387b6d0 | |
parent | 5f0848190c6dd0f5b8a2aaf0f1d900a96d96bee0 (diff) | |
parent | c4f0d16daa6d1c5d862d063379c03310387095d5 (diff) | |
download | lwn-37644cac6e8297d0908aef054caabb439c467c7d.tar.gz lwn-37644cac6e8297d0908aef054caabb439c467c7d.zip |
Merge tag 'gpio-updates-for-v6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski:
"Here are the updates for this merge window from the GPIO subsystem.
We have more lines removed than added thanks to dropping of a driver
for a platform that's no longer supported. Otherwise the changes are
pretty straightforward: support for some new models, various
improvements to existing drivers, some tweaks to the core library code
and DT bindings updates.
Summary:
- remove gpio-vr41xx driver as the only platform using it got dropped
too
- add support for suspend/resume to gpio-davinci
- improvements to the GPIO character device code
- add support for disabling bias for in-kernel users (up until now
only user-space could set it)
- drop unused devm_gpio_free()
- fix a refcount issue in gpiolib OF
- use device match helpers where applicable
- add support for a new model to gpio-rockchip
- non-functional improvements in gpio-adp5588
- improve and simplify teardown in gpio-twl4030 and gpio-ucb1400
- modernize the gpio-74xx-mmio and gpio-adnp drivers
- coding style improvements in gpio-xilinx, gpio-104-idi-48
- support new model (pca9571) in gpio-pca9570
- convert the DT bindings to YAML for gpio-mvebu and update the
document
- don't return error codes from remove() in gpio-brcmstb
- add a library for the intel 8255 PPI interface and use it in
drivers
- reduce using magic numbers and improve code readability in several
drivers
- convert DT bindings to YAML for gpio-tpic2810
- add new models to DT bindings for gpio-frl-imx
- Kconfig improvements
- other minor tweaks and improvements"
* tag 'gpio-updates-for-v6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (52 commits)
dt-bindings: gpio: fsl-imx-gpio: Add i.MXRT compatibles
gpio: 74xx-mmio: Use bits instead of plain numbers for flags
gpio: xilinx: add missing blank line after declarations
MAINTAINERS: Update Intel 8255 GPIO driver file list
gpio: gpio-mm: Implement and utilize register structures
gpio: 104-idi-48: Implement and utilize register structures
gpio: 104-dio-48e: Implement and utilize register structures
gpio: i8255: Introduce the Intel 8255 interface library module
gpio: 104-idio-16: Implement and utilize register structures
gpio: ws16c48: Implement and utilize register structures
gpio: remove VR41XX related gpio driver
dt-bindings: gpio: add pull-disable flag
gpiolib: acpi: support bias pull disable
gpiolib: of: support bias pull disable
gpiolib: add support for bias pull disable
gpio: 74xx-mmio: use bits.h macros for all masks
gpio: 74xx-mmio: Check MMIO_74XX_DIR_IN flag in mmio_74xx_dir_in()
gpio: 74xx-mmio: Make use of device properties
gpiolib: cdev: compile out HTE unless CONFIG_HTE selected
gpiolib: cdev: consolidate edge detector configuration flags
...
47 files changed, 1197 insertions, 1452 deletions
diff --git a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt index 052a967c1f28..c83245065d44 100644 --- a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt @@ -72,7 +72,7 @@ mpp19 19 gpio, uart0(rxd), sdio(pw_off) GPIO: ----- For common binding part and usage, refer to -Documentation/devicetree/bindings/gpio/gpio-mvebu.txt. +Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml. Required properties: diff --git a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt index 0705e765f432..d84105c7c935 100644 --- a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt @@ -156,7 +156,7 @@ GPIO: ----- For common binding part and usage, refer to -Documentation/devicetree/bindings/gpio/gpio-mvebu.txt. +Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml. Required properties: diff --git a/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml index f57d22d1ebd6..ae18603697d7 100644 --- a/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml @@ -37,6 +37,8 @@ properties: - fsl,imx8mp-gpio - fsl,imx8mq-gpio - fsl,imx8qxp-gpio + - fsl,imxrt1050-gpio + - fsl,imxrt1170-gpio - const: fsl,imx35-gpio reg: diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt b/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt deleted file mode 100644 index 0fc6700ed800..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt +++ /dev/null @@ -1,93 +0,0 @@ -* Marvell EBU GPIO controller - -Required properties: - -- compatible : Should be "marvell,orion-gpio", "marvell,mv78200-gpio", - "marvell,armadaxp-gpio" or "marvell,armada-8k-gpio". - - "marvell,orion-gpio" should be used for Orion, Kirkwood, Dove, - Discovery (except MV78200) and Armada 370. "marvell,mv78200-gpio" - should be used for the Discovery MV78200. - - "marvel,armadaxp-gpio" should be used for all Armada XP SoCs - (MV78230, MV78260, MV78460). - - "marvell,armada-8k-gpio" should be used for the Armada 7K and 8K - SoCs (either from AP or CP), see - Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt - for specific details about the offset property. - -- reg: Address and length of the register set for the device. Only one - entry is expected, except for the "marvell,armadaxp-gpio" variant - for which two entries are expected: one for the general registers, - one for the per-cpu registers. Not used for marvell,armada-8k-gpio. - -- interrupts: The list of interrupts that are used for all the pins - managed by this GPIO bank. There can be more than one interrupt - (example: 1 interrupt per 8 pins on Armada XP, which means 4 - interrupts per bank of 32 GPIOs). - -- interrupt-controller: identifies the node as an interrupt controller - -- #interrupt-cells: specifies the number of cells needed to encode an - interrupt source. Should be two. - The first cell is the GPIO number. - The second cell is used to specify flags: - bits[3:0] trigger type and level flags: - 1 = low-to-high edge triggered. - 2 = high-to-low edge triggered. - 4 = active high level-sensitive. - 8 = active low level-sensitive. - -- gpio-controller: marks the device node as a gpio controller - -- ngpios: number of GPIOs this controller has - -- #gpio-cells: Should be two. The first cell is the pin number. The - second cell is reserved for flags, unused at the moment. - -Optional properties: - -In order to use the GPIO lines in PWM mode, some additional optional -properties are required. - -- compatible: Must contain "marvell,armada-370-gpio" - -- reg: an additional register set is needed, for the GPIO Blink - Counter on/off registers. - -- reg-names: Must contain an entry "pwm" corresponding to the - additional register range needed for PWM operation. - -- #pwm-cells: Should be two. The first cell is the GPIO line number. The - second cell is the period in nanoseconds. - -- clocks: Must be a phandle to the clock for the GPIO controller. - -Example: - - gpio0: gpio@d0018100 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018100 0x40>, - <0xd0018800 0x30>; - ngpios = <32>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <16>, <17>, <18>, <19>; - }; - - gpio1: gpio@18140 { - compatible = "marvell,armada-370-gpio"; - reg = <0x18140 0x40>, <0x181c8 0x08>; - reg-names = "gpio", "pwm"; - ngpios = <17>; - gpio-controller; - #gpio-cells = <2>; - #pwm-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <87>, <88>, <89>; - clocks = <&coreclk 0>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml new file mode 100644 index 000000000000..f1bd1e6b2e1f --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml @@ -0,0 +1,146 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-mvebu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell EBU GPIO controller + +maintainers: + - Thomas Petazzoni <thomas.petazzoni@free-electrons.com> + - Andrew Lunn <andrew@lunn.ch> + +properties: + compatible: + oneOf: + - enum: + - marvell,armada-8k-gpio + - marvell,orion-gpio + + - items: + - enum: + - marvell,mv78200-gpio + - marvell,armada-370-gpio + - const: marvell,orion-gpio + + - description: Deprecated binding + items: + - const: marvell,armadaxp-gpio + - const: marvell,orion-gpio + deprecated: true + + reg: + description: | + Address and length of the register set for the device. Not used for + marvell,armada-8k-gpio. + + A second entry can be provided, for the PWM function using the GPIO Blink + Counter on/off registers. + minItems: 1 + maxItems: 2 + + reg-names: + items: + - const: gpio + - const: pwm + minItems: 1 + + offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Offset in the register map for the gpio registers (in bytes) + + interrupts: + description: | + The list of interrupts that are used for all the pins managed by this + GPIO bank. There can be more than one interrupt (example: 1 interrupt + per 8 pins on Armada XP, which means 4 interrupts per bank of 32 + GPIOs). + minItems: 1 + maxItems: 4 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + gpio-controller: true + + ngpios: + minimum: 1 + maximum: 32 + + "#gpio-cells": + const: 2 + + marvell,pwm-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Offset in the register map for the pwm registers (in bytes) + + "#pwm-cells": + description: + The first cell is the GPIO line number. The second cell is the period + in nanoseconds. + const: 2 + + clocks: + description: + Clock(s) used for PWM function. + items: + - description: Core clock + - description: AXI bus clock + minItems: 1 + + clock-names: + items: + - const: core + - const: axi + minItems: 1 + +required: + - compatible + - gpio-controller + - ngpios + - "#gpio-cells" + +allOf: + - if: + properties: + compatible: + contains: + const: marvell,armada-8k-gpio + then: + required: + - offset + else: + required: + - reg + +unevaluatedProperties: true + +examples: + - | + gpio@d0018100 { + compatible = "marvell,armadaxp-gpio", "marvell,orion-gpio"; + reg = <0xd0018100 0x40>, <0xd0018800 0x30>; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <16>, <17>, <18>, <19>; + }; + + - | + gpio@18140 { + compatible = "marvell,armada-370-gpio", "marvell,orion-gpio"; + reg = <0x18140 0x40>, <0x181c8 0x08>; + reg-names = "gpio", "pwm"; + ngpios = <17>; + gpio-controller; + #gpio-cells = <2>; + #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <87>, <88>, <89>; + clocks = <&coreclk 0>; + }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml index 338c5312a106..1acaa0a3d35a 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml @@ -13,6 +13,7 @@ properties: compatible: enum: - nxp,pca9570 + - nxp,pca9571 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt deleted file mode 100644 index 1afc2de7a537..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt +++ /dev/null @@ -1,16 +0,0 @@ -TPIC2810 GPIO controller bindings - -Required properties: - - compatible : Should be "ti,tpic2810". - - reg : The I2C address of the device - - gpio-controller : Marks the device node as a GPIO controller. - - #gpio-cells : Should be two. For consumer use see gpio.txt. - -Example: - - gpio@60 { - compatible = "ti,tpic2810"; - reg = <0x60>; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml new file mode 100644 index 000000000000..cb8a5c376e1e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-tpic2810.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TPIC2810 GPIO controller bindings + +maintainers: + - Aswath Govindraju <a-govindraju@ti.com> + +properties: + compatible: + enum: + - ti,tpic2810 + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio-line-names: + minItems: 1 + maxItems: 32 + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + gpio@60 { + compatible = "ti,tpic2810"; + reg = <0x60>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "LED A", "LED B", "LED C"; + }; + }; diff --git a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml index 0681a4790cd6..75e5da6a7cc0 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml @@ -49,10 +49,8 @@ properties: - const: renesas,rcar-gen3-gpio # R-Car Gen3 or RZ/G2 - items: - - const: renesas,gpio-r8a779a0 # R-Car V3U - - - items: - enum: + - renesas,gpio-r8a779a0 # R-Car V3U - renesas,gpio-r8a779f0 # R-Car S4-8 - const: renesas,rcar-gen4-gpio # R-Car Gen4 diff --git a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml index d4e42c2b995b..affd823c881d 100644 --- a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml +++ b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml @@ -27,6 +27,8 @@ properties: - description: APB interface clock source - description: GPIO debounce reference clock source + gpio-ranges: true + gpio-controller: true gpio-line-names: true diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 2d39967bafcc..55272942e721 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -277,7 +277,6 @@ GPIO devm_gpiochip_add_data() devm_gpio_request() devm_gpio_request_one() - devm_gpio_free() I2C devm_i2c_new_dummy_device() diff --git a/MAINTAINERS b/MAINTAINERS index b169646de59a..05ebeb5b9495 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10045,6 +10045,13 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/fbdev/i810/ +INTEL 8255 GPIO DRIVER +M: William Breathitt Gray <william.gray@linaro.org> +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-i8255.c +F: drivers/gpio/gpio-i8255.h + INTEL ASoC DRIVERS M: Cezary Rojewski <cezary.rojewski@intel.com> M: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> @@ -16503,7 +16510,7 @@ L: linux-pwm@vger.kernel.org S: Maintained Q: https://patchwork.ozlabs.org/project/linux-pwm/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git -F: Documentation/devicetree/bindings/gpio/gpio-mvebu.txt +F: Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml F: Documentation/devicetree/bindings/pwm/ F: Documentation/driver-api/pwm.rst F: drivers/gpio/gpio-mvebu.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b01961999ced..0642f579196f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -544,6 +544,7 @@ config GPIO_SAMA5D2_PIOBU tristate "SAMA5D2 PIOBU GPIO support" depends on MFD_SYSCON depends on OF_GPIO + depends on ARCH_AT91 || COMPILE_TEST select GPIO_SYSCON help Say yes here to use the PIOBU pins as GPIOs. @@ -690,12 +691,6 @@ config GPIO_VISCONTI help Say yes here to support GPIO on Tohisba Visconti. -config GPIO_VR41XX - tristate "NEC VR4100 series General-purpose I/O Unit support" - depends on CPU_VR41XX - help - Say yes here to support the NEC VR4100 series General-purpose I/O Unit. - config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" depends on (X86 || COMPILE_TEST) && PCI @@ -829,11 +824,24 @@ endmenu menu "Port-mapped I/O GPIO drivers" depends on X86 # Unconditional I/O space access +config GPIO_I8255 + tristate + help + Enables support for the i8255 interface library functions. The i8255 + interface library provides functions to facilitate communication with + interfaces compatible with the venerable Intel 8255 Programmable + Peripheral Interface (PPI). The Intel 8255 PPI chip was first released + in the early 1970s but compatible interfaces are nowadays typically + found embedded in larger VLSI processing chips and FPGA components. + + If built as a module its name will be gpio-i8255. + config GPIO_104_DIO_48E tristate "ACCES 104-DIO-48E GPIO support" depends on PC104 select ISA_BUS_API select GPIOLIB_IRQCHIP + select GPIO_I8255 help Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E, 104-DIO-24E). The base port addresses for the devices may be @@ -857,6 +865,7 @@ config GPIO_104_IDI_48 depends on PC104 select ISA_BUS_API select GPIOLIB_IRQCHIP + select GPIO_I8255 help Enables GPIO support for the ACCES 104-IDI-48 family (104-IDI-48A, 104-IDI-48AC, 104-IDI-48B, 104-IDI-48BC). The base port addresses for @@ -877,6 +886,7 @@ config GPIO_GPIO_MM tristate "Diamond Systems GPIO-MM GPIO support" depends on PC104 select ISA_BUS_API + select GPIO_I8255 help Enables GPIO support for the Diamond Systems GPIO-MM and GPIO-MM-12. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 14352f6dfe8e..a0985d30f51b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o +obj-$(CONFIG_GPIO_I8255) += gpio-i8255.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o @@ -169,7 +170,6 @@ obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o -obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index f118ad9bcd33..a41551870759 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -6,8 +6,7 @@ * This driver supports the following ACCES devices: 104-DIO-48E and * 104-DIO-24E. */ -#include <linux/bitmap.h> -#include <linux/bitops.h> +#include <linux/bits.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/gpio/driver.h> @@ -20,6 +19,11 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/spinlock.h> +#include <linux/types.h> + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define DIO48E_EXTENT 16 #define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT) @@ -33,34 +37,54 @@ static unsigned int irq[MAX_NUM_DIO48E]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers"); +#define DIO48E_NUM_PPI 2 + +/** + * struct dio48e_reg - device register structure + * @ppi: Programmable Peripheral Interface groups + * @enable_buffer: Enable/Disable Buffer groups + * @unused1: Unused + * @enable_interrupt: Write: Enable Interrupt + * Read: Disable Interrupt + * @unused2: Unused + * @enable_counter: Write: Enable Counter/Timer Addressing + * Read: Disable Counter/Timer Addressing + * @unused3: Unused + * @clear_interrupt: Clear Interrupt + */ +struct dio48e_reg { + struct i8255 ppi[DIO48E_NUM_PPI]; + u8 enable_buffer[DIO48E_NUM_PPI]; + u8 unused1; + u8 enable_interrupt; + u8 unused2; + u8 enable_counter; + u8 unused3; + u8 clear_interrupt; +}; + /** * struct dio48e_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @io_state: bit I/O state (whether bit is set to input or output) - * @out_state: output bits state - * @control: Control registers state - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device - * @irq_mask: I/O bits affected by interrupts + * @chip: instance of the gpio_chip + * @ppi_state: PPI device states + * @lock: synchronization lock to prevent I/O race conditions + * @reg: I/O address offset for the device registers + * @irq_mask: I/O bits affected by interrupts */ struct dio48e_gpio { struct gpio_chip chip; - unsigned char io_state[6]; - unsigned char out_state[6]; - unsigned char control[2]; + struct i8255_state ppi_state[DIO48E_NUM_PPI]; raw_spinlock_t lock; - void __iomem *base; + struct dio48e_reg __iomem *reg; unsigned char irq_mask; }; static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - if (dio48egpio->io_state[port] & mask) - return GPIO_LINE_DIRECTION_IN; + if (i8255_get_direction(dio48egpio->ppi_state, offset)) + return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; } @@ -68,38 +92,9 @@ static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned int offset static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - void __iomem *const control_addr = dio48egpio->base + 3 + control_port * 4; - unsigned long flags; - unsigned int control; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - dio48egpio->io_state[io_port] |= 0xF0; - dio48egpio->control[control_port] |= BIT(3); - } else { - dio48egpio->io_state[io_port] |= 0x0F; - dio48egpio->control[control_port] |= BIT(0); - } - } else { - dio48egpio->io_state[io_port] |= 0xFF; - if (io_port == 0 || io_port == 3) - dio48egpio->control[control_port] |= BIT(4); - else - dio48egpio->control[control_port] |= BIT(1); - } - - control = BIT(7) | dio48egpio->control[control_port]; - iowrite8(control, control_addr); - control &= ~BIT(7); - iowrite8(control, control_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_direction_input(dio48egpio->reg->ppi, dio48egpio->ppi_state, + offset); return 0; } @@ -108,48 +103,9 @@ static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int off int value) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - const unsigned int mask = BIT(offset % 8); - void __iomem *const control_addr = dio48egpio->base + 3 + control_port * 4; - const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; - unsigned long flags; - unsigned int control; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - dio48egpio->io_state[io_port] &= 0x0F; - dio48egpio->control[control_port] &= ~BIT(3); - } else { - dio48egpio->io_state[io_port] &= 0xF0; - dio48egpio->control[control_port] &= ~BIT(0); - } - } else { - dio48egpio->io_state[io_port] &= 0x00; - if (io_port == 0 || io_port == 3) - dio48egpio->control[control_port] &= ~BIT(4); - else - dio48egpio->control[control_port] &= ~BIT(1); - } - - if (value) - dio48egpio->out_state[io_port] |= mask; - else - dio48egpio->out_state[io_port] &= ~mask; - control = BIT(7) | dio48egpio->control[control_port]; - iowrite8(control, control_addr); - - iowrite8(dio48egpio->out_state[io_port], dio48egpio->base + out_port); - - control &= ~BIT(7); - iowrite8(control, control_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_direction_output(dio48egpio->reg->ppi, dio48egpio->ppi_state, + offset, value); return 0; } @@ -157,47 +113,16 @@ static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int off static int dio48e_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int in_port = (port > 2) ? port + 1 : port; - unsigned long flags; - unsigned int port_state; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - /* ensure that GPIO is set for input */ - if (!(dio48egpio->io_state[port] & mask)) { - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - return -EINVAL; - } - - port_state = ioread8(dio48egpio->base + in_port); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - - return !!(port_state & mask); + return i8255_get(dio48egpio->reg->ppi, offset); } -static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - void __iomem *port_addr; - unsigned long port_state; - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = dio48egpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(dio48egpio->reg->ppi, mask, bits, chip->ngpio); return 0; } @@ -205,49 +130,17 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, static void dio48e_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (port > 2) ? port + 1 : port; - unsigned long flags; - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - if (value) - dio48egpio->out_state[port] |= mask; - else - dio48egpio->out_state[port] &= ~mask; - - iowrite8(dio48egpio->out_state[port], dio48egpio->base + out_port); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_set(dio48egpio->reg->ppi, dio48egpio->ppi_state, offset, value); } static void dio48e_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - size_t index; - void __iomem *port_addr; - unsigned long bitmask; - unsigned long flags; - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - index = offset / 8; - port_addr = dio48egpio->base + ports[index]; - - bitmask = bitmap_get_value8(bits, offset) & gpio_mask; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - /* update output state data and set device gpio register */ - dio48egpio->out_state[index] &= ~gpio_mask; - dio48egpio->out_state[index] |= bitmask; - iowrite8(dio48egpio->out_state[index], port_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - } + i8255_set_multiple(dio48egpio->reg->ppi, dio48egpio->ppi_state, mask, + bits, chip->ngpio); } static void dio48e_irq_ack(struct irq_data *data) @@ -274,7 +167,7 @@ static void dio48e_irq_mask(struct irq_data *data) if (!dio48egpio->irq_mask) /* disable interrupts */ - ioread8(dio48egpio->base + 0xB); + ioread8(&dio48egpio->reg->enable_interrupt); raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); } @@ -294,8 +187,8 @@ static void dio48e_irq_unmask(struct irq_data *data) if (!dio48egpio->irq_mask) { /* enable interrupts */ - iowrite8(0x00, dio48egpio->base + 0xF); - iowrite8(0x00, dio48egpio->base + 0xB); + iowrite8(0x00, &dio48egpio->reg->clear_interrupt); + iowrite8(0x00, &dio48egpio->reg->enable_interrupt); } if (offset == 19) @@ -341,7 +234,7 @@ static irqreturn_t dio48e_irq_handler(int irq, void *dev_id) raw_spin_lock(&dio48egpio->lock); - iowrite8(0x00, dio48egpio->base + 0xF); + iowrite8(0x00, &dio48egpio->reg->clear_interrupt); raw_spin_unlock(&dio48egpio->lock); @@ -373,11 +266,26 @@ static int dio48e_irq_init_hw(struct gpio_chip *gc) struct dio48e_gpio *const dio48egpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - ioread8(dio48egpio->base + 0xB); + ioread8(&dio48egpio->reg->enable_interrupt); return 0; } +static void dio48e_init_ppi(struct i8255 __iomem *const ppi, + struct i8255_state *const ppi_state) +{ + const unsigned long ngpio = 24; + const unsigned long mask = GENMASK(ngpio - 1, 0); + const unsigned long bits = 0; + unsigned long i; + + /* Initialize all GPIO to output 0 */ + for (i = 0; i < DIO48E_NUM_PPI; i++) { + i8255_mode0_output(&ppi[i]); + i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio); + } +} + static int dio48e_probe(struct device *dev, unsigned int id) { struct dio48e_gpio *dio48egpio; @@ -395,8 +303,8 @@ static int dio48e_probe(struct device *dev, unsigned int id) return -EBUSY; } - dio48egpio->base = devm_ioport_map(dev, base[id], DIO48E_EXTENT); - if (!dio48egpio->base) + dio48egpio->reg = devm_ioport_map(dev, base[id], DIO48E_EXTENT); + if (!dio48egpio->reg) return -ENOMEM; dio48egpio->chip.label = name; @@ -425,17 +333,8 @@ static int dio48e_probe(struct device *dev, unsigned int id) raw_spin_lock_init(&dio48egpio->lock); - /* initialize all GPIO as output */ - iowrite8(0x80, dio48egpio->base + 3); - iowrite8(0x00, dio48egpio->base); - iowrite8(0x00, dio48egpio->base + 1); - iowrite8(0x00, dio48egpio->base + 2); - iowrite8(0x00, dio48egpio->base + 3); - iowrite8(0x80, dio48egpio->base + 7); - iowrite8(0x00, dio48egpio->base + 4); - iowrite8(0x00, dio48egpio->base + 5); - iowrite8(0x00, dio48egpio->base + 6); - iowrite8(0x00, dio48egpio->base + 7); + i8255_state_init(dio48egpio->ppi_state, DIO48E_NUM_PPI); + dio48e_init_ppi(dio48egpio->reg->ppi, dio48egpio->ppi_state); err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio); if (err) { diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 9521ece3ebef..40be76efeed7 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -6,8 +6,7 @@ * This driver supports the following ACCES devices: 104-IDI-48A, * 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC. */ -#include <linux/bitmap.h> -#include <linux/bitops.h> +#include <linux/bits.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/gpio/driver.h> @@ -20,6 +19,11 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/spinlock.h> +#include <linux/types.h> + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define IDI_48_EXTENT 8 #define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT) @@ -34,72 +38,61 @@ module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers"); /** + * struct idi_48_reg - device register structure + * @port0: Port 0 Inputs + * @unused: Unused + * @port1: Port 1 Inputs + * @irq: Read: IRQ Status Register/IRQ Clear + * Write: IRQ Enable/Disable + */ +struct idi_48_reg { + u8 port0[3]; + u8 unused; + u8 port1[3]; + u8 irq; +}; + +/** * struct idi_48_gpio - GPIO device private data structure * @chip: instance of the gpio_chip * @lock: synchronization lock to prevent I/O race conditions - * @ack_lock: synchronization lock to prevent IRQ handler race conditions * @irq_mask: input bits affected by interrupts - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers * @cos_enb: Change-Of-State IRQ enable boundaries mask */ struct idi_48_gpio { struct gpio_chip chip; - raw_spinlock_t lock; - spinlock_t ack_lock; + spinlock_t lock; unsigned char irq_mask[6]; - void __iomem *base; + struct idi_48_reg __iomem *reg; unsigned char cos_enb; }; -static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { return GPIO_LINE_DIRECTION_IN; } -static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { return 0; } -static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - unsigned i; - static const unsigned int register_offset[6] = { 0, 1, 2, 4, 5, 6 }; - void __iomem *port_addr; - unsigned mask; - - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - port_addr = idi48gpio->base + register_offset[i / 8]; - mask = BIT(offset - i); - - return !!(ioread8(port_addr) & mask); - } + void __iomem *const ppi = idi48gpio->reg; - /* The following line should never execute since offset < 48 */ - return 0; + return i8255_get(ppi, offset); } static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - void __iomem *port_addr; - unsigned long port_state; - - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); + void __iomem *const ppi = idi48gpio->reg; - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = idi48gpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(ppi, mask, bits, chip->ngpio); return 0; } @@ -112,67 +105,56 @@ static void idi_48_irq_mask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - const unsigned offset = irqd_to_hwirq(data); - unsigned i; - unsigned mask; - unsigned boundary; + const unsigned int offset = irqd_to_hwirq(data); + const unsigned long boundary = offset / 8; + const unsigned long mask = BIT(offset % 8); unsigned long flags; - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - mask = BIT(offset - i); - boundary = i / 8; - - idi48gpio->irq_mask[boundary] &= ~mask; + spin_lock_irqsave(&idi48gpio->lock, flags); - if (!idi48gpio->irq_mask[boundary]) { - idi48gpio->cos_enb &= ~BIT(boundary); + idi48gpio->irq_mask[boundary] &= ~mask; - raw_spin_lock_irqsave(&idi48gpio->lock, flags); + /* Exit early if there are still input lines with IRQ unmasked */ + if (idi48gpio->irq_mask[boundary]) + goto exit; - iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7); + idi48gpio->cos_enb &= ~BIT(boundary); - raw_spin_unlock_irqrestore(&idi48gpio->lock, flags); - } + iowrite8(idi48gpio->cos_enb, &idi48gpio->reg->irq); - return; - } +exit: + spin_unlock_irqrestore(&idi48gpio->lock, flags); } static void idi_48_irq_unmask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - const unsigned offset = irqd_to_hwirq(data); - unsigned i; - unsigned mask; - unsigned boundary; - unsigned prev_irq_mask; + const unsigned int offset = irqd_to_hwirq(data); + const unsigned long boundary = offset / 8; + const unsigned long mask = BIT(offset % 8); + unsigned int prev_irq_mask; unsigned long flags; - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - mask = BIT(offset - i); - boundary = i / 8; - prev_irq_mask = idi48gpio->irq_mask[boundary]; + spin_lock_irqsave(&idi48gpio->lock, flags); - idi48gpio->irq_mask[boundary] |= mask; + prev_irq_mask = idi48gpio->irq_mask[boundary]; - if (!prev_irq_mask) { - idi48gpio->cos_enb |= BIT(boundary); + idi48gpio->irq_mask[boundary] |= mask; - raw_spin_lock_irqsave(&idi48gpio->lock, flags); + /* Exit early if IRQ was already unmasked for this boundary */ + if (prev_irq_mask) + goto exit; - iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7); + idi48gpio->cos_enb |= BIT(boundary); - raw_spin_unlock_irqrestore(&idi48gpio->lock, flags); - } + iowrite8(idi48gpio->cos_enb, &idi48gpio->reg->irq); - return; - } +exit: + spin_unlock_irqrestore(&idi48gpio->lock, flags); } -static int idi_48_irq_set_type(struct irq_data *data, unsigned flow_type) +static int idi_48_irq_set_type(struct irq_data *data, unsigned int flow_type) { /* The only valid irq types are none and both-edges */ if (flow_type != IRQ_TYPE_NONE && @@ -200,17 +182,13 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id) unsigned long gpio; struct gpio_chip *const chip = &idi48gpio->chip; - spin_lock(&idi48gpio->ack_lock); - - raw_spin_lock(&idi48gpio->lock); - - cos_status = ioread8(idi48gpio->base + 7); + spin_lock(&idi48gpio->lock); - raw_spin_unlock(&idi48gpio->lock); + cos_status = ioread8(&idi48gpio->reg->irq); /* IRQ Status (bit 6) is active low (0 = IRQ generated by device) */ if (cos_status & BIT(6)) { - spin_unlock(&idi48gpio->ack_lock); + spin_unlock(&idi48gpio->lock); return IRQ_NONE; } @@ -228,7 +206,7 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id) } } - spin_unlock(&idi48gpio->ack_lock); + spin_unlock(&idi48gpio->lock); return IRQ_HANDLED; } @@ -250,8 +228,8 @@ static int idi_48_irq_init_hw(struct gpio_chip *gc) struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - iowrite8(0, idi48gpio->base + 7); - ioread8(idi48gpio->base + 7); + iowrite8(0, &idi48gpio->reg->irq); + ioread8(&idi48gpio->reg->irq); return 0; } @@ -273,8 +251,8 @@ static int idi_48_probe(struct device *dev, unsigned int id) return -EBUSY; } - idi48gpio->base = devm_ioport_map(dev, base[id], IDI_48_EXTENT); - if (!idi48gpio->base) + idi48gpio->reg = devm_ioport_map(dev, base[id], IDI_48_EXTENT); + if (!idi48gpio->reg) return -ENOMEM; idi48gpio->chip.label = name; @@ -298,8 +276,7 @@ static int idi_48_probe(struct device *dev, unsigned int id) girq->handler = handle_edge_irq; girq->init_hw = idi_48_irq_init_hw; - raw_spin_lock_init(&idi48gpio->lock); - spin_lock_init(&idi48gpio->ack_lock); + spin_lock_init(&idi48gpio->lock); err = devm_gpiochip_add_data(dev, &idi48gpio->chip, idi48gpio); if (err) { diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 45f7ad8573e1..65a5f581d981 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -6,7 +6,7 @@ * This driver supports the following ACCES devices: 104-IDIO-16, * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8. */ -#include <linux/bitops.h> +#include <linux/bits.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/gpio/driver.h> @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/spinlock.h> +#include <linux/types.h> #define IDIO_16_EXTENT 8 #define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT) @@ -33,18 +34,41 @@ module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers"); /** + * struct idio_16_reg - device registers structure + * @out0_7: Read: N/A + * Write: FET Drive Outputs 0-7 + * @in0_7: Read: Isolated Inputs 0-7 + * Write: Clear Interrupt + * @irq_ctl: Read: Enable IRQ + * Write: Disable IRQ + * @unused: N/A + * @out8_15: Read: N/A + * Write: FET Drive Outputs 8-15 + * @in8_15: Read: Isolated Inputs 8-15 + * Write: N/A + */ +struct idio_16_reg { + u8 out0_7; + u8 in0_7; + u8 irq_ctl; + u8 unused; + u8 out8_15; + u8 in8_15; +}; + +/** * struct idio_16_gpio - GPIO device private data structure * @chip: instance of the gpio_chip * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers * @out_state: output bits state */ struct idio_16_gpio { struct gpio_chip chip; raw_spinlock_t lock; unsigned long irq_mask; - void __iomem *base; + struct idio_16_reg __iomem *reg; unsigned int out_state; }; @@ -79,9 +103,9 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset) return -EINVAL; if (offset < 24) - return !!(ioread8(idio16gpio->base + 1) & mask); + return !!(ioread8(&idio16gpio->reg->in0_7) & mask); - return !!(ioread8(idio16gpio->base + 5) & (mask>>8)); + return !!(ioread8(&idio16gpio->reg->in8_15) & (mask>>8)); } static int idio_16_gpio_get_multiple(struct gpio_chip *chip, @@ -91,9 +115,9 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip, *bits = 0; if (*mask & GENMASK(23, 16)) - *bits |= (unsigned long)ioread8(idio16gpio->base + 1) << 16; + *bits |= (unsigned long)ioread8(&idio16gpio->reg->in0_7) << 16; if (*mask & GENMASK(31, 24)) - *bits |= (unsigned long)ioread8(idio16gpio->base + 5) << 24; + *bits |= (unsigned long)ioread8(&idio16gpio->reg->in8_15) << 24; return 0; } @@ -116,9 +140,9 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset, idio16gpio->out_state &= ~mask; if (offset > 7) - iowrite8(idio16gpio->out_state >> 8, idio16gpio->base + 4); + iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15); else - iowrite8(idio16gpio->out_state, idio16gpio->base); + iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -135,9 +159,9 @@ static void idio_16_gpio_set_multiple(struct gpio_chip *chip, idio16gpio->out_state |= *mask & *bits; if (*mask & 0xFF) - iowrite8(idio16gpio->out_state, idio16gpio->base); + iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7); if ((*mask >> 8) & 0xFF) - iowrite8(idio16gpio->out_state >> 8, idio16gpio->base + 4); + iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -158,7 +182,7 @@ static void idio_16_irq_mask(struct irq_data *data) if (!idio16gpio->irq_mask) { raw_spin_lock_irqsave(&idio16gpio->lock, flags); - iowrite8(0, idio16gpio->base + 2); + iowrite8(0, &idio16gpio->reg->irq_ctl); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -177,7 +201,7 @@ static void idio_16_irq_unmask(struct irq_data *data) if (!prev_irq_mask) { raw_spin_lock_irqsave(&idio16gpio->lock, flags); - ioread8(idio16gpio->base + 2); + ioread8(&idio16gpio->reg->irq_ctl); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -212,7 +236,7 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) raw_spin_lock(&idio16gpio->lock); - iowrite8(0, idio16gpio->base + 1); + iowrite8(0, &idio16gpio->reg->in0_7); raw_spin_unlock(&idio16gpio->lock); @@ -232,8 +256,8 @@ static int idio_16_irq_init_hw(struct gpio_chip *gc) struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - iowrite8(0, idio16gpio->base + 2); - iowrite8(0, idio16gpio->base + 1); + iowrite8(0, &idio16gpio->reg->irq_ctl); + iowrite8(0, &idio16gpio->reg->in0_7); return 0; } @@ -255,8 +279,8 @@ static int idio_16_probe(struct device *dev, unsigned int id) return -EBUSY; } - idio16gpio->base = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); - if (!idio16gpio->base) + idio16gpio->reg = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); + if (!idio16gpio->reg) return -ENOMEM; idio16gpio->chip.label = name; diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index 173e06758e6c..0464f1ecd20d 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -5,15 +5,17 @@ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> */ +#include <linux/bits.h> #include <linux/err.h> -#include <linux/module.h> -#include <linux/of_device.h> #include <linux/gpio/driver.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/platform_device.h> +#include <linux/property.h> -#define MMIO_74XX_DIR_IN (0 << 8) -#define MMIO_74XX_DIR_OUT (1 << 8) -#define MMIO_74XX_BIT_CNT(x) ((x) & 0xff) +#define MMIO_74XX_DIR_IN BIT(8) +#define MMIO_74XX_DIR_OUT BIT(9) +#define MMIO_74XX_BIT_CNT(x) ((x) & GENMASK(7, 0)) struct mmio_74xx_gpio_priv { struct gpio_chip gc; @@ -87,7 +89,10 @@ static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio) { struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); - return (priv->flags & MMIO_74XX_DIR_OUT) ? -ENOTSUPP : 0; + if (priv->flags & MMIO_74XX_DIR_IN) + return 0; + + return -ENOTSUPP; } static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) @@ -112,7 +117,7 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->flags = (uintptr_t)of_device_get_match_data(&pdev->dev); + priv->flags = (uintptr_t)device_get_match_data(&pdev->dev); dat = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dat)) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index cc349d4e4973..a6439e3daff0 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -6,8 +6,9 @@ #include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/interrupt.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_irq.h> +#include <linux/property.h> #include <linux/seq_file.h> #include <linux/slab.h> @@ -485,22 +486,17 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, return 0; } -static int adnp_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adnp_i2c_probe(struct i2c_client *client) { - struct device_node *np = client->dev.of_node; + struct device *dev = &client->dev; struct adnp *adnp; u32 num_gpios; int err; - err = of_property_read_u32(np, "nr-gpios", &num_gpios); + err = device_property_read_u32(dev, "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; @@ -508,8 +504,7 @@ static int adnp_i2c_probe(struct i2c_client *client, mutex_init(&adnp->i2c_lock); adnp->client = client; - err = adnp_gpio_setup(adnp, num_gpios, - of_property_read_bool(np, "interrupt-controller")); + err = adnp_gpio_setup(adnp, num_gpios, device_property_read_bool(dev, "interrupt-controller")); if (err) return err; @@ -535,7 +530,7 @@ static struct i2c_driver adnp_i2c_driver = { .name = "gpio-adnp", .of_match_table = adnp_of_match, }, - .probe = adnp_i2c_probe, + .probe_new = adnp_i2c_probe, .id_table = adnp_i2c_id, }; module_i2c_driver(adnp_i2c_driver); diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index e388e75103f4..d49f12560cde 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -6,20 +6,18 @@ * Copyright 2009-2010 Analog Devices Inc. */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/i2c.h> #include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/of_device.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/slab.h> #include <linux/platform_data/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 @@ -422,23 +420,21 @@ static int adp5588_gpio_remove(struct i2c_client *client) } static const struct i2c_device_id adp5588_gpio_id[] = { - {DRV_NAME, 0}, + { "adp5588-gpio" }, {} }; MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); -#ifdef CONFIG_OF static const struct of_device_id adp5588_gpio_of_id[] = { - { .compatible = "adi," DRV_NAME, }, - {}, + { .compatible = "adi,adp5588-gpio" }, + {} }; MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id); -#endif static struct i2c_driver adp5588_gpio_driver = { .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(adp5588_gpio_of_id), + .name = "adp5588-gpio", + .of_match_table = adp5588_gpio_of_id, }, .probe_new = adp5588_gpio_probe, .remove = adp5588_gpio_remove, diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 85429dd6b3b6..c55b35da61a0 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -375,12 +375,7 @@ static int brcmstb_gpio_remove(struct platform_device *pdev) { struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev); struct brcmstb_gpio_bank *bank; - int offset, ret = 0, virq; - - if (!priv) { - dev_err(&pdev->dev, "called %s without drvdata!\n", __func__); - return -EFAULT; - } + int offset, virq; if (priv->parent_irq > 0) irq_set_chained_handler_and_data(priv->parent_irq, NULL, NULL); @@ -401,7 +396,7 @@ static int brcmstb_gpio_remove(struct platform_device *pdev) list_for_each_entry(bank, &priv->bank_list, node) gpiochip_remove(&bank->gc); - return ret; + return 0; } static int brcmstb_gpio_of_xlate(struct gpio_chip *gc, diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index f960587f86a3..59c4c48d8296 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -22,6 +22,7 @@ #include <linux/platform_data/gpio-davinci.h> #include <linux/irqchip/chained_irq.h> #include <linux/spinlock.h> +#include <linux/pm_runtime.h> #include <asm-generic/gpio.h> @@ -62,6 +63,8 @@ struct davinci_gpio_controller { void __iomem *regs[MAX_REGS_BANKS]; int gpio_unbanked; int irqs[MAX_INT_PER_BANK]; + struct davinci_gpio_regs context[MAX_REGS_BANKS]; + u32 binten_context; }; static inline u32 __gpio_mask(unsigned gpio) @@ -622,6 +625,85 @@ done: return 0; } +static void davinci_gpio_save_context(struct davinci_gpio_controller *chips, + u32 nbank) +{ + struct davinci_gpio_regs __iomem *g; + struct davinci_gpio_regs *context; + u32 bank; + void __iomem *base; + + base = chips->regs[0] - offset_array[0]; + chips->binten_context = readl_relaxed(base + BINTEN); + + for (bank = 0; bank < nbank; bank++) { + g = chips->regs[bank]; + context = &chips->context[bank]; + context->dir = readl_relaxed(&g->dir); + context->set_data = readl_relaxed(&g->set_data); + context->set_rising = readl_relaxed(&g->set_rising); + context->set_falling = readl_relaxed(&g->set_falling); + } + + /* Clear Bank interrupt enable bit */ + writel_relaxed(0, base + BINTEN); + + /* Clear all interrupt status registers */ + writel_relaxed(GENMASK(31, 0), &g->intstat); +} + +static void davinci_gpio_restore_context(struct davinci_gpio_controller *chips, + u32 nbank) +{ + struct davinci_gpio_regs __iomem *g; + struct davinci_gpio_regs *context; + u32 bank; + void __iomem *base; + + base = chips->regs[0] - offset_array[0]; + + if (readl_relaxed(base + BINTEN) != chips->binten_context) + writel_relaxed(chips->binten_context, base + BINTEN); + + for (bank = 0; bank < nbank; bank++) { + g = chips->regs[bank]; + context = &chips->context[bank]; + if (readl_relaxed(&g->dir) != context->dir) + writel_relaxed(context->dir, &g->dir); + if (readl_relaxed(&g->set_data) != context->set_data) + writel_relaxed(context->set_data, &g->set_data); + if (readl_relaxed(&g->set_rising) != context->set_rising) + writel_relaxed(context->set_rising, &g->set_rising); + if (readl_relaxed(&g->set_falling) != context->set_falling) + writel_relaxed(context->set_falling, &g->set_falling); + } +} + +static int davinci_gpio_suspend(struct device *dev) +{ + struct davinci_gpio_controller *chips = dev_get_drvdata(dev); + struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev); + u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32); + + davinci_gpio_save_context(chips, nbank); + + return 0; +} + +static int davinci_gpio_resume(struct device *dev) +{ + struct davinci_gpio_controller *chips = dev_get_drvdata(dev); + struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev); + u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32); + + davinci_gpio_restore_context(chips, nbank); + + return 0; +} + +DEFINE_SIMPLE_DEV_PM_OPS(davinci_gpio_dev_pm_ops, davinci_gpio_suspend, + davinci_gpio_resume); + static const struct of_device_id davinci_gpio_ids[] = { { .compatible = "ti,keystone-gpio", keystone_gpio_get_irq_chip}, { .compatible = "ti,am654-gpio", keystone_gpio_get_irq_chip}, @@ -634,6 +716,7 @@ static struct platform_driver davinci_gpio_driver = { .probe = davinci_gpio_probe, .driver = { .name = "davinci_gpio", + .pm = pm_sleep_ptr(&davinci_gpio_dev_pm_ops), .of_match_table = of_match_ptr(davinci_gpio_ids), }, }; diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c index 097a06463d01..2689671b6b01 100644 --- a/drivers/gpio/gpio-gpio-mm.c +++ b/drivers/gpio/gpio-gpio-mm.c @@ -6,8 +6,6 @@ * This driver supports the following Diamond Systems devices: GPIO-MM and * GPIO-MM-12. */ -#include <linux/bitmap.h> -#include <linux/bitops.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/gpio/driver.h> @@ -17,7 +15,10 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/spinlock.h> + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define GPIOMM_EXTENT 8 #define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT) @@ -27,32 +28,26 @@ static unsigned int num_gpiomm; module_param_hw_array(base, uint, ioport, &num_gpiomm, 0); MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses"); +#define GPIOMM_NUM_PPI 2 + /** * struct gpiomm_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @io_state: bit I/O state (whether bit is set to input or output) - * @out_state: output bits state - * @control: Control registers state - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device + * @chip: instance of the gpio_chip + * @ppi_state: Programmable Peripheral Interface group states + * @ppi: Programmable Peripheral Interface groups */ struct gpiomm_gpio { struct gpio_chip chip; - unsigned char io_state[6]; - unsigned char out_state[6]; - unsigned char control[2]; - spinlock_t lock; - void __iomem *base; + struct i8255_state ppi_state[GPIOMM_NUM_PPI]; + struct i8255 __iomem *ppi; }; static int gpiomm_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - if (gpiommgpio->io_state[port] & mask) + if (i8255_get_direction(gpiommgpio->ppi_state, offset)) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; @@ -62,35 +57,8 @@ static int gpiomm_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - unsigned long flags; - unsigned int control; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - gpiommgpio->io_state[io_port] |= 0xF0; - gpiommgpio->control[control_port] |= BIT(3); - } else { - gpiommgpio->io_state[io_port] |= 0x0F; - gpiommgpio->control[control_port] |= BIT(0); - } - } else { - gpiommgpio->io_state[io_port] |= 0xFF; - if (io_port == 0 || io_port == 3) - gpiommgpio->control[control_port] |= BIT(4); - else - gpiommgpio->control[control_port] |= BIT(1); - } - control = BIT(7) | gpiommgpio->control[control_port]; - iowrite8(control, gpiommgpio->base + 3 + control_port*4); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_direction_input(gpiommgpio->ppi, gpiommgpio->ppi_state, offset); return 0; } @@ -99,44 +67,9 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; - unsigned long flags; - unsigned int control; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - gpiommgpio->io_state[io_port] &= 0x0F; - gpiommgpio->control[control_port] &= ~BIT(3); - } else { - gpiommgpio->io_state[io_port] &= 0xF0; - gpiommgpio->control[control_port] &= ~BIT(0); - } - } else { - gpiommgpio->io_state[io_port] &= 0x00; - if (io_port == 0 || io_port == 3) - gpiommgpio->control[control_port] &= ~BIT(4); - else - gpiommgpio->control[control_port] &= ~BIT(1); - } - - if (value) - gpiommgpio->out_state[io_port] |= mask; - else - gpiommgpio->out_state[io_port] &= ~mask; - - control = BIT(7) | gpiommgpio->control[control_port]; - iowrite8(control, gpiommgpio->base + 3 + control_port*4); - iowrite8(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_direction_output(gpiommgpio->ppi, gpiommgpio->ppi_state, offset, + value); return 0; } @@ -144,47 +77,16 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip, static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int in_port = (port > 2) ? port + 1 : port; - unsigned long flags; - unsigned int port_state; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* ensure that GPIO is set for input */ - if (!(gpiommgpio->io_state[port] & mask)) { - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - return -EINVAL; - } - - port_state = ioread8(gpiommgpio->base + in_port); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - return !!(port_state & mask); + return i8255_get(gpiommgpio->ppi, offset); } -static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - void __iomem *port_addr; - unsigned long port_state; - - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = gpiommgpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(gpiommgpio->ppi, mask, bits, chip->ngpio); return 0; } @@ -193,49 +95,17 @@ static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (port > 2) ? port + 1 : port; - unsigned long flags; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - if (value) - gpiommgpio->out_state[port] |= mask; - else - gpiommgpio->out_state[port] &= ~mask; - - iowrite8(gpiommgpio->out_state[port], gpiommgpio->base + out_port); - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_set(gpiommgpio->ppi, gpiommgpio->ppi_state, offset, value); } static void gpiomm_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - size_t index; - void __iomem *port_addr; - unsigned long bitmask; - unsigned long flags; - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - index = offset / 8; - port_addr = gpiommgpio->base + ports[index]; - - bitmask = bitmap_get_value8(bits, offset) & gpio_mask; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - /* update output state data and set device gpio register */ - gpiommgpio->out_state[index] &= ~gpio_mask; - gpiommgpio->out_state[index] |= bitmask; - iowrite8(gpiommgpio->out_state[index], port_addr); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - } + i8255_set_multiple(gpiommgpio->ppi, gpiommgpio->ppi_state, mask, bits, + chip->ngpio); } #define GPIOMM_NGPIO 48 @@ -250,6 +120,21 @@ static const char *gpiomm_names[GPIOMM_NGPIO] = { "Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7", }; +static void gpiomm_init_dio(struct i8255 __iomem *const ppi, + struct i8255_state *const ppi_state) +{ + const unsigned long ngpio = 24; + const unsigned long mask = GENMASK(ngpio - 1, 0); + const unsigned long bits = 0; + unsigned long i; + + /* Initialize all GPIO to output 0 */ + for (i = 0; i < GPIOMM_NUM_PPI; i++) { + i8255_mode0_output(&ppi[i]); + i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio); + } +} + static int gpiomm_probe(struct device *dev, unsigned int id) { struct gpiomm_gpio *gpiommgpio; @@ -266,8 +151,8 @@ static int gpiomm_probe(struct device *dev, unsigned int id) return -EBUSY; } - gpiommgpio->base = devm_ioport_map(dev, base[id], GPIOMM_EXTENT); - if (!gpiommgpio->base) + gpiommgpio->ppi = devm_ioport_map(dev, base[id], GPIOMM_EXTENT); + if (!gpiommgpio->ppi) return -ENOMEM; gpiommgpio->chip.label = name; @@ -284,7 +169,8 @@ static int gpiomm_probe(struct device *dev, unsigned int id) gpiommgpio->chip.set = gpiomm_gpio_set; gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple; - spin_lock_init(&gpiommgpio->lock); + i8255_state_init(gpiommgpio->ppi_state, GPIOMM_NUM_PPI); + gpiomm_init_dio(gpiommgpio->ppi, gpiommgpio->ppi_state); err = devm_gpiochip_add_data(dev, &gpiommgpio->chip, gpiommgpio); if (err) { @@ -292,16 +178,6 @@ static int gpiomm_probe(struct device *dev, unsigned int id) return err; } - /* initialize all GPIO as output */ - iowrite8(0x80, gpiommgpio->base + 3); - iowrite8(0x00, gpiommgpio->base); - iowrite8(0x00, gpiommgpio->base + 1); - iowrite8(0x00, gpiommgpio->base + 2); - iowrite8(0x80, gpiommgpio->base + 7); - iowrite8(0x00, gpiommgpio->base + 4); - iowrite8(0x00, gpiommgpio->base + 5); - iowrite8(0x00, gpiommgpio->base + 6); - return 0; } diff --git a/drivers/gpio/gpio-i8255.c b/drivers/gpio/gpio-i8255.c new file mode 100644 index 000000000000..9b97db418df1 --- /dev/null +++ b/drivers/gpio/gpio-i8255.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel 8255 Programmable Peripheral Interface + * Copyright (C) 2022 William Breathitt Gray + */ +#include <linux/bitmap.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include "gpio-i8255.h" + +#define I8255_CONTROL_PORTC_LOWER_DIRECTION BIT(0) +#define I8255_CONTROL_PORTB_DIRECTION BIT(1) +#define I8255_CONTROL_PORTC_UPPER_DIRECTION BIT(3) +#define I8255_CONTROL_PORTA_DIRECTION BIT(4) +#define I8255_CONTROL_MODE_SET BIT(7) +#define I8255_PORTA 0 +#define I8255_PORTB 1 +#define I8255_PORTC 2 + +static int i8255_get_port(struct i8255 __iomem *const ppi, + const unsigned long io_port, const unsigned long mask) +{ + const unsigned long bank = io_port / 3; + const unsigned long ppi_port = io_port % 3; + + return ioread8(&ppi[bank].port[ppi_port]) & mask; +} + +static u8 i8255_direction_mask(const unsigned long offset) +{ + const unsigned long port_offset = offset % 8; + const unsigned long io_port = offset / 8; + const unsigned long ppi_port = io_port % 3; + + switch (ppi_port) { + case I8255_PORTA: + return I8255_CONTROL_PORTA_DIRECTION; + case I8255_PORTB: + return I8255_CONTROL_PORTB_DIRECTION; + case I8255_PORTC: + /* Port C can be configured by nibble */ + if (port_offset >= 4) + return I8255_CONTROL_PORTC_UPPER_DIRECTION; + return I8255_CONTROL_PORTC_LOWER_DIRECTION; + default: + /* Should never reach this path */ + return 0; + } +} + +static void i8255_set_port(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long io_port, + const unsigned long mask, const unsigned long bits) +{ + const unsigned long bank = io_port / 3; + const unsigned long ppi_port = io_port % 3; + unsigned long flags; + unsigned long out_state; + + spin_lock_irqsave(&state[bank].lock, flags); + + out_state = ioread8(&ppi[bank].port[ppi_port]); + out_state = (out_state & ~mask) | (bits & mask); + iowrite8(out_state, &ppi[bank].port[ppi_port]); + + spin_unlock_irqrestore(&state[bank].lock, flags); +} + +/** + * i8255_direction_input - configure signal offset as input + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: signal offset to configure as input + * + * Configures a signal @offset as input for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks. The @state control_state + * values are updated to reflect the new configuration. + */ +void i8255_direction_input(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + unsigned long flags; + + spin_lock_irqsave(&state[bank].lock, flags); + + state[bank].control_state |= I8255_CONTROL_MODE_SET; + state[bank].control_state |= i8255_direction_mask(offset); + + iowrite8(state[bank].control_state, &ppi[bank].control); + + spin_unlock_irqrestore(&state[bank].lock, flags); +} +EXPORT_SYMBOL_NS_GPL(i8255_direction_input, I8255); + +/** + * i8255_direction_output - configure signal offset as output + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: signal offset to configure as output + * @value: signal value to output + * + * Configures a signal @offset as output for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks and sets the respective signal + * output to the desired @value. The @state control_state values are updated to + * reflect the new configuration. + */ +void i8255_direction_output(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long offset, + const unsigned long value) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + unsigned long flags; + + spin_lock_irqsave(&state[bank].lock, flags); + + state[bank].control_state |= I8255_CONTROL_MODE_SET; + state[bank].control_state &= ~i8255_direction_mask(offset); + + iowrite8(state[bank].control_state, &ppi[bank].control); + + spin_unlock_irqrestore(&state[bank].lock, flags); + + i8255_set(ppi, state, offset, value); +} +EXPORT_SYMBOL_NS_GPL(i8255_direction_output, I8255); + +/** + * i8255_get - get signal value at signal offset + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @offset: offset of signal to get + * + * Returns the signal value (0=low, 1=high) for the signal at @offset for the + * respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +int i8255_get(struct i8255 __iomem *const ppi, const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long offset_mask = BIT(offset % 8); + + return !!i8255_get_port(ppi, io_port, offset_mask); +} +EXPORT_SYMBOL_NS_GPL(i8255_get, I8255); + +/** + * i8255_get_direction - get the I/O direction for a signal offset + * @state: devices states of the respective PPI banks + * @offset: offset of signal to get direction + * + * Returns the signal direction (0=output, 1=input) for the signal at @offset. + */ +int i8255_get_direction(const struct i8255_state *const state, + const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + + return !!(state[bank].control_state & i8255_direction_mask(offset)); +} +EXPORT_SYMBOL_NS_GPL(i8255_get_direction, I8255); + +/** + * i8255_get_multiple - get multiple signal values at multiple signal offsets + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @mask: mask of signals to get + * @bits: bitmap to store signal values + * @ngpio: number of GPIO signals of the respective PPI banks + * + * Stores in @bits the values (0=low, 1=high) for the signals defined by @mask + * for the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_get_multiple(struct i8255 __iomem *const ppi, + const unsigned long *const mask, + unsigned long *const bits, const unsigned long ngpio) +{ + unsigned long offset; + unsigned long port_mask; + unsigned long io_port; + unsigned long port_state; + + bitmap_zero(bits, ngpio); + + for_each_set_clump8(offset, port_mask, mask, ngpio) { + io_port = offset / 8; + port_state = i8255_get_port(ppi, io_port, port_mask); + + bitmap_set_value8(bits, port_state, offset); + } +} +EXPORT_SYMBOL_NS_GPL(i8255_get_multiple, I8255); + +/** + * i8255_mode0_output - configure all PPI ports to MODE 0 output mode + * @ppi: Intel 8255 Programmable Peripheral Interface bank + * + * Configures all Intel 8255 Programmable Peripheral Interface (@ppi) ports to + * MODE 0 (Basic Input/Output) output mode. + */ +void i8255_mode0_output(struct i8255 __iomem *const ppi) +{ + iowrite8(I8255_CONTROL_MODE_SET, &ppi->control); +} +EXPORT_SYMBOL_NS_GPL(i8255_mode0_output, I8255); + +/** + * i8255_set - set signal value at signal offset + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: offset of signal to set + * @value: value of signal to set + * + * Assigns output @value for the signal at @offset for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_set(struct i8255 __iomem *const ppi, struct i8255_state *const state, + const unsigned long offset, const unsigned long value) +{ + const unsigned long io_port = offset / 8; + const unsigned long port_offset = offset % 8; + const unsigned long mask = BIT(port_offset); + const unsigned long bits = value << port_offset; + + i8255_set_port(ppi, state, io_port, mask, bits); +} +EXPORT_SYMBOL_NS_GPL(i8255_set, I8255); + +/** + * i8255_set_multiple - set signal values at multiple signal offsets + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @mask: mask of signals to set + * @bits: bitmap of signal output values + * @ngpio: number of GPIO signals of the respective PPI banks + * + * Assigns output values defined by @bits for the signals defined by @mask for + * the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_set_multiple(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long *const mask, + const unsigned long *const bits, + const unsigned long ngpio) +{ + unsigned long offset; + unsigned long port_mask; + unsigned long io_port; + unsigned long value; + + for_each_set_clump8(offset, port_mask, mask, ngpio) { + io_port = offset / 8; + value = bitmap_get_value8(bits, offset); + i8255_set_port(ppi, state, io_port, port_mask, value); + } +} +EXPORT_SYMBOL_NS_GPL(i8255_set_multiple, I8255); + +/** + * i8255_state_init - initialize i8255_state structure + * @state: devices states of the respective PPI banks + * @nbanks: number of Intel 8255 Programmable Peripheral Interface banks + * + * Initializes the @state of each Intel 8255 Programmable Peripheral Interface + * bank for use in i8255 library functions. + */ +void i8255_state_init(struct i8255_state *const state, + const unsigned long nbanks) +{ + unsigned long bank; + + for (bank = 0; bank < nbanks; bank++) + spin_lock_init(&state[bank].lock); +} +EXPORT_SYMBOL_NS_GPL(i8255_state_init, I8255); + +MODULE_AUTHOR("William Breathitt Gray"); +MODULE_DESCRIPTION("Intel 8255 Programmable Peripheral Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-i8255.h b/drivers/gpio/gpio-i8255.h new file mode 100644 index 000000000000..d9084aae9446 --- /dev/null +++ b/drivers/gpio/gpio-i8255.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2022 William Breathitt Gray */ +#ifndef _I8255_H_ +#define _I8255_H_ + +#include <linux/spinlock.h> +#include <linux/types.h> + +/** + * struct i8255 - Intel 8255 register structure + * @port: Port A, B, and C + * @control: Control register + */ +struct i8255 { + u8 port[3]; + u8 control; +}; + +/** + * struct i8255_state - Intel 8255 state structure + * @lock: synchronization lock for accessing device state + * @control_state: Control register state + */ +struct i8255_state { + spinlock_t lock; + u8 control_state; +}; + +void i8255_direction_input(struct i8255 __iomem *ppi, struct i8255_state *state, + unsigned long offset); +void i8255_direction_output(struct i8255 __iomem *ppi, + struct i8255_state *state, unsigned long offset, + unsigned long value); +int i8255_get(struct i8255 __iomem *ppi, unsigned long offset); +int i8255_get_direction(const struct i8255_state *state, unsigned long offset); +void i8255_get_multiple(struct i8255 __iomem *ppi, const unsigned long *mask, + unsigned long *bits, unsigned long ngpio); +void i8255_mode0_output(struct i8255 __iomem *const ppi); +void i8255_set(struct i8255 __iomem *ppi, struct i8255_state *state, + unsigned long offset, unsigned long value); +void i8255_set_multiple(struct i8255 __iomem *ppi, struct i8255_state *state, + const unsigned long *mask, const unsigned long *bits, + unsigned long ngpio); +void i8255_state_init(struct i8255_state *const state, unsigned long nbanks); + +#endif /* _I8255_H_ */ diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c index 8a30fb185aab..79edd5db49d2 100644 --- a/drivers/gpio/gpio-lp3943.c +++ b/drivers/gpio/gpio-lp3943.c @@ -42,7 +42,7 @@ struct lp3943_gpio { u16 input_mask; /* 1 = GPIO is input direction, 0 = output */ }; -static int lp3943_gpio_request(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_request(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); struct lp3943 *lp3943 = lp3943_gpio->lp3943; @@ -54,7 +54,7 @@ static int lp3943_gpio_request(struct gpio_chip *chip, unsigned offset) return 0; } -static void lp3943_gpio_free(struct gpio_chip *chip, unsigned offset) +static void lp3943_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); struct lp3943 *lp3943 = lp3943_gpio->lp3943; @@ -72,7 +72,7 @@ static int lp3943_gpio_set_mode(struct lp3943_gpio *lp3943_gpio, u8 offset, val << mux[offset].shift); } -static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); @@ -82,7 +82,7 @@ static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned offset) } static int lp3943_get_gpio_in_status(struct lp3943_gpio *lp3943_gpio, - struct gpio_chip *chip, unsigned offset) + struct gpio_chip *chip, unsigned int offset) { u8 addr, read; int err; @@ -107,7 +107,7 @@ static int lp3943_get_gpio_in_status(struct lp3943_gpio *lp3943_gpio, } static int lp3943_get_gpio_out_status(struct lp3943_gpio *lp3943_gpio, - struct gpio_chip *chip, unsigned offset) + struct gpio_chip *chip, unsigned int offset) { struct lp3943 *lp3943 = lp3943_gpio->lp3943; const struct lp3943_reg_cfg *mux = lp3943->mux_cfg; @@ -128,7 +128,7 @@ static int lp3943_get_gpio_out_status(struct lp3943_gpio *lp3943_gpio, return -EINVAL; } -static int lp3943_gpio_get(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); @@ -147,7 +147,7 @@ static int lp3943_gpio_get(struct gpio_chip *chip, unsigned offset) return lp3943_get_gpio_out_status(lp3943_gpio, chip, offset); } -static void lp3943_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static void lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); u8 data; @@ -160,7 +160,7 @@ static void lp3943_gpio_set(struct gpio_chip *chip, unsigned offset, int value) lp3943_gpio_set_mode(lp3943_gpio, offset, data); } -static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned offset, +static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index cb2b2f735c15..ab2a652964ec 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -121,12 +121,14 @@ static int pca9570_probe(struct i2c_client *client) static const struct i2c_device_id pca9570_id_table[] = { { "pca9570", 4 }, + { "pca9571", 8 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9570_id_table); static const struct of_device_id pca9570_of_match_table[] = { { .compatible = "nxp,pca9570", .data = (void *)4 }, + { .compatible = "nxp,pca9571", .data = (void *)8 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pca9570_of_match_table); diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 3a0bd8795741..ee37ecb615cb 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -37,6 +37,11 @@ struct pch_regs { u32 reset; }; +#define PCI_DEVICE_ID_INTEL_EG20T_PCH 0x8803 +#define PCI_DEVICE_ID_ROHM_ML7223m_IOH 0x8014 +#define PCI_DEVICE_ID_ROHM_ML7223n_IOH 0x8043 +#define PCI_DEVICE_ID_ROHM_EG20T_PCH 0x8803 + enum pch_type_t { INTEL_EG20T_PCH, OKISEMI_ML7223m_IOH, /* LAPIS Semiconductor ML7223 IOH PCIe Bus-m */ @@ -357,16 +362,12 @@ static int pch_gpio_probe(struct pci_dev *pdev, chip->dev = dev; ret = pcim_enable_device(pdev); - if (ret) { - dev_err(dev, "pci_enable_device FAILED"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to enable PCI device\n"); ret = pcim_iomap_regions(pdev, BIT(1), KBUILD_MODNAME); - if (ret) { - dev_err(dev, "pci_request_regions FAILED-%d", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request and map PCI regions\n"); chip->base = pcim_iomap_table(pdev)[1]; chip->ioh = id->driver_data; @@ -376,10 +377,8 @@ static int pch_gpio_probe(struct pci_dev *pdev, pch_gpio_setup(chip); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); - if (ret) { - dev_err(dev, "PCH gpio: Failed to register GPIO\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to register GPIO\n"); irq_base = devm_irq_alloc_descs(dev, -1, 0, gpio_pins[chip->ioh], NUMA_NO_NODE); @@ -396,10 +395,8 @@ static int pch_gpio_probe(struct pci_dev *pdev, ret = devm_request_irq(dev, pdev->irq, pch_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); - if (ret) { - dev_err(dev, "request_irq failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request IRQ\n"); return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); } @@ -433,15 +430,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume); static const struct pci_device_id pch_gpio_pcidev_id[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803), - .driver_data = INTEL_EG20T_PCH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014), - .driver_data = OKISEMI_ML7223m_IOH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043), - .driver_data = OKISEMI_ML7223n_IOH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8803), - .driver_data = INTEL_EG20T_PCH }, - { 0, } + { PCI_DEVICE_DATA(INTEL, EG20T_PCH, INTEL_EG20T_PCH) }, + { PCI_DEVICE_DATA(ROHM, ML7223m_IOH, OKISEMI_ML7223m_IOH) }, + { PCI_DEVICE_DATA(ROHM, ML7223n_IOH, OKISEMI_ML7223n_IOH) }, + { PCI_DEVICE_DATA(ROHM, EG20T_PCH, INTEL_EG20T_PCH) }, + { } }; MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id); diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index e342a6dc4c6c..f91e876fd969 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -27,6 +27,7 @@ #define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ #define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ +#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ static const struct rockchip_gpio_regs gpio_regs_v1 = { .port_dr = 0x00, @@ -664,7 +665,7 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) id = readl(bank->reg_base + gpio_regs_v2.version_id); /* If not gpio v2, that is default to v1. */ - if (id == GPIO_TYPE_V2) { + if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) { bank->gpio_regs = &gpio_regs_v2; bank->gpio_type = GPIO_TYPE_V2; bank->db_clk = of_clk_get(bank->of_node, 1); diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index de249726230e..5046e51af8df 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -593,27 +593,13 @@ out: /* 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; + WARN_ON(!is_module()); + return 0; } static const struct of_device_id twl_gpio_match[] = { diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c index d2a8644864c3..386e69300332 100644 --- a/drivers/gpio/gpio-ucb1400.c +++ b/drivers/gpio/gpio-ucb1400.c @@ -64,34 +64,14 @@ static int ucb1400_gpio_probe(struct platform_device *dev) ucb->gc.can_sleep = true; err = devm_gpiochip_add_data(&dev->dev, &ucb->gc, ucb); - 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; - } - - return err; -} - static struct platform_driver ucb1400_gpio_driver = { .probe = ucb1400_gpio_probe, - .remove = ucb1400_gpio_remove, .driver = { .name = "ucb1400_gpio" }, diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c deleted file mode 100644 index 8d09b619c166..000000000000 --- a/drivers/gpio/gpio-vr41xx.c +++ /dev/null @@ -1,541 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * 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> - */ -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/gpio/driver.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> -#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) -{ - int ret; - - ret = gpiochip_lock_as_irq(&vr41xx_gpio_chip, irqd_to_hwirq(data)); - if (ret) { - dev_err(vr41xx_gpio_chip.parent, - "unable to lock HW IRQ %lu for IRQ\n", - data->hwirq); - return ret; - } - - /* 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); - - 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; -} - -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) -{ - unsigned int trigger, i, pin; - struct irq_chip *chip; - int irq; - - 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; - } - - giu_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(giu_base)) - return PTR_ERR(giu_base); - - vr41xx_gpio_chip.parent = &pdev->dev; - - if (gpiochip_add_data(&vr41xx_gpio_chip, NULL)) - 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) { - 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-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index 5078631d8014..b098f2dc196b 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -4,7 +4,6 @@ * Copyright (C) 2016 William Breathitt Gray */ #include <linux/bitmap.h> -#include <linux/bitops.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/gpio/driver.h> @@ -17,8 +16,9 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/spinlock.h> +#include <linux/types.h> -#define WS16C48_EXTENT 16 +#define WS16C48_EXTENT 10 #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT) static unsigned int base[MAX_NUM_WS16C48]; @@ -31,6 +31,20 @@ module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); /** + * struct ws16c48_reg - device register structure + * @port: Port 0 through 5 I/O + * @int_pending: Interrupt Pending + * @page_lock: Register page (Bits 7-6) and I/O port lock (Bits 5-0) + * @pol_enab_int_id: Interrupt polarity, enable, and ID + */ +struct ws16c48_reg { + u8 port[6]; + u8 int_pending; + u8 page_lock; + u8 pol_enab_int_id[3]; +}; + +/** * struct ws16c48_gpio - GPIO device private data structure * @chip: instance of the gpio_chip * @io_state: bit I/O state (whether bit is set to input or output) @@ -38,7 +52,7 @@ MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts * @flow_mask: IRQ flow type mask for the respective I/O bits - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers */ struct ws16c48_gpio { struct gpio_chip chip; @@ -47,7 +61,7 @@ struct ws16c48_gpio { raw_spinlock_t lock; unsigned long irq_mask; unsigned long flow_mask; - void __iomem *base; + struct ws16c48_reg __iomem *reg; }; static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -73,7 +87,7 @@ static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ws16c48gpio->io_state[port] |= mask; ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -95,7 +109,7 @@ static int ws16c48_gpio_direction_output(struct gpio_chip *chip, ws16c48gpio->out_state[port] |= mask; else ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -118,7 +132,7 @@ static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) return -EINVAL; } - port_state = ioread8(ws16c48gpio->base + port); + port_state = ioread8(ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -131,14 +145,16 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip, struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); unsigned long offset; unsigned long gpio_mask; - void __iomem *port_addr; + size_t index; + u8 __iomem *port_addr; unsigned long port_state; /* clear bits array to a clean slate */ bitmap_zero(bits, chip->ngpio); for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { - port_addr = ws16c48gpio->base + offset / 8; + index = offset / 8; + port_addr = ws16c48gpio->reg->port + index; port_state = ioread8(port_addr) & gpio_mask; bitmap_set_value8(bits, port_state, offset); @@ -166,7 +182,7 @@ static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ws16c48gpio->out_state[port] |= mask; else ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -178,13 +194,13 @@ static void ws16c48_gpio_set_multiple(struct gpio_chip *chip, unsigned long offset; unsigned long gpio_mask; size_t index; - void __iomem *port_addr; + u8 __iomem *port_addr; unsigned long bitmask; unsigned long flags; for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { index = offset / 8; - port_addr = ws16c48gpio->base + index; + port_addr = ws16c48gpio->reg->port + index; /* mask out GPIO configured for input */ gpio_mask &= ~ws16c48gpio->io_state[index]; @@ -219,10 +235,15 @@ static void ws16c48_irq_ack(struct irq_data *data) port_state = ws16c48gpio->irq_mask >> (8*port); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(port_state & ~mask, ws16c48gpio->base + 8 + port); - iowrite8(port_state | mask, ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); + + /* Clear pending interrupt */ + iowrite8(port_state & ~mask, ws16c48gpio->reg->pol_enab_int_id + port); + iowrite8(port_state | mask, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -235,6 +256,7 @@ static void ws16c48_irq_mask(struct irq_data *data) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -243,10 +265,16 @@ static void ws16c48_irq_mask(struct irq_data *data) raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); ws16c48gpio->irq_mask &= ~mask; + port_state = ws16c48gpio->irq_mask >> (8 * port); + + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Disable interrupt */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -259,6 +287,7 @@ static void ws16c48_irq_unmask(struct irq_data *data) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -267,10 +296,16 @@ static void ws16c48_irq_unmask(struct irq_data *data) raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); ws16c48gpio->irq_mask |= mask; + port_state = ws16c48gpio->irq_mask >> (8 * port); + + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Enable interrupt */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -283,6 +318,7 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -304,9 +340,16 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) return -EINVAL; } - iowrite8(0x40, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + port_state = ws16c48gpio->flow_mask >> (8 * port); + + /* Select Register Page 1; Unlock all I/O ports */ + iowrite8(0x40, &ws16c48gpio->reg->page_lock); + + /* Set interrupt polarity */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -325,25 +368,26 @@ static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) { struct ws16c48_gpio *const ws16c48gpio = dev_id; struct gpio_chip *const chip = &ws16c48gpio->chip; + struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg; unsigned long int_pending; unsigned long port; unsigned long int_id; unsigned long gpio; - int_pending = ioread8(ws16c48gpio->base + 6) & 0x7; + int_pending = ioread8(®->int_pending) & 0x7; if (!int_pending) return IRQ_NONE; /* loop until all pending interrupts are handled */ do { for_each_set_bit(port, &int_pending, 3) { - int_id = ioread8(ws16c48gpio->base + 8 + port); + int_id = ioread8(reg->pol_enab_int_id + port); for_each_set_bit(gpio, &int_id, 8) generic_handle_domain_irq(chip->irq.domain, gpio + 8*port); } - int_pending = ioread8(ws16c48gpio->base + 6) & 0x7; + int_pending = ioread8(®->int_pending) & 0x7; } while (int_pending); return IRQ_HANDLED; @@ -369,12 +413,16 @@ static int ws16c48_irq_init_hw(struct gpio_chip *gc) { struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); - /* Disable IRQ by default */ - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(0, ws16c48gpio->base + 8); - iowrite8(0, ws16c48gpio->base + 9); - iowrite8(0, ws16c48gpio->base + 10); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); + + /* Disable interrupts for all lines */ + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]); + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]); + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); return 0; } @@ -396,8 +444,8 @@ static int ws16c48_probe(struct device *dev, unsigned int id) return -EBUSY; } - ws16c48gpio->base = devm_ioport_map(dev, base[id], WS16C48_EXTENT); - if (!ws16c48gpio->base) + ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT); + if (!ws16c48gpio->reg) return -ENOMEM; ws16c48gpio->chip.label = name; diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c index 43ca52fa6f9a..fd88500399c6 100644 --- a/drivers/gpio/gpio-xgs-iproc.c +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -281,11 +281,7 @@ static int iproc_gpio_probe(struct platform_device *pdev) static int iproc_gpio_remove(struct platform_device *pdev) { - struct iproc_gpio_chip *chip; - - chip = platform_get_drvdata(pdev); - if (!chip) - return -ENODEV; + struct iproc_gpio_chip *chip = platform_get_drvdata(pdev); if (chip->intr) { u32 val; diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 7f8e2fed2988..2fc6b6ff7f16 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -117,12 +117,14 @@ static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch) static void xgpio_read_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_set_value32(a, bit, xgpio_readreg(addr)); } static void xgpio_write_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_writereg(addr, xgpio_get_value32(a, bit)); } diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c2523ac26fac..9be1376f9a62 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -687,6 +687,9 @@ int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, case ACPI_PIN_CONFIG_PULLDOWN: *lookupflags |= GPIO_PULL_DOWN; break; + case ACPI_PIN_CONFIG_NOPULL: + *lookupflags |= GPIO_PULL_DISABLE; + break; default: break; } diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index b26e64338376..f8041d4898d1 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -434,12 +434,15 @@ struct line { struct linereq *req; unsigned int irq; /* - * eflags is set by edge_detector_setup(), edge_detector_stop() and - * edge_detector_update(), which are themselves mutually exclusive, - * and is accessed by edge_irq_thread() and debounce_work_func(), - * which can both live with a slightly stale value. + * The flags for the active edge detector configuration. + * + * edflags is set by linereq_create(), linereq_free(), and + * linereq_set_config_unlocked(), which are themselves mutually + * exclusive, and is accessed by edge_irq_thread(), + * process_hw_ts_thread() and debounce_work_func(), + * which can all live with a slightly stale value. */ - u64 eflags; + u64 edflags; /* * timestamp_ns and req_seqno are accessed only by * edge_irq_handler() and edge_irq_thread(), which are themselves @@ -469,9 +472,7 @@ struct line { * stale value. */ unsigned int level; - /* - * -- hte specific fields -- - */ +#ifdef CONFIG_HTE struct hte_ts_desc hdesc; /* * HTE provider sets line level at the time of event. The valid @@ -488,6 +489,7 @@ struct line { * last sequence number before debounce period expires. */ u32 last_seqno; +#endif /* CONFIG_HTE */ }; /** @@ -545,6 +547,12 @@ struct linereq { GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ GPIO_V2_LINE_BIAS_FLAGS) +/* subset of flags relevant for edge detector configuration */ +#define GPIO_V2_LINE_EDGE_DETECTOR_FLAGS \ + (GPIO_V2_LINE_FLAG_ACTIVE_LOW | \ + GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ + GPIO_V2_LINE_EDGE_FLAGS) + static void linereq_put_event(struct linereq *lr, struct gpio_v2_line_event *le) { @@ -567,19 +575,28 @@ static u64 line_event_timestamp(struct line *line) { if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags)) return ktime_get_real_ns(); - else if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) + else if (IS_ENABLED(CONFIG_HTE) && + test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) return line->timestamp_ns; return ktime_get_ns(); } +static u32 line_event_id(int level) +{ + return level ? GPIO_V2_LINE_EVENT_RISING_EDGE : + GPIO_V2_LINE_EVENT_FALLING_EDGE; +} + +#ifdef CONFIG_HTE + static enum hte_return process_hw_ts_thread(void *p) { struct line *line; struct linereq *lr; struct gpio_v2_line_event le; + u64 edflags; int level; - u64 eflags; if (!p) return HTE_CB_HANDLED; @@ -590,29 +607,26 @@ static enum hte_return process_hw_ts_thread(void *p) memset(&le, 0, sizeof(le)); le.timestamp_ns = line->timestamp_ns; - eflags = READ_ONCE(line->eflags); + edflags = READ_ONCE(line->edflags); - if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) { - if (line->raw_level >= 0) { - if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) - level = !line->raw_level; - else - level = line->raw_level; - } else { - level = gpiod_get_value_cansleep(line->desc); - } + switch (edflags & GPIO_V2_LINE_EDGE_FLAGS) { + case GPIO_V2_LINE_FLAG_EDGE_BOTH: + level = (line->raw_level >= 0) ? + line->raw_level : + gpiod_get_raw_value_cansleep(line->desc); - if (level) - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { - /* Emit low-to-high event */ + if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) + level = !level; + + le.id = line_event_id(level); + break; + case GPIO_V2_LINE_FLAG_EDGE_RISING: le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { - /* Emit high-to-low event */ + break; + case GPIO_V2_LINE_FLAG_EDGE_FALLING: le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else { + break; + default: return HTE_CB_HANDLED; } le.line_seqno = line->line_seqno; @@ -659,12 +673,47 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) return HTE_CB_HANDLED; } +static int hte_edge_setup(struct line *line, u64 eflags) +{ + int ret; + unsigned long flags = 0; + struct hte_ts_desc *hdesc = &line->hdesc; + + if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING) + flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? + HTE_FALLING_EDGE_TS : + HTE_RISING_EDGE_TS; + if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING) + flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? + HTE_RISING_EDGE_TS : + HTE_FALLING_EDGE_TS; + + line->total_discard_seq = 0; + + hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags, NULL, + line->desc); + + ret = hte_ts_get(NULL, hdesc, 0); + if (ret) + return ret; + + return hte_request_ts_ns(hdesc, process_hw_ts, process_hw_ts_thread, + line); +} + +#else + +static int hte_edge_setup(struct line *line, u64 eflags) +{ + return 0; +} +#endif /* CONFIG_HTE */ + static irqreturn_t edge_irq_thread(int irq, void *p) { struct line *line = p; struct linereq *lr = line->req; struct gpio_v2_line_event le; - u64 eflags; /* Do not leak kernel stack to userspace */ memset(&le, 0, sizeof(le)); @@ -683,23 +732,17 @@ static irqreturn_t edge_irq_thread(int irq, void *p) } line->timestamp_ns = 0; - eflags = READ_ONCE(line->eflags); - if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) { - int level = gpiod_get_value_cansleep(line->desc); - - if (level) - /* Emit low-to-high event */ - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { - /* Emit low-to-high event */ + switch (READ_ONCE(line->edflags) & GPIO_V2_LINE_EDGE_FLAGS) { + case GPIO_V2_LINE_FLAG_EDGE_BOTH: + le.id = line_event_id(gpiod_get_value_cansleep(line->desc)); + break; + case GPIO_V2_LINE_FLAG_EDGE_RISING: le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { - /* Emit high-to-low event */ + break; + case GPIO_V2_LINE_FLAG_EDGE_FALLING: le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else { + break; + default: return IRQ_NONE; } line->line_seqno++; @@ -764,16 +807,16 @@ static void debounce_work_func(struct work_struct *work) struct gpio_v2_line_event le; struct line *line = container_of(work, struct line, work.work); struct linereq *lr; - int level, diff_seqno; - u64 eflags; + u64 eflags, edflags = READ_ONCE(line->edflags); + int level = -1; +#ifdef CONFIG_HTE + int diff_seqno; - if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { + if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) level = line->raw_level; - if (level < 0) - level = gpiod_get_raw_value_cansleep(line->desc); - } else { +#endif + if (level < 0) level = gpiod_get_raw_value_cansleep(line->desc); - } if (level < 0) { pr_debug_ratelimited("debouncer failed to read line value\n"); return; @@ -785,12 +828,12 @@ static void debounce_work_func(struct work_struct *work) WRITE_ONCE(line->level, level); /* -- edge detection -- */ - eflags = READ_ONCE(line->eflags); + eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; if (!eflags) return; /* switch from physical level to logical - if they differ */ - if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) + if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) level = !level; /* ignore edges that are not being monitored */ @@ -804,7 +847,8 @@ static void debounce_work_func(struct work_struct *work) lr = line->req; le.timestamp_ns = line_event_timestamp(line); le.offset = gpio_chip_hwgpio(line->desc); - if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { +#ifdef CONFIG_HTE + if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) { /* discard events except the last one */ line->total_discard_seq -= 1; diff_seqno = line->last_seqno - line->total_discard_seq - @@ -813,51 +857,21 @@ static void debounce_work_func(struct work_struct *work) le.line_seqno = line->line_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : atomic_add_return(diff_seqno, &lr->seqno); - } else { + } else +#endif /* CONFIG_HTE */ + { line->line_seqno++; le.line_seqno = line->line_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : atomic_inc_return(&lr->seqno); } - if (level) - /* Emit low-to-high event */ - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; + le.id = line_event_id(level); linereq_put_event(lr, &le); } -static int hte_edge_setup(struct line *line, u64 eflags) -{ - int ret; - unsigned long flags = 0; - struct hte_ts_desc *hdesc = &line->hdesc; - - if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING) - flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? - HTE_FALLING_EDGE_TS : HTE_RISING_EDGE_TS; - if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING) - flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? - HTE_RISING_EDGE_TS : HTE_FALLING_EDGE_TS; - - line->total_discard_seq = 0; - - hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags, - NULL, line->desc); - - ret = hte_ts_get(NULL, hdesc, 0); - if (ret) - return ret; - - return hte_request_ts_ns(hdesc, process_hw_ts, - process_hw_ts_thread, line); -} - -static int debounce_setup(struct line *line, - unsigned int debounce_period_us, bool hte_req) +static int debounce_setup(struct line *line, unsigned int debounce_period_us) { unsigned long irqflags; int ret, level, irq; @@ -877,7 +891,8 @@ static int debounce_setup(struct line *line, if (level < 0) return level; - if (!hte_req) { + if (!(IS_ENABLED(CONFIG_HTE) && + test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))) { irq = gpiod_to_irq(line->desc); if (irq < 0) return -ENXIO; @@ -889,9 +904,7 @@ static int debounce_setup(struct line *line, return ret; line->irq = irq; } else { - ret = hte_edge_setup(line, - GPIO_V2_LINE_FLAG_EDGE_RISING | - GPIO_V2_LINE_FLAG_EDGE_FALLING); + ret = hte_edge_setup(line, GPIO_V2_LINE_FLAG_EDGE_BOTH); if (ret) return ret; } @@ -930,19 +943,21 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc, return 0; } -static void edge_detector_stop(struct line *line, bool hte_en) +static void edge_detector_stop(struct line *line) { - if (line->irq && !hte_en) { + if (line->irq) { free_irq(line->irq, line); line->irq = 0; } - if (hte_en) +#ifdef CONFIG_HTE + if (READ_ONCE(line->edflags) & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) hte_ts_put(&line->hdesc); +#endif cancel_delayed_work_sync(&line->work); WRITE_ONCE(line->sw_debounced, 0); - WRITE_ONCE(line->eflags, 0); + WRITE_ONCE(line->edflags, 0); if (line->desc) WRITE_ONCE(line->desc->debounce_period_us, 0); /* do not change line->level - see comment in debounced_value() */ @@ -950,23 +965,23 @@ static void edge_detector_stop(struct line *line, bool hte_en) static int edge_detector_setup(struct line *line, struct gpio_v2_line_config *lc, - unsigned int line_idx, - u64 eflags, bool hte_req) + unsigned int line_idx, u64 edflags) { u32 debounce_period_us; unsigned long irqflags = 0; + u64 eflags; int irq, ret; + eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; if (eflags && !kfifo_initialized(&line->req->events)) { ret = kfifo_alloc(&line->req->events, line->req->event_buffer_size, GFP_KERNEL); if (ret) return ret; } - WRITE_ONCE(line->eflags, eflags); if (gpio_v2_line_config_debounced(lc, line_idx)) { debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); - ret = debounce_setup(line, debounce_period_us, hte_req); + ret = debounce_setup(line, debounce_period_us); if (ret) return ret; WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); @@ -976,8 +991,9 @@ static int edge_detector_setup(struct line *line, if (!eflags || READ_ONCE(line->sw_debounced)) return 0; - if (hte_req) - return hte_edge_setup(line, eflags); + if (IS_ENABLED(CONFIG_HTE) && + (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) + return hte_edge_setup(line, edflags); irq = gpiod_to_irq(line->desc); if (irq < 0) @@ -1003,35 +1019,29 @@ static int edge_detector_setup(struct line *line, static int edge_detector_update(struct line *line, struct gpio_v2_line_config *lc, - unsigned int line_idx, - u64 flags, bool polarity_change, - bool prev_hte_flag) + unsigned int line_idx, u64 edflags) { - u64 eflags = flags & GPIO_V2_LINE_EDGE_FLAGS; + u64 active_edflags = READ_ONCE(line->edflags); unsigned int debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); - bool hte_change = (prev_hte_flag != - ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) != 0)); - if ((READ_ONCE(line->eflags) == eflags) && !polarity_change && - (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us) - && !hte_change) + if ((active_edflags == edflags) && + (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)) return 0; /* sw debounced and still will be...*/ if (debounce_period_us && READ_ONCE(line->sw_debounced)) { - WRITE_ONCE(line->eflags, eflags); WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); return 0; } /* reconfiguring edge detection or sw debounce being disabled */ - if ((line->irq && !READ_ONCE(line->sw_debounced)) || prev_hte_flag || + if ((line->irq && !READ_ONCE(line->sw_debounced)) || + (active_edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) || (!debounce_period_us && READ_ONCE(line->sw_debounced))) - edge_detector_stop(line, prev_hte_flag); + edge_detector_stop(line); - return edge_detector_setup(line, lc, line_idx, eflags, - flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); + return edge_detector_setup(line, lc, line_idx, edflags); } static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc, @@ -1067,6 +1077,11 @@ static int gpio_v2_line_flags_validate(u64 flags) /* Return an error if an unknown flag is set */ if (flags & ~GPIO_V2_LINE_VALID_FLAGS) return -EINVAL; + + if (!IS_ENABLED(CONFIG_HTE) && + (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) + return -EOPNOTSUPP; + /* * Do not allow both INPUT and OUTPUT flags to be set as they are * contradictory. @@ -1076,7 +1091,8 @@ static int gpio_v2_line_flags_validate(u64 flags) return -EINVAL; /* Only allow one event clock source */ - if ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) && + if (IS_ENABLED(CONFIG_HTE) && + (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) && (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) return -EINVAL; @@ -1300,22 +1316,17 @@ static long linereq_set_config_unlocked(struct linereq *lr, struct gpio_v2_line_config *lc) { struct gpio_desc *desc; + struct line *line; unsigned int i; - u64 flags; - bool polarity_change; - bool prev_hte_flag; + u64 flags, edflags; int ret; for (i = 0; i < lr->num_lines; i++) { + line = &lr->lines[i]; desc = lr->lines[i].desc; flags = gpio_v2_line_config_flags(lc, i); - polarity_change = - (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) != - ((flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) != 0)); - - prev_hte_flag = !!test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags); - gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags); + edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* * Lines have to be requested explicitly for input * or output, else the line will be treated "as is". @@ -1323,7 +1334,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { int val = gpio_v2_line_config_output_value(lc, i); - edge_detector_stop(&lr->lines[i], prev_hte_flag); + edge_detector_stop(line); ret = gpiod_direction_output(desc, val); if (ret) return ret; @@ -1332,12 +1343,13 @@ static long linereq_set_config_unlocked(struct linereq *lr, if (ret) return ret; - ret = edge_detector_update(&lr->lines[i], lc, i, - flags, polarity_change, prev_hte_flag); + ret = edge_detector_update(line, lc, i, edflags); if (ret) return ret; } + WRITE_ONCE(line->edflags, edflags); + blocking_notifier_call_chain(&desc->gdev->notifier, GPIO_V2_LINE_CHANGED_CONFIG, desc); @@ -1464,15 +1476,12 @@ static ssize_t linereq_read(struct file *file, static void linereq_free(struct linereq *lr) { unsigned int i; - bool hte = false; for (i = 0; i < lr->num_lines; i++) { - if (lr->lines[i].desc) - hte = !!test_bit(FLAG_EVENT_CLOCK_HTE, - &lr->lines[i].desc->flags); - edge_detector_stop(&lr->lines[i], hte); - if (lr->lines[i].desc) + if (lr->lines[i].desc) { + edge_detector_stop(&lr->lines[i]); gpiod_free(lr->lines[i].desc); + } } kfifo_free(&lr->events); kfree(lr->label); @@ -1506,7 +1515,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) struct gpio_v2_line_config *lc; struct linereq *lr; struct file *file; - u64 flags; + u64 flags, edflags; unsigned int i; int fd, ret; @@ -1580,6 +1589,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) if (ret < 0) goto out_free_linereq; + edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* * Lines have to be requested explicitly for input * or output, else the line will be treated "as is". @@ -1596,12 +1606,13 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) goto out_free_linereq; ret = edge_detector_setup(&lr->lines[i], lc, i, - flags & GPIO_V2_LINE_EDGE_FLAGS, - flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); + edflags); if (ret) goto out_free_linereq; } + lr->lines[i].edflags = edflags; + blocking_notifier_call_chain(&desc->gdev->notifier, GPIO_V2_LINE_CHANGED_REQUESTED, desc); diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 79da85d17b71..16a696249229 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -375,9 +375,6 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) } EXPORT_SYMBOL_GPL(devm_gpiod_put_array); - - - static void devm_gpio_release(struct device *dev, void *res) { unsigned *gpio = res; @@ -385,13 +382,6 @@ static void devm_gpio_release(struct device *dev, void *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 @@ -402,11 +392,7 @@ static int devm_gpio_match(struct device *dev, void *res, void *data) * 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; @@ -459,24 +445,6 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio, } EXPORT_SYMBOL_GPL(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_GPL(devm_gpio_free); - static void devm_gpio_chip_release(void *data) { struct gpio_chip *gc = data; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 3d6c3ffd5576..a037b50bef33 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -354,6 +354,9 @@ struct gpio_desc *gpiod_get_from_of_node(const struct device_node *node, if (flags & OF_GPIO_PULL_DOWN) lflags |= GPIO_PULL_DOWN; + if (flags & OF_GPIO_PULL_DISABLE) + lflags |= GPIO_PULL_DISABLE; + ret = gpiod_configure_flags(desc, propname, lflags, dflags); if (ret < 0) { gpiod_put(desc); @@ -556,6 +559,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, *flags |= GPIO_PULL_UP; if (of_flags & OF_GPIO_PULL_DOWN) *flags |= GPIO_PULL_DOWN; + if (of_flags & OF_GPIO_PULL_DISABLE) + *flags |= GPIO_PULL_DISABLE; return desc; } @@ -621,6 +626,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, *lflags |= GPIO_PULL_UP; if (xlate_flags & OF_GPIO_PULL_DOWN) *lflags |= GPIO_PULL_DOWN; + if (xlate_flags & OF_GPIO_PULL_DISABLE) + *lflags |= GPIO_PULL_DISABLE; if (of_property_read_bool(np, "input")) *dflags |= GPIOD_IN; @@ -720,7 +727,7 @@ static void of_gpiochip_remove_hog(struct gpio_chip *chip, static int of_gpiochip_match_node(struct gpio_chip *chip, void *data) { - return chip->gpiodev->dev.of_node == data; + return device_match_of_node(&chip->gpiodev->dev, data); } static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np) @@ -860,7 +867,8 @@ int of_mm_gpiochip_add_data(struct device_node *np, if (mm_gc->save_regs) mm_gc->save_regs(mm_gc); - mm_gc->gc.of_node = np; + of_node_put(mm_gc->gc.of_node); + mm_gc->gc.of_node = of_node_get(np); ret = gpiochip_add_data(gc, data); if (ret) @@ -868,6 +876,7 @@ int of_mm_gpiochip_add_data(struct device_node *np, return 0; err2: + of_node_put(np); iounmap(mm_gc->regs); err1: kfree(gc->label); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 68d9f95d7799..cc9c0a12259e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3942,9 +3942,11 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) { + if (((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) || + ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DISABLE)) || + ((lflags & GPIO_PULL_DOWN) && (lflags & GPIO_PULL_DISABLE))) { gpiod_err(desc, - "both pull-up and pull-down enabled, invalid configuration\n"); + "multiple pull-up, pull-down or pull-disable enabled, invalid configuration\n"); return -EINVAL; } @@ -3952,6 +3954,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, set_bit(FLAG_PULL_UP, &desc->flags); else if (lflags & GPIO_PULL_DOWN) set_bit(FLAG_PULL_DOWN, &desc->flags); + else if (lflags & GPIO_PULL_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); ret = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); if (ret < 0) diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index 8c3832a58ef6..ac1d18039568 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -72,11 +72,9 @@ static int ucb1400_core_probe(struct device *dev) /* GPIO */ ucb_gpio.ac97 = ac97; - if (pdata) { - ucb_gpio.gpio_setup = pdata->gpio_setup; - ucb_gpio.gpio_teardown = pdata->gpio_teardown; + if (pdata) ucb_gpio.gpio_offset = pdata->gpio_offset; - } + ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1); if (!ucb->ucb1400_gpio) { err = -ENOMEM; diff --git a/include/dt-bindings/gpio/gpio.h b/include/dt-bindings/gpio/gpio.h index c029467e828b..5566e58196a2 100644 --- a/include/dt-bindings/gpio/gpio.h +++ b/include/dt-bindings/gpio/gpio.h @@ -39,4 +39,7 @@ /* Bit 5 express pull down */ #define GPIO_PULL_DOWN 32 +/* Bit 6 express pull disable */ +#define GPIO_PULL_DISABLE 64 + #endif diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 008ad3ee56b7..a370387fa406 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -95,7 +95,6 @@ struct device; int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); int devm_gpio_request_one(struct device *dev, unsigned gpio, unsigned long flags, const char *label); -void devm_gpio_free(struct device *dev, unsigned int gpio); #else /* ! CONFIG_GPIOLIB */ @@ -240,11 +239,6 @@ static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, return -EINVAL; } -static inline void devm_gpio_free(struct device *dev, unsigned int gpio) -{ - WARN_ON(1); -} - #endif /* ! CONFIG_GPIOLIB */ #endif /* __LINUX_GPIO_H */ diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index 4d55da28e664..0b619eb7ae83 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -14,6 +14,7 @@ enum gpio_lookup_flags { GPIO_TRANSITORY = (1 << 3), GPIO_PULL_UP = (1 << 4), GPIO_PULL_DOWN = (1 << 5), + GPIO_PULL_DISABLE = (1 << 6), GPIO_LOOKUP_FLAGS_DEFAULT = GPIO_ACTIVE_HIGH | GPIO_PERSISTENT, }; diff --git a/include/linux/mfd/twl.h b/include/linux/mfd/twl.h index 8871cc5188a0..c8cd31756037 100644 --- a/include/linux/mfd/twl.h +++ b/include/linux/mfd/twl.h @@ -594,8 +594,6 @@ struct twl4030_gpio_platform_data { int (*setup)(struct device *dev, unsigned gpio, unsigned ngpio); - int (*teardown)(struct device *dev, - unsigned gpio, unsigned ngpio); }; struct twl4030_madc_platform_data { diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 8bf2ea859653..a5166eb93437 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -29,6 +29,7 @@ enum of_gpio_flags { OF_GPIO_TRANSITORY = 0x8, OF_GPIO_PULL_UP = 0x10, OF_GPIO_PULL_DOWN = 0x20, + OF_GPIO_PULL_DISABLE = 0x40, }; #ifdef CONFIG_OF_GPIO diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h index 0968ef458447..22345391350b 100644 --- a/include/linux/ucb1400.h +++ b/include/linux/ucb1400.h @@ -84,8 +84,6 @@ struct ucb1400_gpio { struct gpio_chip gc; struct snd_ac97 *ac97; int gpio_offset; - int (*gpio_setup)(struct device *dev, int ngpio); - int (*gpio_teardown)(struct device *dev, int ngpio); }; struct ucb1400_ts { |