mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
gpio: updates for v6.0-rc1
- 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmLruBoACgkQEacuoBRx 13LSqA//QMdrdsYOvSp3m6Dy1swj8a2VpeInDclx/JQ51hIsv03lW6sysrRBBKfy gslkj0KO+kelEQbcHZdXF6f434Y2QqSU/JRCPQlQ55Uo3vSbUulvVkUtSoegdNKG airr5KebZtLzjBgc23n38HiTJxa1J238+3UScxYHqL9jQ6AA6sPx7Kpy2zlTwojn iygJ1CKuyMyHOjU1uhAWYVzCAoguVvOb58emUt5HUsOjjW42d8T+iCHxrJnjC3ST YWwHnkSd3GO5CLI+5w7MmLk4kaOA8KU7PGRljglwpbsNGknUQ3PFFSlqFUziBzMU nOG1gZ9bvzOy5xjFcLkT3p/NHZiTnyq+ugDl2RAVQB2UF31KHk2sVGrzIsRpbBgt kDst5Wn21oymfEO6FM269h5ln+haXouJv2eQvnayBr3rfMxaZCm8veFxjQBDRADf D3muvi6u/EJPsPg08owcaVrINPVYVGQIzQp5hi+UCBkzXghn+MovNuI/i07Qf1kr fBELOXTy+MGK22p+rO+rXsp0Cp1zUIbwSz0m8ImbhLqcYLa+Vm5bJHk31/Igvbv3 9FMR75RmfE98EvMhd6ECarZHF9rvCVN7R1U9P1aK8+85m7X5eIVehoQ125uAZf+N +W49bceSCI/mGqIg8MiQCM5NIW0AXvyjd7gTNN5kr7qsMGTJI3c= =rGNU -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
37644cac6e
@ -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:
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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>;
|
||||
};
|
146
Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml
Normal file
146
Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml
Normal file
@ -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>;
|
||||
};
|
@ -13,6 +13,7 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nxp,pca9570
|
||||
- nxp,pca9571
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -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>;
|
||||
};
|
51
Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml
Normal file
51
Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml
Normal file
@ -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";
|
||||
};
|
||||
};
|
@ -48,11 +48,9 @@ properties:
|
||||
- renesas,gpio-r8a77995 # R-Car D3
|
||||
- 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
@ -33,73 +37,62 @@ static unsigned int irq[MAX_NUM_IDI_48];
|
||||
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;
|
||||
void __iomem *const ppi = idi48gpio->reg;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
void __iomem *const ppi = idi48gpio->reg;
|
||||
|
||||
/* 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 = 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;
|
||||
spin_lock_irqsave(&idi48gpio->lock, flags);
|
||||
|
||||
idi48gpio->irq_mask[boundary] &= ~mask;
|
||||
idi48gpio->irq_mask[boundary] &= ~mask;
|
||||
|
||||
if (!idi48gpio->irq_mask[boundary]) {
|
||||
idi48gpio->cos_enb &= ~BIT(boundary);
|
||||
/* Exit early if there are still input lines with IRQ unmasked */
|
||||
if (idi48gpio->irq_mask[boundary])
|
||||
goto exit;
|
||||
|
||||
raw_spin_lock_irqsave(&idi48gpio->lock, flags);
|
||||
idi48gpio->cos_enb &= ~BIT(boundary);
|
||||
|
||||
iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7);
|
||||
iowrite8(idi48gpio->cos_enb, &idi48gpio->reg->irq);
|
||||
|
||||
raw_spin_unlock_irqrestore(&idi48gpio->lock, flags);
|
||||
}
|
||||
|
||||
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);
|
||||
spin_lock(&idi48gpio->lock);
|
||||
|
||||
raw_spin_lock(&idi48gpio->lock);
|
||||
|
||||
cos_status = ioread8(idi48gpio->base + 7);
|
||||
|
||||
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) {
|
||||
|
@ -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)
|
||||
@ -32,19 +33,42 @@ static unsigned int irq[MAX_NUM_IDIO_16];
|
||||
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;
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
287
drivers/gpio/gpio-i8255.c
Normal file
287
drivers/gpio/gpio-i8255.c
Normal file
@ -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");
|
46
drivers/gpio/gpio-i8255.h
Normal file
46
drivers/gpio/gpio-i8255.h
Normal file
@ -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_ */
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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[] = {
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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);
|
@ -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];
|
||||
@ -30,6 +30,20 @@ static unsigned int irq[MAX_NUM_WS16C48];
|
||||
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
|
||||
@ -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);
|
||||
|
||||
iowrite8(0x80, ws16c48gpio->base + 7);
|
||||
iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
|
||||
iowrite8(0xC0, ws16c48gpio->base + 7);
|
||||
/* Select Register Page 2; Unlock all I/O ports */
|
||||
iowrite8(0x80, &ws16c48gpio->reg->page_lock);
|
||||
|
||||
/* 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);
|
||||
|
||||
iowrite8(0x80, ws16c48gpio->base + 7);
|
||||
iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
|
||||
iowrite8(0xC0, ws16c48gpio->base + 7);
|
||||
/* Select Register Page 2; Unlock all I/O ports */
|
||||
iowrite8(0x80, &ws16c48gpio->reg->page_lock);
|
||||
|
||||
/* 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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user