gpio: updates for v6.2
GPIO core: - teach gpiolib to work with software nodes for HW description - remove ARCH_NR_GPIOS treewide as we no longer impose any limit on the number of GPIOS since the allocation became entirely dynamic - add support for HW quirks for Cirrus CS42L56 codec, Marvell NFC controller, Freescale PCIe and Ethernet controller, Himax LCDs and Mediatek mt2701 - refactor OF quirk code - some general refactoring of the OF and ACPI code, adding new helpers, minor tweaks and fixes, making fwnode usage consistent etc. GPIO uAPI: - fix an issue where the user-space can trigger a NULL-pointer dereference in the kernel by opening a device file, forcing a driver unbind and then calling one of the syscalls on the associated file descriptor New drivers: - add gpio-latch: a new GPIO multiplexer based on latches connected to other GPIOs Driver updates: - convert i2c GPIO expanders to using .probe_new() - drop the gpio-sta2x11 driver - factor out common code for the ACCES IDIO-16 family of controllers and use this new library wherever applicable in drivers - add DT support to gpio-hisi - allow building gpio-davinci as a module and increase its maxItems property - add support for a new model to gpio-pca9570 - other minor changes to various drivers -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmObAGkACgkQEacuoBRx 13Jrew//VWgqyLgfOysJ5hdVQigY3KGEPbai2nXQK58HFymdBer2MG/G27j0aw46 mEgwYcrDKO4fi08AzCXexF/JYFZha7s4EwujJ/uRmye7xtVgs1xlaPPhTtFV2Iky P2994k1IhsScou5Tu9WZmHyeGLhiMleuBe+KbL4Xhfa1JYUhQymiQi8aiBGs7fW3 aMTtTa/7NpDl3YFNS+un7Ahuftj1CfwGYOiWeQy+Fy1UE5uE/UgvmiSYi/3rvrCQ O/WVWgd26sTKyGb92nrbHjY2DPr5ULAC8aRY3JQ1pmfyPpTuqNUtb+CUYjP/oxqx JjZms96YW7B7sL93SNWog+9ZyYr+jnfdg+ZgGDEZ1ViGXgoe/Fr+xs6tRwww8GL4 Bt3nAlAR/X2Udarlmep4Udca5BOr2kc7JmcVEvNrVJAI7wGxo3SKWdIWcgs43e0B Ps3iJmdK4ndzHh4jrcZEzZUXpmOSHzpiW/YuqPd/9XNpJowhT2BObukRlAcVZqjf PvyN2nktF45fqjuszBo0GK9QZv0DUofgkUxYgEpdIvLwfvodJVoFbK5KOI0Kqxfc CJxuAgKgEI569iEguEj7+pF5c1VW5LWJRV2kG6XbxwXKn2c+47/HkvvrR34sLu9n +7yp4x5BflVQiQsrbDfQiYXOz8jb8tWgn1o1LIQyYkUan4zCjjk= =zg1O -----END PGP SIGNATURE----- Merge tag 'gpio-updates-for-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux Pull gpio updates from Bartosz Golaszewski: "We have a new GPIO multiplexer driver, bunch of driver updates and refactoring in the core GPIO library. GPIO core: - teach gpiolib to work with software nodes for HW description - remove ARCH_NR_GPIOS treewide as we no longer impose any limit on the number of GPIOS since the allocation became entirely dynamic - add support for HW quirks for Cirrus CS42L56 codec, Marvell NFC controller, Freescale PCIe and Ethernet controller, Himax LCDs and Mediatek mt2701 - refactor OF quirk code - some general refactoring of the OF and ACPI code, adding new helpers, minor tweaks and fixes, making fwnode usage consistent etc. GPIO uAPI: - fix an issue where the user-space can trigger a NULL-pointer dereference in the kernel by opening a device file, forcing a driver unbind and then calling one of the syscalls on the associated file descriptor New drivers: - add gpio-latch: a new GPIO multiplexer based on latches connected to other GPIOs Driver updates: - convert i2c GPIO expanders to using .probe_new() - drop the gpio-sta2x11 driver - factor out common code for the ACCES IDIO-16 family of controllers and use this new library wherever applicable in drivers - add DT support to gpio-hisi - allow building gpio-davinci as a module and increase its maxItems property - add support for a new model to gpio-pca9570 - other minor changes to various drivers" * tag 'gpio-updates-for-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (66 commits) gpio: sim: set a limit on the number of GPIOs gpiolib: protect the GPIO device against being dropped while in use by user-space gpiolib: cdev: fix NULL-pointer dereferences gpiolib: Provide to_gpio_device() helper gpiolib: Unify access to the device properties gpio: Do not include <linux/kernel.h> when not really needed. gpio: pcf857x: Convert to i2c's .probe_new() gpio: pca953x: Convert to i2c's .probe_new() gpio: max732x: Convert to i2c's .probe_new() dt-bindings: gpio: gpio-davinci: Increase maxItems in gpio-line-names gpiolib: ensure that fwnode is properly set gpio: sl28cpld: Replace irqchip mask_invert with unmask_base gpiolib: of: Use correct fwnode for DT-probed chips gpiolib: of: Drop redundant check in of_mm_gpiochip_remove() gpiolib: of: Prepare of_mm_gpiochip_add_data() for fwnode gpiolib: add support for software nodes gpiolib: consolidate GPIO lookups gpiolib: acpi: avoid leaking ACPI details into upper gpiolib layers gpiolib: acpi: teach acpi_find_gpio() to handle data-only nodes gpiolib: acpi: change acpi_find_gpio() to accept firmware node ...
This commit is contained in:
commit
c0f234ff90
|
@ -35,7 +35,7 @@ properties:
|
|||
gpio-line-names:
|
||||
description: strings describing the names of each gpio line.
|
||||
minItems: 1
|
||||
maxItems: 100
|
||||
maxItems: 144
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/gpio/gpio-latch.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: GPIO latch controller
|
||||
|
||||
maintainers:
|
||||
- Sascha Hauer <s.hauer@pengutronix.de>
|
||||
|
||||
description: |
|
||||
This binding describes a GPIO multiplexer based on latches connected to
|
||||
other GPIOs, like this:
|
||||
|
||||
CLK0 ----------------------. ,--------.
|
||||
CLK1 -------------------. `--------|> #0 |
|
||||
| | |
|
||||
OUT0 ----------------+--|-----------|D0 Q0|-----|<
|
||||
OUT1 --------------+-|--|-----------|D1 Q1|-----|<
|
||||
OUT2 ------------+-|-|--|-----------|D2 Q2|-----|<
|
||||
OUT3 ----------+-|-|-|--|-----------|D3 Q3|-----|<
|
||||
OUT4 --------+-|-|-|-|--|-----------|D4 Q4|-----|<
|
||||
OUT5 ------+-|-|-|-|-|--|-----------|D5 Q5|-----|<
|
||||
OUT6 ----+-|-|-|-|-|-|--|-----------|D6 Q6|-----|<
|
||||
OUT7 --+-|-|-|-|-|-|-|--|-----------|D7 Q7|-----|<
|
||||
| | | | | | | | | `--------'
|
||||
| | | | | | | | |
|
||||
| | | | | | | | | ,--------.
|
||||
| | | | | | | | `-----------|> #1 |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | `--------------|D0 Q0|-----|<
|
||||
| | | | | | `----------------|D1 Q1|-----|<
|
||||
| | | | | `------------------|D2 Q2|-----|<
|
||||
| | | | `--------------------|D3 Q3|-----|<
|
||||
| | | `----------------------|D4 Q4|-----|<
|
||||
| | `------------------------|D5 Q5|-----|<
|
||||
| `--------------------------|D6 Q6|-----|<
|
||||
`----------------------------|D7 Q7|-----|<
|
||||
`--------'
|
||||
|
||||
The number of clk-gpios and latched-gpios is not fixed. The actual number
|
||||
of number of latches and the number of inputs per latch is derived from
|
||||
the number of GPIOs given in the corresponding device tree properties.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: gpio-latch
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
clk-gpios:
|
||||
description: Array of GPIOs to be used to clock a latch
|
||||
|
||||
latched-gpios:
|
||||
description: Array of GPIOs to be used as inputs per latch
|
||||
|
||||
setup-duration-ns:
|
||||
description: Delay in nanoseconds to wait after the latch inputs have been
|
||||
set up
|
||||
|
||||
clock-duration-ns:
|
||||
description: Delay in nanoseconds to wait between clock output changes
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
gpio-line-names: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#gpio-cells"
|
||||
- gpio-controller
|
||||
- clk-gpios
|
||||
- latched-gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
gpio-latch {
|
||||
#gpio-cells = <2>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_di_do_leds>;
|
||||
compatible = "gpio-latch";
|
||||
gpio-controller;
|
||||
setup-duration-ns = <100>;
|
||||
clock-duration-ns = <100>;
|
||||
|
||||
clk-gpios = <&gpio3 7 0>, <&gpio3 8 0>;
|
||||
latched-gpios = <&gpio3 21 0>, <&gpio3 22 0>,
|
||||
<&gpio3 23 0>, <&gpio3 24 0>,
|
||||
<&gpio3 25 0>, <&gpio3 26 0>,
|
||||
<&gpio3 27 0>, <&gpio3 28 0>;
|
||||
};
|
|
@ -12,6 +12,7 @@ maintainers:
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- dlg,slg7xl45106
|
||||
- nxp,pca9570
|
||||
- nxp,pca9571
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/gpio/hisilicon,ascend910-gpio.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: HiSilicon common GPIO controller
|
||||
|
||||
maintainers:
|
||||
- Jay Fang <f.fangjian@huawei.com>
|
||||
|
||||
description:
|
||||
The HiSilicon common GPIO controller can be used for many different
|
||||
types of SoC such as Huawei Ascend AI series chips.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: hisilicon,ascend910-gpio
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
ngpios:
|
||||
minimum: 1
|
||||
maximum: 32
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- gpio-controller
|
||||
- "#gpio-cells"
|
||||
- ngpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
gpio@840d0000 {
|
||||
compatible = "hisilicon,ascend910-gpio";
|
||||
reg = <0x840d0000 0x1000>;
|
||||
ngpios = <32>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
|
@ -558,11 +558,6 @@ Platform Support
|
|||
To force-enable this framework, a platform's Kconfig will "select" GPIOLIB,
|
||||
else it is up to the user to configure support for GPIO.
|
||||
|
||||
It may also provide a custom value for ARCH_NR_GPIOS, so that it better
|
||||
reflects the number of GPIOs in actual use on that platform, without
|
||||
wasting static table space. (It should count both built-in/SoC GPIOs and
|
||||
also ones on GPIO expanders.
|
||||
|
||||
If neither of these options are selected, the platform does not support
|
||||
GPIOs through GPIO-lib and the code cannot be enabled by the user.
|
||||
|
||||
|
|
|
@ -312,6 +312,13 @@ L: linux-iio@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/counter/104-quad-8.c
|
||||
|
||||
ACCES IDIO-16 GPIO LIBRARY
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-idio-16.c
|
||||
F: drivers/gpio/gpio-idio-16.h
|
||||
|
||||
ACCES PCI-IDIO-16 GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
|
@ -9266,6 +9273,7 @@ HISILICON GPIO DRIVER
|
|||
M: Jay Fang <f.fangjian@huawei.com>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/gpio/hisilicon,ascend910-gpio.yaml
|
||||
F: drivers/gpio/gpio-hisi.c
|
||||
|
||||
HISILICON HIGH PERFORMANCE RSA ENGINE DRIVER (HPRE)
|
||||
|
|
|
@ -1158,27 +1158,6 @@ config ARM_PSCI
|
|||
0022A ("Power State Coordination Interface System Software on
|
||||
ARM processors").
|
||||
|
||||
# The GPIO number here must be sorted by descending number. In case of
|
||||
# a multiplatform kernel, we just want the highest value required by the
|
||||
# selected platforms.
|
||||
config ARCH_NR_GPIO
|
||||
int
|
||||
default 2048 if ARCH_INTEL_SOCFPGA
|
||||
default 1024 if ARCH_BRCMSTB || ARCH_RENESAS || ARCH_TEGRA || \
|
||||
ARCH_ZYNQ || ARCH_ASPEED
|
||||
default 512 if ARCH_EXYNOS || ARCH_KEYSTONE || SOC_OMAP5 || \
|
||||
SOC_DRA7XX || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210
|
||||
default 416 if ARCH_SUNXI
|
||||
default 392 if ARCH_U8500
|
||||
default 352 if ARCH_VT8500
|
||||
default 288 if ARCH_ROCKCHIP
|
||||
default 264 if MACH_H4700
|
||||
default 0
|
||||
help
|
||||
Maximum number of GPIOs in the system.
|
||||
|
||||
If unsure, leave the default value.
|
||||
|
||||
config HZ_FIXED
|
||||
int
|
||||
default 128 if SOC_AT91RM9200
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#ifndef _ARCH_ARM_GPIO_H
|
||||
#define _ARCH_ARM_GPIO_H
|
||||
|
||||
/* Note: this may rely upon the value of ARCH_NR_GPIOS set in mach/gpio.h */
|
||||
#include <asm-generic/gpio.h>
|
||||
|
||||
/* The trivial gpiolib dispatchers */
|
||||
|
|
|
@ -2168,18 +2168,6 @@ config STACKPROTECTOR_PER_TASK
|
|||
def_bool y
|
||||
depends on STACKPROTECTOR && CC_HAVE_STACKPROTECTOR_SYSREG
|
||||
|
||||
# The GPIO number here must be sorted by descending number. In case of
|
||||
# a multiplatform kernel, we just want the highest value required by the
|
||||
# selected platforms.
|
||||
config ARCH_NR_GPIO
|
||||
int
|
||||
default 2048 if ARCH_APPLE
|
||||
default 0
|
||||
help
|
||||
Maximum number of GPIOs in the system.
|
||||
|
||||
If unsure, leave the default value.
|
||||
|
||||
config UNWIND_PATCH_PAC_INTO_SCS
|
||||
bool "Enable shadow call stack dynamically using code patching"
|
||||
# needs Clang with https://reviews.llvm.org/D111780 incorporated
|
||||
|
|
|
@ -361,11 +361,6 @@ config ARCH_HAS_CPU_RELAX
|
|||
config ARCH_HIBERNATION_POSSIBLE
|
||||
def_bool y
|
||||
|
||||
config ARCH_NR_GPIO
|
||||
int
|
||||
default 1024 if X86_64
|
||||
default 512
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
|
||||
|
|
|
@ -109,6 +109,15 @@ config GPIO_REGMAP
|
|||
config GPIO_MAX730X
|
||||
tristate
|
||||
|
||||
config GPIO_IDIO_16
|
||||
tristate
|
||||
help
|
||||
Enables support for the idio-16 library functions. The idio-16 library
|
||||
provides functions to facilitate communication with devices within the
|
||||
ACCES IDIO-16 family such as the 104-IDIO-16 and the PCI-IDIO-16.
|
||||
|
||||
If built as a module its name will be gpio-idio-16.
|
||||
|
||||
menu "Memory mapped GPIO drivers"
|
||||
depends on HAS_IOMEM
|
||||
|
||||
|
@ -219,7 +228,7 @@ config GPIO_CLPS711X
|
|||
Say yes here to support GPIO on CLPS711X SoCs.
|
||||
|
||||
config GPIO_DAVINCI
|
||||
bool "TI Davinci/Keystone GPIO support"
|
||||
tristate "TI Davinci/Keystone GPIO support"
|
||||
default y if ARCH_DAVINCI
|
||||
depends on (ARM || ARM64) && (ARCH_DAVINCI || ARCH_KEYSTONE || ARCH_K3)
|
||||
help
|
||||
|
@ -310,7 +319,7 @@ config GPIO_GRGPIO
|
|||
|
||||
config GPIO_HISI
|
||||
tristate "HiSilicon GPIO controller driver"
|
||||
depends on (ARM64 && ACPI) || COMPILE_TEST
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
|
@ -600,14 +609,6 @@ config GPIO_SPRD
|
|||
help
|
||||
Say yes here to support Spreadtrum GPIO device.
|
||||
|
||||
config GPIO_STA2X11
|
||||
bool "STA2x11/ConneXt GPIO support"
|
||||
depends on MFD_STA2X11
|
||||
select GENERIC_IRQ_CHIP
|
||||
help
|
||||
Say yes here to support the STA2x11/ConneXt GPIO device.
|
||||
The GPIO module has 128 GPIO pins with alternate functions.
|
||||
|
||||
config GPIO_STP_XWAY
|
||||
bool "XWAY STP GPIOs"
|
||||
depends on SOC_XWAY || COMPILE_TEST
|
||||
|
@ -857,6 +858,7 @@ config GPIO_104_IDIO_16
|
|||
depends on PC104
|
||||
select ISA_BUS_API
|
||||
select GPIOLIB_IRQCHIP
|
||||
select GPIO_IDIO_16
|
||||
help
|
||||
Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16,
|
||||
104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, 104-IDO-8). The
|
||||
|
@ -1561,6 +1563,7 @@ config GPIO_PCH
|
|||
config GPIO_PCI_IDIO_16
|
||||
tristate "ACCES PCI-IDIO-16 GPIO support"
|
||||
select GPIOLIB_IRQCHIP
|
||||
select GPIO_IDIO_16
|
||||
help
|
||||
Enables GPIO support for the ACCES PCI-IDIO-16. An interrupt is
|
||||
generated when any of the inputs change state (low to high or high to
|
||||
|
@ -1681,6 +1684,12 @@ config GPIO_AGGREGATOR
|
|||
industrial control context, to be operated from userspace using
|
||||
the GPIO chardev interface.
|
||||
|
||||
config GPIO_LATCH
|
||||
tristate "GPIO latch driver"
|
||||
help
|
||||
Say yes here to enable a driver for GPIO multiplexers based on latches
|
||||
connected to other GPIOs.
|
||||
|
||||
config GPIO_MOCKUP
|
||||
tristate "GPIO Testing Driver"
|
||||
select IRQ_SIM
|
||||
|
|
|
@ -10,6 +10,7 @@ obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
|
|||
obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o
|
||||
obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
|
||||
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
|
||||
|
||||
# Device drivers. Generally keep list sorted alphabetically
|
||||
obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o
|
||||
|
@ -68,6 +69,7 @@ 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_IDIO_16) += gpio-idio-16.o
|
||||
obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o
|
||||
obj-$(CONFIG_GPIO_IMX_SCU) += gpio-imx-scu.o
|
||||
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
|
||||
|
@ -75,6 +77,7 @@ obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
|
|||
obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o
|
||||
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
|
||||
obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
|
||||
obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o
|
||||
obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o
|
||||
obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o
|
||||
obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o
|
||||
|
@ -140,7 +143,6 @@ obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o
|
|||
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
|
||||
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
|
||||
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
|
||||
obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
|
||||
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
|
||||
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
|
||||
obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
|
||||
|
|
|
@ -124,6 +124,13 @@ Work items:
|
|||
this with dry-coding and sending to maintainers to test
|
||||
|
||||
|
||||
Generic regmap GPIO
|
||||
|
||||
In the very similar way to Generic MMIO GPIO convert the users which can
|
||||
take advantage of using regmap over direct IO accessors. Note, even in
|
||||
MMIO case the regmap MMIO with gpio-regmap.c is preferable over gpio-mmio.c.
|
||||
|
||||
|
||||
GPIOLIB irqchip
|
||||
|
||||
The GPIOLIB irqchip is a helper irqchip for "simple cases" that should
|
||||
|
|
|
@ -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/bits.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
@ -21,6 +21,8 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "gpio-idio-16.h"
|
||||
|
||||
#define IDIO_16_EXTENT 8
|
||||
#define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT)
|
||||
|
||||
|
@ -34,49 +36,26 @@ static unsigned int num_irq;
|
|||
module_param_hw_array(irq, uint, irq, &num_irq, 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
|
||||
* @reg: I/O address offset for the device registers
|
||||
* @out_state: output bits state
|
||||
* @state: ACCES IDIO-16 device state
|
||||
*/
|
||||
struct idio_16_gpio {
|
||||
struct gpio_chip chip;
|
||||
raw_spinlock_t lock;
|
||||
unsigned long irq_mask;
|
||||
struct idio_16_reg __iomem *reg;
|
||||
unsigned int out_state;
|
||||
struct idio_16 __iomem *reg;
|
||||
struct idio_16_state state;
|
||||
};
|
||||
|
||||
static int idio_16_gpio_get_direction(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
if (offset > 15)
|
||||
if (idio_16_get_direction(offset))
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
|
@ -98,15 +77,8 @@ static int idio_16_gpio_direction_output(struct gpio_chip *chip,
|
|||
static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
const unsigned int mask = BIT(offset-16);
|
||||
|
||||
if (offset < 16)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset < 24)
|
||||
return !!(ioread8(&idio16gpio->reg->in0_7) & mask);
|
||||
|
||||
return !!(ioread8(&idio16gpio->reg->in8_15) & (mask>>8));
|
||||
return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset);
|
||||
}
|
||||
|
||||
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
|
||||
|
@ -114,11 +86,7 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
|
|||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
|
||||
*bits = 0;
|
||||
if (*mask & GENMASK(23, 16))
|
||||
*bits |= (unsigned long)ioread8(&idio16gpio->reg->in0_7) << 16;
|
||||
if (*mask & GENMASK(31, 24))
|
||||
*bits |= (unsigned long)ioread8(&idio16gpio->reg->in8_15) << 24;
|
||||
idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -127,44 +95,16 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
|||
int value)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
const unsigned int mask = BIT(offset);
|
||||
unsigned long flags;
|
||||
|
||||
if (offset > 15)
|
||||
return;
|
||||
|
||||
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
|
||||
|
||||
if (value)
|
||||
idio16gpio->out_state |= mask;
|
||||
else
|
||||
idio16gpio->out_state &= ~mask;
|
||||
|
||||
if (offset > 7)
|
||||
iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15);
|
||||
else
|
||||
iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7);
|
||||
|
||||
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
|
||||
idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value);
|
||||
}
|
||||
|
||||
static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
|
||||
|
||||
idio16gpio->out_state &= ~*mask;
|
||||
idio16gpio->out_state |= *mask & *bits;
|
||||
|
||||
if (*mask & 0xFF)
|
||||
iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7);
|
||||
if ((*mask >> 8) & 0xFF)
|
||||
iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15);
|
||||
|
||||
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
|
||||
idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
|
||||
}
|
||||
|
||||
static void idio_16_irq_ack(struct irq_data *data)
|
||||
|
@ -301,7 +241,10 @@ static int idio_16_probe(struct device *dev, unsigned int id)
|
|||
idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
|
||||
idio16gpio->chip.set = idio_16_gpio_set;
|
||||
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
|
||||
idio16gpio->out_state = 0xFFFF;
|
||||
|
||||
idio_16_state_init(&idio16gpio->state);
|
||||
/* FET off states are represented by bit values of "1" */
|
||||
bitmap_fill(idio16gpio->state.out_state, IDIO_16_NOUT);
|
||||
|
||||
girq = &idio16gpio->chip.irq;
|
||||
gpio_irq_chip_set_chip(girq, &idio_16_irqchip);
|
||||
|
@ -343,3 +286,4 @@ module_isa_driver_with_irq(idio_16_driver, num_idio_16, num_irq);
|
|||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(GPIO_IDIO_16);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define AGGREGATOR_MAX_GPIOS 512
|
||||
|
||||
/*
|
||||
* GPIO Aggregator sysfs interface
|
||||
|
@ -64,7 +65,7 @@ static int aggr_parse(struct gpio_aggregator *aggr)
|
|||
unsigned int i, n = 0;
|
||||
int error = 0;
|
||||
|
||||
bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL);
|
||||
bitmap = bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL);
|
||||
if (!bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -84,13 +85,13 @@ static int aggr_parse(struct gpio_aggregator *aggr)
|
|||
}
|
||||
|
||||
/* GPIO chip + offset(s) */
|
||||
error = bitmap_parselist(offsets, bitmap, ARCH_NR_GPIOS);
|
||||
error = bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS);
|
||||
if (error) {
|
||||
pr_err("Cannot parse %s: %d\n", offsets, error);
|
||||
goto free_bitmap;
|
||||
}
|
||||
|
||||
for_each_set_bit(i, bitmap, ARCH_NR_GPIOS) {
|
||||
for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) {
|
||||
error = aggr_add_gpio(aggr, name, i, &n);
|
||||
if (error)
|
||||
goto free_bitmap;
|
||||
|
|
|
@ -217,9 +217,6 @@ static int davinci_gpio_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (WARN_ON(ARCH_NR_GPIOS < ngpio))
|
||||
ngpio = ARCH_NR_GPIOS;
|
||||
|
||||
/*
|
||||
* If there are unbanked interrupts then the number of
|
||||
* interrupts is equal to number of gpios else all are banked so
|
||||
|
@ -730,3 +727,14 @@ static int __init davinci_gpio_drv_reg(void)
|
|||
return platform_driver_register(&davinci_gpio_driver);
|
||||
}
|
||||
postcore_initcall(davinci_gpio_drv_reg);
|
||||
|
||||
static void __exit davinci_gpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&davinci_gpio_driver);
|
||||
}
|
||||
module_exit(davinci_gpio_exit);
|
||||
|
||||
MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
|
||||
MODULE_DESCRIPTION("DAVINCI GPIO driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:gpio-davinci");
|
||||
|
|
|
@ -141,6 +141,7 @@ static const struct regmap_config exar_regmap_config = {
|
|||
.name = "exar-gpio",
|
||||
.reg_bits = 16,
|
||||
.val_bits = 8,
|
||||
.io_port = true,
|
||||
};
|
||||
|
||||
static int gpio_exar_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -277,7 +277,7 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
|
|||
dev_err(dev, "unable to init generic GPIO\n");
|
||||
goto dis_clk;
|
||||
}
|
||||
g->gc.label = "FTGPIO010";
|
||||
g->gc.label = dev_name(dev);
|
||||
g->gc.base = -1;
|
||||
g->gc.parent = dev;
|
||||
g->gc.owner = THIS_MODULE;
|
||||
|
|
|
@ -67,8 +67,7 @@ static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value)
|
|||
gw_pld_output8(gc, offset, value);
|
||||
}
|
||||
|
||||
static int gw_pld_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int gw_pld_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct gw_pld *gw;
|
||||
|
@ -126,7 +125,7 @@ static struct i2c_driver gw_pld_driver = {
|
|||
.name = "gw_pld",
|
||||
.of_match_table = gw_pld_dt_ids,
|
||||
},
|
||||
.probe = gw_pld_probe,
|
||||
.probe_new = gw_pld_probe,
|
||||
.id_table = gw_pld_id,
|
||||
};
|
||||
module_i2c_driver(gw_pld_driver);
|
||||
|
|
|
@ -221,6 +221,12 @@ static const struct acpi_device_id hisi_gpio_acpi_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, hisi_gpio_acpi_match);
|
||||
|
||||
static const struct of_device_id hisi_gpio_dts_match[] = {
|
||||
{ .compatible = "hisilicon,ascend910-gpio", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hisi_gpio_dts_match);
|
||||
|
||||
static void hisi_gpio_get_pdata(struct device *dev,
|
||||
struct hisi_gpio *hisi_gpio)
|
||||
{
|
||||
|
@ -311,6 +317,7 @@ static struct platform_driver hisi_gpio_driver = {
|
|||
.driver = {
|
||||
.name = HISI_GPIO_DRIVER_NAME,
|
||||
.acpi_match_table = hisi_gpio_acpi_match,
|
||||
.of_match_table = hisi_gpio_dts_match,
|
||||
},
|
||||
.probe = hisi_gpio_probe,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPIO library for the ACCES IDIO-16 family
|
||||
* Copyright (C) 2022 William Breathitt Gray
|
||||
*/
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "gpio-idio-16.h"
|
||||
|
||||
#define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_16
|
||||
|
||||
/**
|
||||
* idio_16_get - get signal value at signal offset
|
||||
* @reg: ACCES IDIO-16 device registers
|
||||
* @state: ACCES IDIO-16 device state
|
||||
* @offset: offset of signal to get
|
||||
*
|
||||
* Returns the signal value (0=low, 1=high) for the signal at @offset.
|
||||
*/
|
||||
int idio_16_get(struct idio_16 __iomem *const reg,
|
||||
struct idio_16_state *const state, const unsigned long offset)
|
||||
{
|
||||
const unsigned long mask = BIT(offset);
|
||||
|
||||
if (offset < IDIO_16_NOUT)
|
||||
return test_bit(offset, state->out_state);
|
||||
|
||||
if (offset < 24)
|
||||
return !!(ioread8(®->in0_7) & (mask >> IDIO_16_NOUT));
|
||||
|
||||
if (offset < 32)
|
||||
return !!(ioread8(®->in8_15) & (mask >> 24));
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(idio_16_get);
|
||||
|
||||
/**
|
||||
* idio_16_get_multiple - get multiple signal values at multiple signal offsets
|
||||
* @reg: ACCES IDIO-16 device registers
|
||||
* @state: ACCES IDIO-16 device state
|
||||
* @mask: mask of signals to get
|
||||
* @bits: bitmap to store signal values
|
||||
*
|
||||
* Stores in @bits the values (0=low, 1=high) for the signals defined by @mask.
|
||||
*/
|
||||
void idio_16_get_multiple(struct idio_16 __iomem *const reg,
|
||||
struct idio_16_state *const state,
|
||||
const unsigned long *const mask,
|
||||
unsigned long *const bits)
|
||||
{
|
||||
unsigned long flags;
|
||||
const unsigned long out_mask = GENMASK(IDIO_16_NOUT - 1, 0);
|
||||
|
||||
spin_lock_irqsave(&state->lock, flags);
|
||||
|
||||
bitmap_replace(bits, bits, state->out_state, &out_mask, IDIO_16_NOUT);
|
||||
if (*mask & GENMASK(23, 16))
|
||||
bitmap_set_value8(bits, ioread8(®->in0_7), 16);
|
||||
if (*mask & GENMASK(31, 24))
|
||||
bitmap_set_value8(bits, ioread8(®->in8_15), 24);
|
||||
|
||||
spin_unlock_irqrestore(&state->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(idio_16_get_multiple);
|
||||
|
||||
/**
|
||||
* idio_16_set - set signal value at signal offset
|
||||
* @reg: ACCES IDIO-16 device registers
|
||||
* @state: ACCES IDIO-16 device state
|
||||
* @offset: offset of signal to set
|
||||
* @value: value of signal to set
|
||||
*
|
||||
* Assigns output @value for the signal at @offset.
|
||||
*/
|
||||
void idio_16_set(struct idio_16 __iomem *const reg,
|
||||
struct idio_16_state *const state, const unsigned long offset,
|
||||
const unsigned long value)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (offset >= IDIO_16_NOUT)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&state->lock, flags);
|
||||
|
||||
__assign_bit(offset, state->out_state, value);
|
||||
if (offset < 8)
|
||||
iowrite8(bitmap_get_value8(state->out_state, 0), ®->out0_7);
|
||||
else
|
||||
iowrite8(bitmap_get_value8(state->out_state, 8), ®->out8_15);
|
||||
|
||||
spin_unlock_irqrestore(&state->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(idio_16_set);
|
||||
|
||||
/**
|
||||
* idio_16_set_multiple - set signal values at multiple signal offsets
|
||||
* @reg: ACCES IDIO-16 device registers
|
||||
* @state: ACCES IDIO-16 device state
|
||||
* @mask: mask of signals to set
|
||||
* @bits: bitmap of signal output values
|
||||
*
|
||||
* Assigns output values defined by @bits for the signals defined by @mask.
|
||||
*/
|
||||
void idio_16_set_multiple(struct idio_16 __iomem *const reg,
|
||||
struct idio_16_state *const state,
|
||||
const unsigned long *const mask,
|
||||
const unsigned long *const bits)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&state->lock, flags);
|
||||
|
||||
bitmap_replace(state->out_state, state->out_state, bits, mask,
|
||||
IDIO_16_NOUT);
|
||||
if (*mask & GENMASK(7, 0))
|
||||
iowrite8(bitmap_get_value8(state->out_state, 0), ®->out0_7);
|
||||
if (*mask & GENMASK(15, 8))
|
||||
iowrite8(bitmap_get_value8(state->out_state, 8), ®->out8_15);
|
||||
|
||||
spin_unlock_irqrestore(&state->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(idio_16_set_multiple);
|
||||
|
||||
/**
|
||||
* idio_16_state_init - initialize idio_16_state structure
|
||||
* @state: ACCES IDIO-16 device state
|
||||
*
|
||||
* Initializes the ACCES IDIO-16 device @state for use in idio-16 library
|
||||
* functions.
|
||||
*/
|
||||
void idio_16_state_init(struct idio_16_state *const state)
|
||||
{
|
||||
spin_lock_init(&state->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(idio_16_state_init);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray");
|
||||
MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,71 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright 2022 William Breathitt Gray */
|
||||
#ifndef _IDIO_16_H_
|
||||
#define _IDIO_16_H_
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* struct idio_16 - IDIO-16 registers structure
|
||||
* @out0_7: Read: FET Drive Outputs 0-7
|
||||
* Write: FET Drive Outputs 0-7
|
||||
* @in0_7: Read: Isolated Inputs 0-7
|
||||
* Write: Clear Interrupt
|
||||
* @irq_ctl: Read: Enable IRQ
|
||||
* Write: Disable IRQ
|
||||
* @filter_ctl: Read: Activate Input Filters 0-15
|
||||
* Write: Deactivate Input Filters 0-15
|
||||
* @out8_15: Read: FET Drive Outputs 8-15
|
||||
* Write: FET Drive Outputs 8-15
|
||||
* @in8_15: Read: Isolated Inputs 8-15
|
||||
* Write: Unused
|
||||
* @irq_status: Read: Interrupt status
|
||||
* Write: Unused
|
||||
*/
|
||||
struct idio_16 {
|
||||
u8 out0_7;
|
||||
u8 in0_7;
|
||||
u8 irq_ctl;
|
||||
u8 filter_ctl;
|
||||
u8 out8_15;
|
||||
u8 in8_15;
|
||||
u8 irq_status;
|
||||
};
|
||||
|
||||
#define IDIO_16_NOUT 16
|
||||
|
||||
/**
|
||||
* struct idio_16_state - IDIO-16 state structure
|
||||
* @lock: synchronization lock for accessing device state
|
||||
* @out_state: output signals state
|
||||
*/
|
||||
struct idio_16_state {
|
||||
spinlock_t lock;
|
||||
DECLARE_BITMAP(out_state, IDIO_16_NOUT);
|
||||
};
|
||||
|
||||
/**
|
||||
* idio_16_get_direction - get the I/O direction for a signal offset
|
||||
* @offset: offset of signal to get direction
|
||||
*
|
||||
* Returns the signal direction (0=output, 1=input) for the signal at @offset.
|
||||
*/
|
||||
static inline int idio_16_get_direction(const unsigned long offset)
|
||||
{
|
||||
return (offset >= IDIO_16_NOUT) ? 1 : 0;
|
||||
}
|
||||
|
||||
int idio_16_get(struct idio_16 __iomem *reg, struct idio_16_state *state,
|
||||
unsigned long offset);
|
||||
void idio_16_get_multiple(struct idio_16 __iomem *reg,
|
||||
struct idio_16_state *state,
|
||||
const unsigned long *mask, unsigned long *bits);
|
||||
void idio_16_set(struct idio_16 __iomem *reg, struct idio_16_state *state,
|
||||
unsigned long offset, unsigned long value);
|
||||
void idio_16_set_multiple(struct idio_16 __iomem *reg,
|
||||
struct idio_16_state *state,
|
||||
const unsigned long *mask, const unsigned long *bits);
|
||||
void idio_16_state_init(struct idio_16_state *state);
|
||||
|
||||
#endif /* _IDIO_16_H_ */
|
|
@ -0,0 +1,219 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPIO latch driver
|
||||
*
|
||||
* Copyright (C) 2022 Sascha Hauer <s.hauer@pengutronix.de>
|
||||
*
|
||||
* This driver implements a GPIO (or better GPO as there is no input)
|
||||
* multiplexer based on latches like this:
|
||||
*
|
||||
* CLK0 ----------------------. ,--------.
|
||||
* CLK1 -------------------. `--------|> #0 |
|
||||
* | | |
|
||||
* OUT0 ----------------+--|-----------|D0 Q0|-----|<
|
||||
* OUT1 --------------+-|--|-----------|D1 Q1|-----|<
|
||||
* OUT2 ------------+-|-|--|-----------|D2 Q2|-----|<
|
||||
* OUT3 ----------+-|-|-|--|-----------|D3 Q3|-----|<
|
||||
* OUT4 --------+-|-|-|-|--|-----------|D4 Q4|-----|<
|
||||
* OUT5 ------+-|-|-|-|-|--|-----------|D5 Q5|-----|<
|
||||
* OUT6 ----+-|-|-|-|-|-|--|-----------|D6 Q6|-----|<
|
||||
* OUT7 --+-|-|-|-|-|-|-|--|-----------|D7 Q7|-----|<
|
||||
* | | | | | | | | | `--------'
|
||||
* | | | | | | | | |
|
||||
* | | | | | | | | | ,--------.
|
||||
* | | | | | | | | `-----------|> #1 |
|
||||
* | | | | | | | | | |
|
||||
* | | | | | | | `--------------|D0 Q0|-----|<
|
||||
* | | | | | | `----------------|D1 Q1|-----|<
|
||||
* | | | | | `------------------|D2 Q2|-----|<
|
||||
* | | | | `--------------------|D3 Q3|-----|<
|
||||
* | | | `----------------------|D4 Q4|-----|<
|
||||
* | | `------------------------|D5 Q5|-----|<
|
||||
* | `--------------------------|D6 Q6|-----|<
|
||||
* `----------------------------|D7 Q7|-----|<
|
||||
* `--------'
|
||||
*
|
||||
* The above is just an example. The actual number of number of latches and
|
||||
* the number of inputs per latch is derived from the number of GPIOs given
|
||||
* in the corresponding device tree properties.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
|
||||
struct gpio_latch_priv {
|
||||
struct gpio_chip gc;
|
||||
struct gpio_descs *clk_gpios;
|
||||
struct gpio_descs *latched_gpios;
|
||||
int n_latched_gpios;
|
||||
unsigned int setup_duration_ns;
|
||||
unsigned int clock_duration_ns;
|
||||
unsigned long *shadow;
|
||||
/*
|
||||
* Depending on whether any of the underlying GPIOs may sleep we either
|
||||
* use a mutex or a spinlock to protect our shadow map.
|
||||
*/
|
||||
union {
|
||||
struct mutex mutex; /* protects @shadow */
|
||||
spinlock_t spinlock; /* protects @shadow */
|
||||
};
|
||||
};
|
||||
|
||||
static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
}
|
||||
|
||||
static void gpio_latch_set_unlocked(struct gpio_latch_priv *priv,
|
||||
void (*set)(struct gpio_desc *desc, int value),
|
||||
unsigned int offset, bool val)
|
||||
{
|
||||
int latch = offset / priv->n_latched_gpios;
|
||||
int i;
|
||||
|
||||
assign_bit(offset, priv->shadow, val);
|
||||
|
||||
for (i = 0; i < priv->n_latched_gpios; i++)
|
||||
set(priv->latched_gpios->desc[i],
|
||||
test_bit(latch * priv->n_latched_gpios + i, priv->shadow));
|
||||
|
||||
ndelay(priv->setup_duration_ns);
|
||||
set(priv->clk_gpios->desc[latch], 1);
|
||||
ndelay(priv->clock_duration_ns);
|
||||
set(priv->clk_gpios->desc[latch], 0);
|
||||
}
|
||||
|
||||
static void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val)
|
||||
{
|
||||
struct gpio_latch_priv *priv = gpiochip_get_data(gc);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->spinlock, flags);
|
||||
|
||||
gpio_latch_set_unlocked(priv, gpiod_set_value, offset, val);
|
||||
|
||||
spin_unlock_irqrestore(&priv->spinlock, flags);
|
||||
}
|
||||
|
||||
static void gpio_latch_set_can_sleep(struct gpio_chip *gc, unsigned int offset, int val)
|
||||
{
|
||||
struct gpio_latch_priv *priv = gpiochip_get_data(gc);
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
|
||||
gpio_latch_set_unlocked(priv, gpiod_set_value_cansleep, offset, val);
|
||||
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
|
||||
static bool gpio_latch_can_sleep(struct gpio_latch_priv *priv, unsigned int n_latches)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_latches; i++)
|
||||
if (gpiod_cansleep(priv->clk_gpios->desc[i]))
|
||||
return true;
|
||||
|
||||
for (i = 0; i < priv->n_latched_gpios; i++)
|
||||
if (gpiod_cansleep(priv->latched_gpios->desc[i]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some value which is still acceptable to delay in atomic context.
|
||||
* If we need to go higher we might have to switch to usleep_range(),
|
||||
* but that cannot ne used in atomic context and the driver would have
|
||||
* to be adjusted to support that.
|
||||
*/
|
||||
#define DURATION_NS_MAX 5000
|
||||
|
||||
static int gpio_latch_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_latch_priv *priv;
|
||||
unsigned int n_latches;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->clk_gpios = devm_gpiod_get_array(&pdev->dev, "clk", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->clk_gpios))
|
||||
return PTR_ERR(priv->clk_gpios);
|
||||
|
||||
priv->latched_gpios = devm_gpiod_get_array(&pdev->dev, "latched", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->latched_gpios))
|
||||
return PTR_ERR(priv->latched_gpios);
|
||||
|
||||
n_latches = priv->clk_gpios->ndescs;
|
||||
priv->n_latched_gpios = priv->latched_gpios->ndescs;
|
||||
|
||||
priv->shadow = devm_bitmap_zalloc(&pdev->dev, n_latches * priv->n_latched_gpios,
|
||||
GFP_KERNEL);
|
||||
if (!priv->shadow)
|
||||
return -ENOMEM;
|
||||
|
||||
if (gpio_latch_can_sleep(priv, n_latches)) {
|
||||
priv->gc.can_sleep = true;
|
||||
priv->gc.set = gpio_latch_set_can_sleep;
|
||||
mutex_init(&priv->mutex);
|
||||
} else {
|
||||
priv->gc.can_sleep = false;
|
||||
priv->gc.set = gpio_latch_set;
|
||||
spin_lock_init(&priv->spinlock);
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "setup-duration-ns", &priv->setup_duration_ns);
|
||||
if (priv->setup_duration_ns > DURATION_NS_MAX) {
|
||||
dev_warn(&pdev->dev, "setup-duration-ns too high, limit to %d\n",
|
||||
DURATION_NS_MAX);
|
||||
priv->setup_duration_ns = DURATION_NS_MAX;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "clock-duration-ns", &priv->clock_duration_ns);
|
||||
if (priv->clock_duration_ns > DURATION_NS_MAX) {
|
||||
dev_warn(&pdev->dev, "clock-duration-ns too high, limit to %d\n",
|
||||
DURATION_NS_MAX);
|
||||
priv->clock_duration_ns = DURATION_NS_MAX;
|
||||
}
|
||||
|
||||
priv->gc.get_direction = gpio_latch_get_direction;
|
||||
priv->gc.ngpio = n_latches * priv->n_latched_gpios;
|
||||
priv->gc.owner = THIS_MODULE;
|
||||
priv->gc.base = -1;
|
||||
priv->gc.parent = &pdev->dev;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
|
||||
}
|
||||
|
||||
static const struct of_device_id gpio_latch_ids[] = {
|
||||
{
|
||||
.compatible = "gpio-latch",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpio_latch_ids);
|
||||
|
||||
static struct platform_driver gpio_latch_driver = {
|
||||
.driver = {
|
||||
.name = "gpio-latch",
|
||||
.of_match_table = gpio_latch_ids,
|
||||
},
|
||||
.probe = gpio_latch_probe,
|
||||
};
|
||||
module_platform_driver(gpio_latch_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("GPIO latch driver");
|
|
@ -28,8 +28,7 @@ static int max7300_i2c_read(struct device *dev, unsigned int reg)
|
|||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
|
||||
static int max7300_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int max7300_probe(struct i2c_client *client)
|
||||
{
|
||||
struct max7301 *ts;
|
||||
|
||||
|
@ -63,7 +62,7 @@ static struct i2c_driver max7300_driver = {
|
|||
.driver = {
|
||||
.name = "max7300",
|
||||
},
|
||||
.probe = max7300_probe,
|
||||
.probe_new = max7300_probe,
|
||||
.remove = max7300_remove,
|
||||
.id_table = max7300_id,
|
||||
};
|
||||
|
|
|
@ -608,9 +608,9 @@ static struct max732x_platform_data *of_gpio_max732x(struct device *dev)
|
|||
return pdata;
|
||||
}
|
||||
|
||||
static int max732x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int max732x_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_client_get_device_id(client);
|
||||
struct max732x_platform_data *pdata;
|
||||
struct device_node *node;
|
||||
struct max732x_chip *chip;
|
||||
|
@ -707,7 +707,7 @@ static struct i2c_driver max732x_driver = {
|
|||
.name = "max732x",
|
||||
.of_match_table = of_match_ptr(max732x_of_table),
|
||||
},
|
||||
.probe = max732x_probe,
|
||||
.probe_new = max732x_probe,
|
||||
.id_table = max732x_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#define GCCR 0x000 /* controller configuration */
|
||||
#define GPLR 0x004 /* pin level r/o */
|
||||
|
@ -331,7 +332,7 @@ static int mrfld_irq_set_wake(struct irq_data *d, unsigned int on)
|
|||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
dev_dbg(priv->dev, "%sable wake for gpio %u\n", on ? "en" : "dis", gpio);
|
||||
dev_dbg(priv->dev, "%s wake for gpio %u\n", str_enable_disable(on), gpio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1050,9 +1050,9 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int pca953x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *i2c_id)
|
||||
static int pca953x_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *i2c_id = i2c_client_get_device_id(client);
|
||||
struct pca953x_platform_data *pdata;
|
||||
struct pca953x_chip *chip;
|
||||
int irq_base = 0;
|
||||
|
@ -1376,7 +1376,7 @@ static struct i2c_driver pca953x_driver = {
|
|||
.of_match_table = pca953x_dt_ids,
|
||||
.acpi_match_table = pca953x_acpi_ids,
|
||||
},
|
||||
.probe = pca953x_probe,
|
||||
.probe_new = pca953x_probe,
|
||||
.remove = pca953x_remove,
|
||||
.id_table = pca953x_id,
|
||||
};
|
||||
|
|
|
@ -15,14 +15,28 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#define SLG7XL45106_GPO_REG 0xDB
|
||||
|
||||
/**
|
||||
* struct pca9570_platform_data - GPIO platformdata
|
||||
* @ngpio: no of gpios
|
||||
* @command: Command to be sent
|
||||
*/
|
||||
struct pca9570_platform_data {
|
||||
u16 ngpio;
|
||||
u32 command;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pca9570 - GPIO driver data
|
||||
* @chip: GPIO controller chip
|
||||
* @p_data: GPIO controller platform data
|
||||
* @lock: Protects write sequences
|
||||
* @out: Buffer for device register
|
||||
*/
|
||||
struct pca9570 {
|
||||
struct gpio_chip chip;
|
||||
const struct pca9570_platform_data *p_data;
|
||||
struct mutex lock;
|
||||
u8 out;
|
||||
};
|
||||
|
@ -32,7 +46,11 @@ static int pca9570_read(struct pca9570 *gpio, u8 *value)
|
|||
struct i2c_client *client = to_i2c_client(gpio->chip.parent);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (gpio->p_data->command != 0)
|
||||
ret = i2c_smbus_read_byte_data(client, gpio->p_data->command);
|
||||
else
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -44,6 +62,9 @@ static int pca9570_write(struct pca9570 *gpio, u8 value)
|
|||
{
|
||||
struct i2c_client *client = to_i2c_client(gpio->chip.parent);
|
||||
|
||||
if (gpio->p_data->command != 0)
|
||||
return i2c_smbus_write_byte_data(client, gpio->p_data->command, value);
|
||||
|
||||
return i2c_smbus_write_byte(client, value);
|
||||
}
|
||||
|
||||
|
@ -106,7 +127,8 @@ static int pca9570_probe(struct i2c_client *client)
|
|||
gpio->chip.get = pca9570_get;
|
||||
gpio->chip.set = pca9570_set;
|
||||
gpio->chip.base = -1;
|
||||
gpio->chip.ngpio = (uintptr_t)device_get_match_data(&client->dev);
|
||||
gpio->p_data = device_get_match_data(&client->dev);
|
||||
gpio->chip.ngpio = gpio->p_data->ngpio;
|
||||
gpio->chip.can_sleep = true;
|
||||
|
||||
mutex_init(&gpio->lock);
|
||||
|
@ -119,16 +141,31 @@ static int pca9570_probe(struct i2c_client *client)
|
|||
return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio);
|
||||
}
|
||||
|
||||
static const struct pca9570_platform_data pca9570_gpio = {
|
||||
.ngpio = 4,
|
||||
};
|
||||
|
||||
static const struct pca9570_platform_data pca9571_gpio = {
|
||||
.ngpio = 8,
|
||||
};
|
||||
|
||||
static const struct pca9570_platform_data slg7xl45106_gpio = {
|
||||
.ngpio = 8,
|
||||
.command = SLG7XL45106_GPO_REG,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id pca9570_id_table[] = {
|
||||
{ "pca9570", 4 },
|
||||
{ "pca9571", 8 },
|
||||
{ "pca9570", (kernel_ulong_t)&pca9570_gpio},
|
||||
{ "pca9571", (kernel_ulong_t)&pca9571_gpio },
|
||||
{ "slg7xl45106", (kernel_ulong_t)&slg7xl45106_gpio },
|
||||
{ /* 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 },
|
||||
{ .compatible = "dlg,slg7xl45106", .data = &slg7xl45106_gpio},
|
||||
{ .compatible = "nxp,pca9570", .data = &pca9570_gpio },
|
||||
{ .compatible = "nxp,pca9571", .data = &pca9571_gpio },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pca9570_of_match_table);
|
||||
|
|
|
@ -247,9 +247,9 @@ static const struct irq_chip pcf857x_irq_chip = {
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int pcf857x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int pcf857x_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_client_get_device_id(client);
|
||||
struct pcf857x_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct pcf857x *gpio;
|
||||
|
@ -422,7 +422,7 @@ static struct i2c_driver pcf857x_driver = {
|
|||
.name = "pcf857x",
|
||||
.of_match_table = of_match_ptr(pcf857x_of_table),
|
||||
},
|
||||
.probe = pcf857x_probe,
|
||||
.probe_new = pcf857x_probe,
|
||||
.remove = pcf857x_remove,
|
||||
.shutdown = pcf857x_shutdown,
|
||||
.id_table = pcf857x_id,
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
* GPIO driver for the ACCES PCI-IDIO-16
|
||||
* Copyright (C) 2017 William Breathitt Gray
|
||||
*/
|
||||
#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>
|
||||
|
@ -16,51 +15,28 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* struct idio_16_gpio_reg - GPIO device registers structure
|
||||
* @out0_7: Read: FET Drive Outputs 0-7
|
||||
* Write: FET Drive Outputs 0-7
|
||||
* @in0_7: Read: Isolated Inputs 0-7
|
||||
* Write: Clear Interrupt
|
||||
* @irq_ctl: Read: Enable IRQ
|
||||
* Write: Disable IRQ
|
||||
* @filter_ctl: Read: Activate Input Filters 0-15
|
||||
* Write: Deactivate Input Filters 0-15
|
||||
* @out8_15: Read: FET Drive Outputs 8-15
|
||||
* Write: FET Drive Outputs 8-15
|
||||
* @in8_15: Read: Isolated Inputs 8-15
|
||||
* Write: Unused
|
||||
* @irq_status: Read: Interrupt status
|
||||
* Write: Unused
|
||||
*/
|
||||
struct idio_16_gpio_reg {
|
||||
u8 out0_7;
|
||||
u8 in0_7;
|
||||
u8 irq_ctl;
|
||||
u8 filter_ctl;
|
||||
u8 out8_15;
|
||||
u8 in8_15;
|
||||
u8 irq_status;
|
||||
};
|
||||
#include "gpio-idio-16.h"
|
||||
|
||||
/**
|
||||
* struct idio_16_gpio - GPIO device private data structure
|
||||
* @chip: instance of the gpio_chip
|
||||
* @lock: synchronization lock to prevent I/O race conditions
|
||||
* @reg: I/O address offset for the GPIO device registers
|
||||
* @state: ACCES IDIO-16 device state
|
||||
* @irq_mask: I/O bits affected by interrupts
|
||||
*/
|
||||
struct idio_16_gpio {
|
||||
struct gpio_chip chip;
|
||||
raw_spinlock_t lock;
|
||||
struct idio_16_gpio_reg __iomem *reg;
|
||||
struct idio_16 __iomem *reg;
|
||||
struct idio_16_state state;
|
||||
unsigned long irq_mask;
|
||||
};
|
||||
|
||||
static int idio_16_gpio_get_direction(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
if (offset > 15)
|
||||
if (idio_16_get_direction(offset))
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
|
@ -82,43 +58,16 @@ static int idio_16_gpio_direction_output(struct gpio_chip *chip,
|
|||
static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
unsigned long mask = BIT(offset);
|
||||
|
||||
if (offset < 8)
|
||||
return !!(ioread8(&idio16gpio->reg->out0_7) & mask);
|
||||
|
||||
if (offset < 16)
|
||||
return !!(ioread8(&idio16gpio->reg->out8_15) & (mask >> 8));
|
||||
|
||||
if (offset < 24)
|
||||
return !!(ioread8(&idio16gpio->reg->in0_7) & (mask >> 16));
|
||||
|
||||
return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24));
|
||||
return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset);
|
||||
}
|
||||
|
||||
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
unsigned long offset;
|
||||
unsigned long gpio_mask;
|
||||
void __iomem *ports[] = {
|
||||
&idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
|
||||
&idio16gpio->reg->in0_7, &idio16gpio->reg->in8_15,
|
||||
};
|
||||
void __iomem *port_addr;
|
||||
unsigned long port_state;
|
||||
|
||||
/* clear bits array to a clean slate */
|
||||
bitmap_zero(bits, chip->ngpio);
|
||||
|
||||
for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
|
||||
port_addr = ports[offset / 8];
|
||||
port_state = ioread8(port_addr) & gpio_mask;
|
||||
|
||||
bitmap_set_value8(bits, port_state, offset);
|
||||
}
|
||||
|
||||
idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -126,61 +75,16 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
|||
int value)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
unsigned int mask = BIT(offset);
|
||||
void __iomem *base;
|
||||
unsigned long flags;
|
||||
unsigned int out_state;
|
||||
|
||||
if (offset > 15)
|
||||
return;
|
||||
|
||||
if (offset > 7) {
|
||||
mask >>= 8;
|
||||
base = &idio16gpio->reg->out8_15;
|
||||
} else
|
||||
base = &idio16gpio->reg->out0_7;
|
||||
|
||||
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
|
||||
|
||||
if (value)
|
||||
out_state = ioread8(base) | mask;
|
||||
else
|
||||
out_state = ioread8(base) & ~mask;
|
||||
|
||||
iowrite8(out_state, base);
|
||||
|
||||
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
|
||||
idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value);
|
||||
}
|
||||
|
||||
static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
unsigned long offset;
|
||||
unsigned long gpio_mask;
|
||||
void __iomem *ports[] = {
|
||||
&idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
|
||||
};
|
||||
size_t index;
|
||||
void __iomem *port_addr;
|
||||
unsigned long bitmask;
|
||||
unsigned long flags;
|
||||
unsigned long out_state;
|
||||
|
||||
for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
|
||||
index = offset / 8;
|
||||
port_addr = ports[index];
|
||||
|
||||
bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
|
||||
|
||||
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
|
||||
|
||||
out_state = ioread8(port_addr) & ~gpio_mask;
|
||||
out_state |= bitmask;
|
||||
iowrite8(out_state, port_addr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
|
||||
}
|
||||
idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
|
||||
}
|
||||
|
||||
static void idio_16_irq_ack(struct irq_data *data)
|
||||
|
@ -335,6 +239,8 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
idio16gpio->chip.set = idio_16_gpio_set;
|
||||
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
|
||||
|
||||
idio_16_state_init(&idio16gpio->state);
|
||||
|
||||
girq = &idio16gpio->chip.irq;
|
||||
girq->chip = &idio_16_irqchip;
|
||||
/* This will let us handle the parent IRQ in the driver */
|
||||
|
@ -379,3 +285,4 @@ module_pci_driver(idio_16_driver);
|
|||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(GPIO_IDIO_16);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "gpiolib.h"
|
||||
|
||||
#define GPIO_SIM_NGPIO_MAX 1024
|
||||
#define GPIO_SIM_PROP_MAX 4 /* Max 3 properties + sentinel. */
|
||||
#define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */
|
||||
|
||||
|
@ -371,6 +372,9 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (num_lines > GPIO_SIM_NGPIO_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
ret = fwnode_property_read_string(swnode, "gpio-sim,label", &label);
|
||||
if (ret) {
|
||||
label = devm_kasprintf(dev, GFP_KERNEL, "%s-%s",
|
||||
|
|
|
@ -70,8 +70,7 @@ static int sl28cpld_gpio_irq_init(struct platform_device *pdev,
|
|||
irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs);
|
||||
irq_chip->num_regs = 1;
|
||||
irq_chip->status_base = base + GPIO_REG_IP;
|
||||
irq_chip->mask_base = base + GPIO_REG_IE;
|
||||
irq_chip->mask_invert = true;
|
||||
irq_chip->unmask_base = base + GPIO_REG_IE;
|
||||
irq_chip->ack_base = base + GPIO_REG_IP;
|
||||
|
||||
ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
|
||||
|
|
|
@ -1,411 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics ConneXt (STA2X11) GPIO driver
|
||||
*
|
||||
* Copyright 2012 ST Microelectronics (Alessandro Rubini)
|
||||
* Based on gpio-ml-ioh.c, Copyright 2010 OKI Semiconductors Ltd.
|
||||
* Also based on previous sta2x11 work, Copyright 2011 Wind River Systems, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/sta2x11-mfd.h>
|
||||
|
||||
struct gsta_regs {
|
||||
u32 dat; /* 0x00 */
|
||||
u32 dats;
|
||||
u32 datc;
|
||||
u32 pdis;
|
||||
u32 dir; /* 0x10 */
|
||||
u32 dirs;
|
||||
u32 dirc;
|
||||
u32 unused_1c;
|
||||
u32 afsela; /* 0x20 */
|
||||
u32 unused_24[7];
|
||||
u32 rimsc; /* 0x40 */
|
||||
u32 fimsc;
|
||||
u32 is;
|
||||
u32 ic;
|
||||
};
|
||||
|
||||
struct gsta_gpio {
|
||||
spinlock_t lock;
|
||||
struct device *dev;
|
||||
void __iomem *reg_base;
|
||||
struct gsta_regs __iomem *regs[GSTA_NR_BLOCKS];
|
||||
struct gpio_chip gpio;
|
||||
int irq_base;
|
||||
/* FIXME: save the whole config here (AF, ...) */
|
||||
unsigned irq_type[GSTA_NR_GPIO];
|
||||
};
|
||||
|
||||
/*
|
||||
* gpio methods
|
||||
*/
|
||||
|
||||
static void gsta_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
|
||||
{
|
||||
struct gsta_gpio *chip = gpiochip_get_data(gpio);
|
||||
struct gsta_regs __iomem *regs = chip->regs[nr / GSTA_GPIO_PER_BLOCK];
|
||||
u32 bit = BIT(nr % GSTA_GPIO_PER_BLOCK);
|
||||
|
||||
if (val)
|
||||
writel(bit, ®s->dats);
|
||||
else
|
||||
writel(bit, ®s->datc);
|
||||
}
|
||||
|
||||
static int gsta_gpio_get(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
struct gsta_gpio *chip = gpiochip_get_data(gpio);
|
||||
struct gsta_regs __iomem *regs = chip->regs[nr / GSTA_GPIO_PER_BLOCK];
|
||||
u32 bit = BIT(nr % GSTA_GPIO_PER_BLOCK);
|
||||
|
||||
return !!(readl(®s->dat) & bit);
|
||||
}
|
||||
|
||||
static int gsta_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||
int val)
|
||||
{
|
||||
struct gsta_gpio *chip = gpiochip_get_data(gpio);
|
||||
struct gsta_regs __iomem *regs = chip->regs[nr / GSTA_GPIO_PER_BLOCK];
|
||||
u32 bit = BIT(nr % GSTA_GPIO_PER_BLOCK);
|
||||
|
||||
writel(bit, ®s->dirs);
|
||||
/* Data register after direction, otherwise pullup/down is selected */
|
||||
if (val)
|
||||
writel(bit, ®s->dats);
|
||||
else
|
||||
writel(bit, ®s->datc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsta_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
struct gsta_gpio *chip = gpiochip_get_data(gpio);
|
||||
struct gsta_regs __iomem *regs = chip->regs[nr / GSTA_GPIO_PER_BLOCK];
|
||||
u32 bit = BIT(nr % GSTA_GPIO_PER_BLOCK);
|
||||
|
||||
writel(bit, ®s->dirc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsta_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
|
||||
{
|
||||
struct gsta_gpio *chip = gpiochip_get_data(gpio);
|
||||
return chip->irq_base + offset;
|
||||
}
|
||||
|
||||
static void gsta_gpio_setup(struct gsta_gpio *chip) /* called from probe */
|
||||
{
|
||||
struct gpio_chip *gpio = &chip->gpio;
|
||||
|
||||
/*
|
||||
* ARCH_NR_GPIOS is currently 256 and dynamic allocation starts
|
||||
* from the end. However, for compatibility, we need the first
|
||||
* ConneXt device to start from gpio 0: it's the main chipset
|
||||
* on most boards so documents and drivers assume gpio0..gpio127
|
||||
*/
|
||||
static int gpio_base;
|
||||
|
||||
gpio->label = dev_name(chip->dev);
|
||||
gpio->owner = THIS_MODULE;
|
||||
gpio->direction_input = gsta_gpio_direction_input;
|
||||
gpio->get = gsta_gpio_get;
|
||||
gpio->direction_output = gsta_gpio_direction_output;
|
||||
gpio->set = gsta_gpio_set;
|
||||
gpio->dbg_show = NULL;
|
||||
gpio->base = gpio_base;
|
||||
gpio->ngpio = GSTA_NR_GPIO;
|
||||
gpio->can_sleep = false;
|
||||
gpio->to_irq = gsta_gpio_to_irq;
|
||||
|
||||
/*
|
||||
* After the first device, turn to dynamic gpio numbers.
|
||||
* For example, with ARCH_NR_GPIOS = 256 we can fit two cards
|
||||
*/
|
||||
if (!gpio_base)
|
||||
gpio_base = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special method: alternate functions and pullup/pulldown. This is only
|
||||
* invoked on startup to configure gpio's according to platform data.
|
||||
* FIXME : this functionality shall be managed (and exported to other drivers)
|
||||
* via the pin control subsystem.
|
||||
*/
|
||||
static void gsta_set_config(struct gsta_gpio *chip, int nr, unsigned cfg)
|
||||
{
|
||||
struct gsta_regs __iomem *regs = chip->regs[nr / GSTA_GPIO_PER_BLOCK];
|
||||
unsigned long flags;
|
||||
u32 bit = BIT(nr % GSTA_GPIO_PER_BLOCK);
|
||||
u32 val;
|
||||
int err = 0;
|
||||
|
||||
pr_info("%s: %p %i %i\n", __func__, chip, nr, cfg);
|
||||
|
||||
if (cfg == PINMUX_TYPE_NONE)
|
||||
return;
|
||||
|
||||
/* Alternate function or not? */
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
val = readl(®s->afsela);
|
||||
if (cfg == PINMUX_TYPE_FUNCTION)
|
||||
val |= bit;
|
||||
else
|
||||
val &= ~bit;
|
||||
writel(val | bit, ®s->afsela);
|
||||
if (cfg == PINMUX_TYPE_FUNCTION) {
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/* not alternate function: set details */
|
||||
switch (cfg) {
|
||||
case PINMUX_TYPE_OUTPUT_LOW:
|
||||
writel(bit, ®s->dirs);
|
||||
writel(bit, ®s->datc);
|
||||
break;
|
||||
case PINMUX_TYPE_OUTPUT_HIGH:
|
||||
writel(bit, ®s->dirs);
|
||||
writel(bit, ®s->dats);
|
||||
break;
|
||||
case PINMUX_TYPE_INPUT:
|
||||
writel(bit, ®s->dirc);
|
||||
val = readl(®s->pdis) | bit;
|
||||
writel(val, ®s->pdis);
|
||||
break;
|
||||
case PINMUX_TYPE_INPUT_PULLUP:
|
||||
writel(bit, ®s->dirc);
|
||||
val = readl(®s->pdis) & ~bit;
|
||||
writel(val, ®s->pdis);
|
||||
writel(bit, ®s->dats);
|
||||
break;
|
||||
case PINMUX_TYPE_INPUT_PULLDOWN:
|
||||
writel(bit, ®s->dirc);
|
||||
val = readl(®s->pdis) & ~bit;
|
||||
writel(val, ®s->pdis);
|
||||
writel(bit, ®s->datc);
|
||||
break;
|
||||
default:
|
||||
err = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
if (err)
|
||||
pr_err("%s: chip %p, pin %i, cfg %i is invalid\n",
|
||||
__func__, chip, nr, cfg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Irq methods
|
||||
*/
|
||||
|
||||
static void gsta_irq_disable(struct irq_data *data)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
|
||||
struct gsta_gpio *chip = gc->private;
|
||||
int nr = data->irq - chip->irq_base;
|
||||
struct gsta_regs __iomem *regs = chip->regs[nr / GSTA_GPIO_PER_BLOCK];
|
||||
u32 bit = BIT(nr % GSTA_GPIO_PER_BLOCK);
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
if (chip->irq_type[nr] & IRQ_TYPE_EDGE_RISING) {
|
||||
val = readl(®s->rimsc) & ~bit;
|
||||
writel(val, ®s->rimsc);
|
||||
}
|
||||
if (chip->irq_type[nr] & IRQ_TYPE_EDGE_FALLING) {
|
||||
val = readl(®s->fimsc) & ~bit;
|
||||
writel(val, ®s->fimsc);
|
||||
}
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static void gsta_irq_enable(struct irq_data *data)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
|
||||
struct gsta_gpio *chip = gc->private;
|
||||
int nr = data->irq - chip->irq_base;
|
||||
struct gsta_regs __iomem *regs = chip->regs[nr / GSTA_GPIO_PER_BLOCK];
|
||||
u32 bit = BIT(nr % GSTA_GPIO_PER_BLOCK);
|
||||
u32 val;
|
||||
int type;
|
||||
unsigned long flags;
|
||||
|
||||
type = chip->irq_type[nr];
|
||||
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
val = readl(®s->rimsc);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
writel(val | bit, ®s->rimsc);
|
||||
else
|
||||
writel(val & ~bit, ®s->rimsc);
|
||||
val = readl(®s->rimsc);
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
writel(val | bit, ®s->fimsc);
|
||||
else
|
||||
writel(val & ~bit, ®s->fimsc);
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static int gsta_irq_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct gsta_gpio *chip = gc->private;
|
||||
int nr = d->irq - chip->irq_base;
|
||||
|
||||
/* We only support edge interrupts */
|
||||
if (!(type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))) {
|
||||
pr_debug("%s: unsupported type 0x%x\n", __func__, type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->irq_type[nr] = type; /* used for enable/disable */
|
||||
|
||||
gsta_irq_enable(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t gsta_gpio_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct gsta_gpio *chip = dev_id;
|
||||
struct gsta_regs __iomem *regs;
|
||||
u32 is;
|
||||
int i, nr, base;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
for (i = 0; i < GSTA_NR_BLOCKS; i++) {
|
||||
regs = chip->regs[i];
|
||||
base = chip->irq_base + i * GSTA_GPIO_PER_BLOCK;
|
||||
while ((is = readl(®s->is))) {
|
||||
nr = __ffs(is);
|
||||
irq = base + nr;
|
||||
generic_handle_irq(irq);
|
||||
writel(1 << nr, ®s->ic);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gsta_alloc_irq_chip(struct gsta_gpio *chip)
|
||||
{
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_chip_type *ct;
|
||||
int rv;
|
||||
|
||||
gc = devm_irq_alloc_generic_chip(chip->dev, KBUILD_MODNAME, 1,
|
||||
chip->irq_base,
|
||||
chip->reg_base, handle_simple_irq);
|
||||
if (!gc)
|
||||
return -ENOMEM;
|
||||
|
||||
gc->private = chip;
|
||||
ct = gc->chip_types;
|
||||
|
||||
ct->chip.irq_set_type = gsta_irq_type;
|
||||
ct->chip.irq_disable = gsta_irq_disable;
|
||||
ct->chip.irq_enable = gsta_irq_enable;
|
||||
|
||||
/* FIXME: this makes at most 32 interrupts. Request 0 by now */
|
||||
rv = devm_irq_setup_generic_chip(chip->dev, gc,
|
||||
0 /* IRQ_MSK(GSTA_GPIO_PER_BLOCK) */,
|
||||
0, IRQ_NOREQUEST | IRQ_NOPROBE, 0);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* Set up all 128 interrupts: code from setup_generic_chip */
|
||||
{
|
||||
struct irq_chip_type *ct = gc->chip_types;
|
||||
int i, j;
|
||||
for (j = 0; j < GSTA_NR_GPIO; j++) {
|
||||
i = chip->irq_base + j;
|
||||
irq_set_chip_and_handler(i, &ct->chip, ct->handler);
|
||||
irq_set_chip_data(i, gc);
|
||||
irq_clear_status_flags(i, IRQ_NOREQUEST | IRQ_NOPROBE);
|
||||
}
|
||||
gc->irq_cnt = i - gc->irq_base;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The platform device used here is instantiated by the MFD device */
|
||||
static int gsta_probe(struct platform_device *dev)
|
||||
{
|
||||
int i, err;
|
||||
struct pci_dev *pdev;
|
||||
struct sta2x11_gpio_pdata *gpio_pdata;
|
||||
struct gsta_gpio *chip;
|
||||
|
||||
pdev = *(struct pci_dev **)dev_get_platdata(&dev->dev);
|
||||
gpio_pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (gpio_pdata == NULL)
|
||||
dev_err(&dev->dev, "no gpio config\n");
|
||||
pr_debug("gpio config: %p\n", gpio_pdata);
|
||||
|
||||
chip = devm_kzalloc(&dev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
chip->dev = &dev->dev;
|
||||
chip->reg_base = devm_platform_ioremap_resource(dev, 0);
|
||||
if (IS_ERR(chip->reg_base))
|
||||
return PTR_ERR(chip->reg_base);
|
||||
|
||||
for (i = 0; i < GSTA_NR_BLOCKS; i++) {
|
||||
chip->regs[i] = chip->reg_base + i * 4096;
|
||||
/* disable all irqs */
|
||||
writel(0, &chip->regs[i]->rimsc);
|
||||
writel(0, &chip->regs[i]->fimsc);
|
||||
writel(~0, &chip->regs[i]->ic);
|
||||
}
|
||||
spin_lock_init(&chip->lock);
|
||||
gsta_gpio_setup(chip);
|
||||
if (gpio_pdata)
|
||||
for (i = 0; i < GSTA_NR_GPIO; i++)
|
||||
gsta_set_config(chip, i, gpio_pdata->pinconfig[i]);
|
||||
|
||||
/* 384 was used in previous code: be compatible for other drivers */
|
||||
err = devm_irq_alloc_descs(&dev->dev, -1, 384,
|
||||
GSTA_NR_GPIO, NUMA_NO_NODE);
|
||||
if (err < 0) {
|
||||
dev_warn(&dev->dev, "sta2x11 gpio: Can't get irq base (%i)\n",
|
||||
-err);
|
||||
return err;
|
||||
}
|
||||
chip->irq_base = err;
|
||||
|
||||
err = gsta_alloc_irq_chip(chip);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devm_request_irq(&dev->dev, pdev->irq, gsta_gpio_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, chip);
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "sta2x11 gpio: Can't request irq (%i)\n",
|
||||
-err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return devm_gpiochip_add_data(&dev->dev, &chip->gpio, chip);
|
||||
}
|
||||
|
||||
static struct platform_driver sta2x11_gpio_platform_driver = {
|
||||
.driver = {
|
||||
.name = "sta2x11-gpio",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = gsta_probe,
|
||||
};
|
||||
builtin_platform_driver(sta2x11_gpio_platform_driver);
|
|
@ -98,8 +98,7 @@ static const struct of_device_id tpic2810_of_match_table[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, tpic2810_of_match_table);
|
||||
|
||||
static int tpic2810_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int tpic2810_probe(struct i2c_client *client)
|
||||
{
|
||||
struct tpic2810 *gpio;
|
||||
int ret;
|
||||
|
@ -144,7 +143,7 @@ static struct i2c_driver tpic2810_driver = {
|
|||
.name = "tpic2810",
|
||||
.of_match_table = tpic2810_of_match_table,
|
||||
},
|
||||
.probe = tpic2810_probe,
|
||||
.probe_new = tpic2810_probe,
|
||||
.remove = tpic2810_remove,
|
||||
.id_table = tpic2810_id_table,
|
||||
};
|
||||
|
|
|
@ -136,8 +136,7 @@ static const struct of_device_id ts4900_gpio_of_match_table[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, ts4900_gpio_of_match_table);
|
||||
|
||||
static int ts4900_gpio_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int ts4900_gpio_probe(struct i2c_client *client)
|
||||
{
|
||||
struct ts4900_gpio_priv *priv;
|
||||
u32 ngpio;
|
||||
|
@ -186,7 +185,7 @@ static struct i2c_driver ts4900_gpio_driver = {
|
|||
.name = "ts4900-gpio",
|
||||
.of_match_table = ts4900_gpio_of_match_table,
|
||||
},
|
||||
.probe = ts4900_gpio_probe,
|
||||
.probe_new = ts4900_gpio_probe,
|
||||
.id_table = ts4900_gpio_id_table,
|
||||
};
|
||||
module_i2c_driver(ts4900_gpio_driver);
|
||||
|
|
|
@ -89,6 +89,30 @@ struct acpi_gpio_chip {
|
|||
struct list_head deferred_req_irqs_list_entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_gpio_info - ACPI GPIO specific information
|
||||
* @adev: reference to ACPI device which consumes GPIO resource
|
||||
* @flags: GPIO initialization flags
|
||||
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
|
||||
* @pin_config: pin bias as provided by ACPI
|
||||
* @polarity: interrupt polarity as provided by ACPI
|
||||
* @triggering: triggering type as provided by ACPI
|
||||
* @wake_capable: wake capability as provided by ACPI
|
||||
* @debounce: debounce timeout as provided by ACPI
|
||||
* @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
|
||||
*/
|
||||
struct acpi_gpio_info {
|
||||
struct acpi_device *adev;
|
||||
enum gpiod_flags flags;
|
||||
bool gpioint;
|
||||
int pin_config;
|
||||
int polarity;
|
||||
int triggering;
|
||||
bool wake_capable;
|
||||
unsigned int debounce;
|
||||
unsigned int quirks;
|
||||
};
|
||||
|
||||
/*
|
||||
* For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init
|
||||
* (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a
|
||||
|
@ -512,7 +536,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
|
|||
if (ACPI_FAILURE(status))
|
||||
return;
|
||||
|
||||
acpi_walk_resources(handle, "_AEI",
|
||||
acpi_walk_resources(handle, METHOD_NAME__AEI,
|
||||
acpi_gpiochip_alloc_event, acpi_gpio);
|
||||
|
||||
mutex_lock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
|
@ -670,8 +694,8 @@ __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info)
|
||||
static int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
|
||||
struct acpi_gpio_info *info)
|
||||
{
|
||||
struct device *dev = &info->adev->dev;
|
||||
enum gpiod_flags old = *flags;
|
||||
|
@ -690,8 +714,8 @@ acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *inf
|
|||
return ret;
|
||||
}
|
||||
|
||||
int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
|
||||
struct acpi_gpio_info *info)
|
||||
static int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
|
||||
struct acpi_gpio_info *info)
|
||||
{
|
||||
switch (info->pin_config) {
|
||||
case ACPI_PIN_CONFIG_PULLUP:
|
||||
|
@ -864,8 +888,9 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,
|
|||
* function only returns the first.
|
||||
*/
|
||||
static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
||||
const char *propname, int index,
|
||||
struct acpi_gpio_info *info)
|
||||
const char *propname,
|
||||
int index,
|
||||
struct acpi_gpio_info *info)
|
||||
{
|
||||
struct acpi_gpio_lookup lookup;
|
||||
int ret;
|
||||
|
@ -896,6 +921,44 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
|||
return ret ? ERR_PTR(ret) : lookup.desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_get_gpiod_from_data() - get a GPIO descriptor from ACPI data node
|
||||
* @fwnode: pointer to an ACPI firmware node to get the GPIO information from
|
||||
* @propname: Property name of the GPIO
|
||||
* @index: index of GpioIo/GpioInt resource (starting from %0)
|
||||
* @info: info pointer to fill in (optional)
|
||||
*
|
||||
* This function uses the property-based GPIO lookup to get to the GPIO
|
||||
* resource with the relevant information from a data-only ACPI firmware node
|
||||
* and uses that to obtain the GPIO descriptor to return.
|
||||
*
|
||||
* If the GPIO cannot be translated or there is an error an ERR_PTR is
|
||||
* returned.
|
||||
*/
|
||||
static struct gpio_desc *acpi_get_gpiod_from_data(struct fwnode_handle *fwnode,
|
||||
const char *propname,
|
||||
int index,
|
||||
struct acpi_gpio_info *info)
|
||||
{
|
||||
struct acpi_gpio_lookup lookup;
|
||||
int ret;
|
||||
|
||||
if (!is_acpi_data_node(fwnode))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (!propname)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
lookup.index = index;
|
||||
|
||||
ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = acpi_gpio_resource_lookup(&lookup, info);
|
||||
return ret ? ERR_PTR(ret) : lookup.desc;
|
||||
}
|
||||
|
||||
static bool acpi_can_fallback_to_crs(struct acpi_device *adev,
|
||||
const char *con_id)
|
||||
{
|
||||
|
@ -906,13 +969,13 @@ static bool acpi_can_fallback_to_crs(struct acpi_device *adev,
|
|||
return con_id == NULL;
|
||||
}
|
||||
|
||||
struct gpio_desc *acpi_find_gpio(struct device *dev,
|
||||
struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum gpiod_flags *dflags,
|
||||
unsigned long *lookupflags)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
struct acpi_device *adev = to_acpi_device_node(fwnode);
|
||||
struct acpi_gpio_info info;
|
||||
struct gpio_desc *desc;
|
||||
char propname[32];
|
||||
|
@ -928,7 +991,12 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
|
|||
gpio_suffixes[i]);
|
||||
}
|
||||
|
||||
desc = acpi_get_gpiod_by_index(adev, propname, idx, &info);
|
||||
if (adev)
|
||||
desc = acpi_get_gpiod_by_index(adev,
|
||||
propname, idx, &info);
|
||||
else
|
||||
desc = acpi_get_gpiod_from_data(fwnode,
|
||||
propname, idx, &info);
|
||||
if (!IS_ERR(desc))
|
||||
break;
|
||||
if (PTR_ERR(desc) == -EPROBE_DEFER)
|
||||
|
@ -937,7 +1005,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
|
|||
|
||||
/* Then from plain _CRS GPIOs */
|
||||
if (IS_ERR(desc)) {
|
||||
if (!acpi_can_fallback_to_crs(adev, con_id))
|
||||
if (!adev || !acpi_can_fallback_to_crs(adev, con_id))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
|
||||
|
@ -956,50 +1024,6 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
|
|||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_node_get_gpiod() - get a GPIO descriptor from ACPI resources
|
||||
* @fwnode: pointer to an ACPI firmware node to get the GPIO information from
|
||||
* @propname: Property name of the GPIO
|
||||
* @index: index of GpioIo/GpioInt resource (starting from %0)
|
||||
* @info: info pointer to fill in (optional)
|
||||
*
|
||||
* If @fwnode is an ACPI device object, call acpi_get_gpiod_by_index() for it.
|
||||
* Otherwise (i.e. it is a data-only non-device object), use the property-based
|
||||
* GPIO lookup to get to the GPIO resource with the relevant information and use
|
||||
* that to obtain the GPIO descriptor to return.
|
||||
*
|
||||
* If the GPIO cannot be translated or there is an error an ERR_PTR is
|
||||
* returned.
|
||||
*/
|
||||
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
||||
const char *propname, int index,
|
||||
struct acpi_gpio_info *info)
|
||||
{
|
||||
struct acpi_gpio_lookup lookup;
|
||||
struct acpi_device *adev;
|
||||
int ret;
|
||||
|
||||
adev = to_acpi_device_node(fwnode);
|
||||
if (adev)
|
||||
return acpi_get_gpiod_by_index(adev, propname, index, info);
|
||||
|
||||
if (!is_acpi_data_node(fwnode))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (!propname)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
memset(&lookup, 0, sizeof(lookup));
|
||||
lookup.index = index;
|
||||
|
||||
ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = acpi_gpio_resource_lookup(&lookup, info);
|
||||
return ret ? ERR_PTR(ret) : lookup.desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number
|
||||
* @adev: pointer to a ACPI device to get IRQ from
|
||||
|
|
|
@ -22,30 +22,6 @@ struct gpio_chip;
|
|||
struct gpio_desc;
|
||||
struct gpio_device;
|
||||
|
||||
/**
|
||||
* struct acpi_gpio_info - ACPI GPIO specific information
|
||||
* @adev: reference to ACPI device which consumes GPIO resource
|
||||
* @flags: GPIO initialization flags
|
||||
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
|
||||
* @pin_config: pin bias as provided by ACPI
|
||||
* @polarity: interrupt polarity as provided by ACPI
|
||||
* @triggering: triggering type as provided by ACPI
|
||||
* @wake_capable: wake capability as provided by ACPI
|
||||
* @debounce: debounce timeout as provided by ACPI
|
||||
* @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
|
||||
*/
|
||||
struct acpi_gpio_info {
|
||||
struct acpi_device *adev;
|
||||
enum gpiod_flags flags;
|
||||
bool gpioint;
|
||||
int pin_config;
|
||||
int polarity;
|
||||
int triggering;
|
||||
bool wake_capable;
|
||||
unsigned int debounce;
|
||||
unsigned int quirks;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
void acpi_gpiochip_add(struct gpio_chip *chip);
|
||||
void acpi_gpiochip_remove(struct gpio_chip *chip);
|
||||
|
@ -55,19 +31,11 @@ void acpi_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev);
|
|||
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
|
||||
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
|
||||
|
||||
int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
|
||||
struct acpi_gpio_info *info);
|
||||
int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
|
||||
struct acpi_gpio_info *info);
|
||||
|
||||
struct gpio_desc *acpi_find_gpio(struct device *dev,
|
||||
struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum gpiod_flags *dflags,
|
||||
unsigned long *lookupflags);
|
||||
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
||||
const char *propname, int index,
|
||||
struct acpi_gpio_info *info);
|
||||
|
||||
int acpi_gpio_count(struct device *dev, const char *con_id);
|
||||
#else
|
||||
|
@ -82,31 +50,13 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
|
|||
static inline void
|
||||
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
|
||||
|
||||
static inline int
|
||||
acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int
|
||||
acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
|
||||
struct acpi_gpio_info *info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct gpio_desc *
|
||||
acpi_find_gpio(struct device *dev, const char *con_id,
|
||||
acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id,
|
||||
unsigned int idx, enum gpiod_flags *dflags,
|
||||
unsigned long *lookupflags)
|
||||
{
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
static inline struct gpio_desc *
|
||||
acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname,
|
||||
int index, struct acpi_gpio_info *info)
|
||||
{
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
static inline int acpi_gpio_count(struct device *dev, const char *con_id)
|
||||
{
|
||||
return -ENODEV;
|
||||
|
|
|
@ -57,6 +57,50 @@ static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_values), 8));
|
|||
* interface to gpiolib GPIOs via ioctl()s.
|
||||
*/
|
||||
|
||||
typedef __poll_t (*poll_fn)(struct file *, struct poll_table_struct *);
|
||||
typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long);
|
||||
typedef ssize_t (*read_fn)(struct file *, char __user *,
|
||||
size_t count, loff_t *);
|
||||
|
||||
static __poll_t call_poll_locked(struct file *file,
|
||||
struct poll_table_struct *wait,
|
||||
struct gpio_device *gdev, poll_fn func)
|
||||
{
|
||||
__poll_t ret;
|
||||
|
||||
down_read(&gdev->sem);
|
||||
ret = func(file, wait);
|
||||
up_read(&gdev->sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long call_ioctl_locked(struct file *file, unsigned int cmd,
|
||||
unsigned long arg, struct gpio_device *gdev,
|
||||
ioctl_fn func)
|
||||
{
|
||||
long ret;
|
||||
|
||||
down_read(&gdev->sem);
|
||||
ret = func(file, cmd, arg);
|
||||
up_read(&gdev->sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t call_read_locked(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *f_ps,
|
||||
struct gpio_device *gdev, read_fn func)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
down_read(&gdev->sem);
|
||||
ret = func(file, buf, count, f_ps);
|
||||
up_read(&gdev->sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPIO line handle management
|
||||
*/
|
||||
|
@ -193,8 +237,8 @@ static long linehandle_set_config(struct linehandle_state *lh,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long linehandle_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct linehandle_state *lh = file->private_data;
|
||||
void __user *ip = (void __user *)arg;
|
||||
|
@ -203,6 +247,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
|
|||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!lh->gdev->chip)
|
||||
return -ENODEV;
|
||||
|
||||
switch (cmd) {
|
||||
case GPIOHANDLE_GET_LINE_VALUES_IOCTL:
|
||||
/* NOTE: It's okay to read values of output lines */
|
||||
|
@ -249,6 +296,15 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
|
|||
}
|
||||
}
|
||||
|
||||
static long linehandle_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct linehandle_state *lh = file->private_data;
|
||||
|
||||
return call_ioctl_locked(file, cmd, arg, lh->gdev,
|
||||
linehandle_ioctl_unlocked);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long linehandle_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
|
@ -412,7 +468,7 @@ out_free_lh:
|
|||
* @desc: the GPIO descriptor for this line.
|
||||
* @req: the corresponding line request
|
||||
* @irq: the interrupt triggered in response to events on this GPIO
|
||||
* @eflags: the edge flags, GPIO_V2_LINE_FLAG_EDGE_RISING and/or
|
||||
* @edflags: the edge flags, GPIO_V2_LINE_FLAG_EDGE_RISING and/or
|
||||
* GPIO_V2_LINE_FLAG_EDGE_FALLING, indicating the edge detection applied
|
||||
* @timestamp_ns: cache for the timestamp storing it between hardirq and
|
||||
* IRQ thread, used to bring the timestamp close to the actual event
|
||||
|
@ -1380,12 +1436,15 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static long linereq_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
static long linereq_ioctl_unlocked(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct linereq *lr = file->private_data;
|
||||
void __user *ip = (void __user *)arg;
|
||||
|
||||
if (!lr->gdev->chip)
|
||||
return -ENODEV;
|
||||
|
||||
switch (cmd) {
|
||||
case GPIO_V2_LINE_GET_VALUES_IOCTL:
|
||||
return linereq_get_values(lr, ip);
|
||||
|
@ -1398,6 +1457,15 @@ static long linereq_ioctl(struct file *file, unsigned int cmd,
|
|||
}
|
||||
}
|
||||
|
||||
static long linereq_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct linereq *lr = file->private_data;
|
||||
|
||||
return call_ioctl_locked(file, cmd, arg, lr->gdev,
|
||||
linereq_ioctl_unlocked);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long linereq_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
|
@ -1406,12 +1474,15 @@ static long linereq_ioctl_compat(struct file *file, unsigned int cmd,
|
|||
}
|
||||
#endif
|
||||
|
||||
static __poll_t linereq_poll(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
static __poll_t linereq_poll_unlocked(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct linereq *lr = file->private_data;
|
||||
__poll_t events = 0;
|
||||
|
||||
if (!lr->gdev->chip)
|
||||
return EPOLLHUP | EPOLLERR;
|
||||
|
||||
poll_wait(file, &lr->wait, wait);
|
||||
|
||||
if (!kfifo_is_empty_spinlocked_noirqsave(&lr->events,
|
||||
|
@ -1421,16 +1492,25 @@ static __poll_t linereq_poll(struct file *file,
|
|||
return events;
|
||||
}
|
||||
|
||||
static ssize_t linereq_read(struct file *file,
|
||||
char __user *buf,
|
||||
size_t count,
|
||||
loff_t *f_ps)
|
||||
static __poll_t linereq_poll(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct linereq *lr = file->private_data;
|
||||
|
||||
return call_poll_locked(file, wait, lr->gdev, linereq_poll_unlocked);
|
||||
}
|
||||
|
||||
static ssize_t linereq_read_unlocked(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *f_ps)
|
||||
{
|
||||
struct linereq *lr = file->private_data;
|
||||
struct gpio_v2_line_event le;
|
||||
ssize_t bytes_read = 0;
|
||||
int ret;
|
||||
|
||||
if (!lr->gdev->chip)
|
||||
return -ENODEV;
|
||||
|
||||
if (count < sizeof(le))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1475,6 +1555,15 @@ static ssize_t linereq_read(struct file *file,
|
|||
return bytes_read;
|
||||
}
|
||||
|
||||
static ssize_t linereq_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *f_ps)
|
||||
{
|
||||
struct linereq *lr = file->private_data;
|
||||
|
||||
return call_read_locked(file, buf, count, f_ps, lr->gdev,
|
||||
linereq_read_unlocked);
|
||||
}
|
||||
|
||||
static void linereq_free(struct linereq *lr)
|
||||
{
|
||||
unsigned int i;
|
||||
|
@ -1712,12 +1801,15 @@ struct lineevent_state {
|
|||
(GPIOEVENT_REQUEST_RISING_EDGE | \
|
||||
GPIOEVENT_REQUEST_FALLING_EDGE)
|
||||
|
||||
static __poll_t lineevent_poll(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
static __poll_t lineevent_poll_unlocked(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct lineevent_state *le = file->private_data;
|
||||
__poll_t events = 0;
|
||||
|
||||
if (!le->gdev->chip)
|
||||
return EPOLLHUP | EPOLLERR;
|
||||
|
||||
poll_wait(file, &le->wait, wait);
|
||||
|
||||
if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock))
|
||||
|
@ -1726,15 +1818,21 @@ static __poll_t lineevent_poll(struct file *file,
|
|||
return events;
|
||||
}
|
||||
|
||||
static __poll_t lineevent_poll(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct lineevent_state *le = file->private_data;
|
||||
|
||||
return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked);
|
||||
}
|
||||
|
||||
struct compat_gpioeevent_data {
|
||||
compat_u64 timestamp;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
static ssize_t lineevent_read(struct file *file,
|
||||
char __user *buf,
|
||||
size_t count,
|
||||
loff_t *f_ps)
|
||||
static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *f_ps)
|
||||
{
|
||||
struct lineevent_state *le = file->private_data;
|
||||
struct gpioevent_data ge;
|
||||
|
@ -1742,6 +1840,9 @@ static ssize_t lineevent_read(struct file *file,
|
|||
ssize_t ge_size;
|
||||
int ret;
|
||||
|
||||
if (!le->gdev->chip)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* When compatible system call is being used the struct gpioevent_data,
|
||||
* in case of at least ia32, has different size due to the alignment
|
||||
|
@ -1799,6 +1900,15 @@ static ssize_t lineevent_read(struct file *file,
|
|||
return bytes_read;
|
||||
}
|
||||
|
||||
static ssize_t lineevent_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *f_ps)
|
||||
{
|
||||
struct lineevent_state *le = file->private_data;
|
||||
|
||||
return call_read_locked(file, buf, count, f_ps, le->gdev,
|
||||
lineevent_read_unlocked);
|
||||
}
|
||||
|
||||
static void lineevent_free(struct lineevent_state *le)
|
||||
{
|
||||
if (le->irq)
|
||||
|
@ -1816,13 +1926,16 @@ static int lineevent_release(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long lineevent_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
static long lineevent_ioctl_unlocked(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct lineevent_state *le = file->private_data;
|
||||
void __user *ip = (void __user *)arg;
|
||||
struct gpiohandle_data ghd;
|
||||
|
||||
if (!le->gdev->chip)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* We can get the value for an event line but not set it,
|
||||
* because it is input by definition.
|
||||
|
@ -1845,6 +1958,15 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static long lineevent_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct lineevent_state *le = file->private_data;
|
||||
|
||||
return call_ioctl_locked(file, cmd, arg, le->gdev,
|
||||
lineevent_ioctl_unlocked);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long lineevent_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
|
@ -2403,12 +2525,15 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
|
|||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static __poll_t lineinfo_watch_poll(struct file *file,
|
||||
struct poll_table_struct *pollt)
|
||||
static __poll_t lineinfo_watch_poll_unlocked(struct file *file,
|
||||
struct poll_table_struct *pollt)
|
||||
{
|
||||
struct gpio_chardev_data *cdev = file->private_data;
|
||||
__poll_t events = 0;
|
||||
|
||||
if (!cdev->gdev->chip)
|
||||
return EPOLLHUP | EPOLLERR;
|
||||
|
||||
poll_wait(file, &cdev->wait, pollt);
|
||||
|
||||
if (!kfifo_is_empty_spinlocked_noirqsave(&cdev->events,
|
||||
|
@ -2418,8 +2543,17 @@ static __poll_t lineinfo_watch_poll(struct file *file,
|
|||
return events;
|
||||
}
|
||||
|
||||
static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *off)
|
||||
static __poll_t lineinfo_watch_poll(struct file *file,
|
||||
struct poll_table_struct *pollt)
|
||||
{
|
||||
struct gpio_chardev_data *cdev = file->private_data;
|
||||
|
||||
return call_poll_locked(file, pollt, cdev->gdev,
|
||||
lineinfo_watch_poll_unlocked);
|
||||
}
|
||||
|
||||
static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct gpio_chardev_data *cdev = file->private_data;
|
||||
struct gpio_v2_line_info_changed event;
|
||||
|
@ -2427,6 +2561,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
|
|||
int ret;
|
||||
size_t event_size;
|
||||
|
||||
if (!cdev->gdev->chip)
|
||||
return -ENODEV;
|
||||
|
||||
#ifndef CONFIG_GPIO_CDEV_V1
|
||||
event_size = sizeof(struct gpio_v2_line_info_changed);
|
||||
if (count < event_size)
|
||||
|
@ -2494,6 +2631,15 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
|
|||
return bytes_read;
|
||||
}
|
||||
|
||||
static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct gpio_chardev_data *cdev = file->private_data;
|
||||
|
||||
return call_read_locked(file, buf, count, off, cdev->gdev,
|
||||
lineinfo_watch_read_unlocked);
|
||||
}
|
||||
|
||||
/**
|
||||
* gpio_chrdev_open() - open the chardev for ioctl operations
|
||||
* @inode: inode for this chardev
|
||||
|
@ -2507,13 +2653,17 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
|
|||
struct gpio_chardev_data *cdev;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
down_read(&gdev->sem);
|
||||
|
||||
/* Fail on open if the backing gpiochip is gone */
|
||||
if (!gdev->chip)
|
||||
return -ENODEV;
|
||||
if (!gdev->chip) {
|
||||
ret = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
|
||||
if (!cdev)
|
||||
return -ENOMEM;
|
||||
goto out_unlock;
|
||||
|
||||
cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL);
|
||||
if (!cdev->watched_lines)
|
||||
|
@ -2536,6 +2686,8 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
|
|||
if (ret)
|
||||
goto out_unregister_notifier;
|
||||
|
||||
up_read(&gdev->sem);
|
||||
|
||||
return ret;
|
||||
|
||||
out_unregister_notifier:
|
||||
|
@ -2545,6 +2697,8 @@ out_free_bitmap:
|
|||
bitmap_free(cdev->watched_lines);
|
||||
out_free_cdev:
|
||||
kfree(cdev);
|
||||
out_unlock:
|
||||
up_read(&gdev->sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
|
|||
{
|
||||
struct of_phandle_args *gpiospec = data;
|
||||
|
||||
return chip->gpiodev->dev.of_node == gpiospec->np &&
|
||||
return device_match_of_node(&chip->gpiodev->dev, gpiospec->np) &&
|
||||
chip->of_xlate &&
|
||||
chip->of_xlate(chip, gpiospec, NULL) >= 0;
|
||||
}
|
||||
|
@ -112,22 +112,123 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
|
|||
return gpiochip_get_desc(chip, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_gpio_need_valid_mask() - figure out if the OF GPIO driver needs
|
||||
* to set the .valid_mask
|
||||
* @gc: the target gpio_chip
|
||||
*
|
||||
* Return: true if the valid mask needs to be set
|
||||
/*
|
||||
* Overrides stated polarity of a gpio line and warns when there is a
|
||||
* discrepancy.
|
||||
*/
|
||||
bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
|
||||
static void of_gpio_quirk_polarity(const struct device_node *np,
|
||||
bool active_high,
|
||||
enum of_gpio_flags *flags)
|
||||
{
|
||||
int size;
|
||||
const struct device_node *np = gc->of_node;
|
||||
if (active_high) {
|
||||
if (*flags & OF_GPIO_ACTIVE_LOW) {
|
||||
pr_warn("%s GPIO handle specifies active low - ignored\n",
|
||||
of_node_full_name(np));
|
||||
*flags &= ~OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
} else {
|
||||
if (!(*flags & OF_GPIO_ACTIVE_LOW))
|
||||
pr_info("%s enforce active low on GPIO handle\n",
|
||||
of_node_full_name(np));
|
||||
*flags |= OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
}
|
||||
|
||||
size = of_property_count_u32_elems(np, "gpio-reserved-ranges");
|
||||
if (size > 0 && size % 2 == 0)
|
||||
return true;
|
||||
return false;
|
||||
/*
|
||||
* This quirk does static polarity overrides in cases where existing
|
||||
* DTS specified incorrect polarity.
|
||||
*/
|
||||
static void of_gpio_try_fixup_polarity(const struct device_node *np,
|
||||
const char *propname,
|
||||
enum of_gpio_flags *flags)
|
||||
{
|
||||
static const struct {
|
||||
const char *compatible;
|
||||
const char *propname;
|
||||
bool active_high;
|
||||
} gpios[] = {
|
||||
#if !IS_ENABLED(CONFIG_LCD_HX8357)
|
||||
/*
|
||||
* Himax LCD controllers used incorrectly named
|
||||
* "gpios-reset" property and also specified wrong
|
||||
* polarity.
|
||||
*/
|
||||
{ "himax,hx8357", "gpios-reset", false },
|
||||
{ "himax,hx8369", "gpios-reset", false },
|
||||
#endif
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gpios); i++) {
|
||||
if (of_device_is_compatible(np, gpios[i].compatible) &&
|
||||
!strcmp(propname, gpios[i].propname)) {
|
||||
of_gpio_quirk_polarity(np, gpios[i].active_high, flags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void of_gpio_set_polarity_by_property(const struct device_node *np,
|
||||
const char *propname,
|
||||
enum of_gpio_flags *flags)
|
||||
{
|
||||
static const struct {
|
||||
const char *compatible;
|
||||
const char *gpio_propname;
|
||||
const char *polarity_propname;
|
||||
} gpios[] = {
|
||||
#if IS_ENABLED(CONFIG_FEC)
|
||||
/* Freescale Fast Ethernet Controller */
|
||||
{ "fsl,imx25-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,imx27-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,imx28-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,imx6q-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,mvf600-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,imx6sx-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,imx6ul-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,imx8mq-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,imx8qm-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
{ "fsl,s32v234-fec", "phy-reset-gpios", "phy-reset-active-high" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_PCI_IMX6)
|
||||
{ "fsl,imx6q-pcie", "reset-gpio", "reset-gpio-active-high" },
|
||||
{ "fsl,imx6sx-pcie", "reset-gpio", "reset-gpio-active-high" },
|
||||
{ "fsl,imx6qp-pcie", "reset-gpio", "reset-gpio-active-high" },
|
||||
{ "fsl,imx7d-pcie", "reset-gpio", "reset-gpio-active-high" },
|
||||
{ "fsl,imx8mq-pcie", "reset-gpio", "reset-gpio-active-high" },
|
||||
{ "fsl,imx8mm-pcie", "reset-gpio", "reset-gpio-active-high" },
|
||||
{ "fsl,imx8mp-pcie", "reset-gpio", "reset-gpio-active-high" },
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The regulator GPIO handles are specified such that the
|
||||
* presence or absence of "enable-active-high" solely controls
|
||||
* the polarity of the GPIO line. Any phandle flags must
|
||||
* be actively ignored.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_REGULATOR_FIXED_VOLTAGE)
|
||||
{ "regulator-fixed", "gpios", "enable-active-high" },
|
||||
{ "regulator-fixed", "gpio", "enable-active-high" },
|
||||
{ "reg-fixed-voltage", "gpios", "enable-active-high" },
|
||||
{ "reg-fixed-voltage", "gpio", "enable-active-high" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_REGULATOR_GPIO)
|
||||
{ "regulator-gpio", "enable-gpio", "enable-active-high" },
|
||||
{ "regulator-gpio", "enable-gpios", "enable-active-high" },
|
||||
#endif
|
||||
};
|
||||
unsigned int i;
|
||||
bool active_high;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gpios); i++) {
|
||||
if (of_device_is_compatible(np, gpios[i].compatible) &&
|
||||
!strcmp(propname, gpios[i].gpio_propname)) {
|
||||
active_high = of_property_read_bool(np,
|
||||
gpios[i].polarity_propname);
|
||||
of_gpio_quirk_polarity(np, active_high, flags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void of_gpio_flags_quirks(const struct device_node *np,
|
||||
|
@ -135,32 +236,9 @@ static void of_gpio_flags_quirks(const struct device_node *np,
|
|||
enum of_gpio_flags *flags,
|
||||
int index)
|
||||
{
|
||||
/*
|
||||
* Some GPIO fixed regulator quirks.
|
||||
* Note that active low is the default.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_REGULATOR) &&
|
||||
(of_device_is_compatible(np, "regulator-fixed") ||
|
||||
of_device_is_compatible(np, "reg-fixed-voltage") ||
|
||||
(!(strcmp(propname, "enable-gpio") &&
|
||||
strcmp(propname, "enable-gpios")) &&
|
||||
of_device_is_compatible(np, "regulator-gpio")))) {
|
||||
bool active_low = !of_property_read_bool(np,
|
||||
"enable-active-high");
|
||||
/*
|
||||
* The regulator GPIO handles are specified such that the
|
||||
* presence or absence of "enable-active-high" solely controls
|
||||
* the polarity of the GPIO line. Any phandle flags must
|
||||
* be actively ignored.
|
||||
*/
|
||||
if ((*flags & OF_GPIO_ACTIVE_LOW) && !active_low) {
|
||||
pr_warn("%s GPIO handle specifies active low - ignored\n",
|
||||
of_node_full_name(np));
|
||||
*flags &= ~OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
if (active_low)
|
||||
*flags |= OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
of_gpio_try_fixup_polarity(np, propname, flags);
|
||||
of_gpio_set_polarity_by_property(np, propname, flags);
|
||||
|
||||
/*
|
||||
* Legacy open drain handling for fixed voltage regulators.
|
||||
*/
|
||||
|
@ -200,18 +278,10 @@ static void of_gpio_flags_quirks(const struct device_node *np,
|
|||
* conflict and the "spi-cs-high" flag will
|
||||
* take precedence.
|
||||
*/
|
||||
if (of_property_read_bool(child, "spi-cs-high")) {
|
||||
if (*flags & OF_GPIO_ACTIVE_LOW) {
|
||||
pr_warn("%s GPIO handle specifies active low - ignored\n",
|
||||
of_node_full_name(child));
|
||||
*flags &= ~OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
} else {
|
||||
if (!(*flags & OF_GPIO_ACTIVE_LOW))
|
||||
pr_info("%s enforce active low on chipselect handle\n",
|
||||
of_node_full_name(child));
|
||||
*flags |= OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
bool active_high = of_property_read_bool(child,
|
||||
"spi-cs-high");
|
||||
of_gpio_quirk_polarity(child, active_high,
|
||||
flags);
|
||||
of_node_put(child);
|
||||
break;
|
||||
}
|
||||
|
@ -365,127 +435,164 @@ struct gpio_desc *gpiod_get_from_of_node(const struct device_node *node,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_get_from_of_node);
|
||||
|
||||
/*
|
||||
* The SPI GPIO bindings happened before we managed to establish that GPIO
|
||||
* properties should be named "foo-gpios" so we have this special kludge for
|
||||
* them.
|
||||
*/
|
||||
static struct gpio_desc *of_find_spi_gpio(struct device_node *np,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum of_gpio_flags *of_flags)
|
||||
{
|
||||
char prop_name[32]; /* 32 is max size of property name */
|
||||
|
||||
/*
|
||||
* Hopefully the compiler stubs the rest of the function if this
|
||||
* is false.
|
||||
*/
|
||||
if (!IS_ENABLED(CONFIG_SPI_MASTER))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
/* Allow this specifically for "spi-gpio" devices */
|
||||
if (!of_device_is_compatible(np, "spi-gpio") || !con_id)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
/* Will be "gpio-sck", "gpio-mosi" or "gpio-miso" */
|
||||
snprintf(prop_name, sizeof(prop_name), "%s-%s", "gpio", con_id);
|
||||
|
||||
return of_get_named_gpiod_flags(np, prop_name, idx, of_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* The old Freescale bindings use simply "gpios" as name for the chip select
|
||||
* lines rather than "cs-gpios" like all other SPI hardware. Account for this
|
||||
* with a special quirk.
|
||||
*/
|
||||
static struct gpio_desc *of_find_spi_cs_gpio(struct device_node *np,
|
||||
static struct gpio_desc *of_find_gpio_rename(struct device_node *np,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum of_gpio_flags *of_flags)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_SPI_MASTER))
|
||||
return ERR_PTR(-ENOENT);
|
||||
static const struct of_rename_gpio {
|
||||
const char *con_id;
|
||||
const char *legacy_id; /* NULL - same as con_id */
|
||||
/*
|
||||
* Compatible string can be set to NULL in case where
|
||||
* matching to a particular compatible is not practical,
|
||||
* but it should only be done for gpio names that have
|
||||
* vendor prefix to reduce risk of false positives.
|
||||
* Addition of such entries is strongly discouraged.
|
||||
*/
|
||||
const char *compatible;
|
||||
} gpios[] = {
|
||||
#if !IS_ENABLED(CONFIG_LCD_HX8357)
|
||||
/* Himax LCD controllers used "gpios-reset" */
|
||||
{ "reset", "gpios-reset", "himax,hx8357" },
|
||||
{ "reset", "gpios-reset", "himax,hx8369" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_MFD_ARIZONA)
|
||||
{ "wlf,reset", NULL, NULL },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_RTC_DRV_MOXART)
|
||||
{ "rtc-data", "gpio-rtc-data", "moxa,moxart-rtc" },
|
||||
{ "rtc-sclk", "gpio-rtc-sclk", "moxa,moxart-rtc" },
|
||||
{ "rtc-reset", "gpio-rtc-reset", "moxa,moxart-rtc" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_NFC_MRVL_I2C)
|
||||
{ "reset", "reset-n-io", "marvell,nfc-i2c" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_NFC_MRVL_SPI)
|
||||
{ "reset", "reset-n-io", "marvell,nfc-spi" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_NFC_MRVL_UART)
|
||||
{ "reset", "reset-n-io", "marvell,nfc-uart" },
|
||||
{ "reset", "reset-n-io", "mrvl,nfc-uart" },
|
||||
#endif
|
||||
#if !IS_ENABLED(CONFIG_PCI_LANTIQ)
|
||||
/* MIPS Lantiq PCI */
|
||||
{ "reset", "gpios-reset", "lantiq,pci-xway" },
|
||||
#endif
|
||||
|
||||
/* Allow this specifically for Freescale and PPC devices */
|
||||
if (!of_device_is_compatible(np, "fsl,spi") &&
|
||||
!of_device_is_compatible(np, "aeroflexgaisler,spictrl") &&
|
||||
!of_device_is_compatible(np, "ibm,ppc4xx-spi"))
|
||||
return ERR_PTR(-ENOENT);
|
||||
/* Allow only if asking for "cs-gpios" */
|
||||
if (!con_id || strcmp(con_id, "cs"))
|
||||
return ERR_PTR(-ENOENT);
|
||||
/*
|
||||
* Some regulator bindings happened before we managed to
|
||||
* establish that GPIO properties should be named
|
||||
* "foo-gpios" so we have this special kludge for them.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_REGULATOR_ARIZONA_LDO1)
|
||||
{ "wlf,ldoena", NULL, NULL }, /* Arizona */
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_REGULATOR_WM8994)
|
||||
{ "wlf,ldo1ena", NULL, NULL }, /* WM8994 */
|
||||
{ "wlf,ldo2ena", NULL, NULL }, /* WM8994 */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* While all other SPI controllers use "cs-gpios" the Freescale
|
||||
* uses just "gpios" so translate to that when "cs-gpios" is
|
||||
* requested.
|
||||
*/
|
||||
return of_get_named_gpiod_flags(np, "gpios", idx, of_flags);
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_CS42L56)
|
||||
{ "reset", "cirrus,gpio-nreset", "cirrus,cs42l56" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_TLV320AIC3X)
|
||||
{ "reset", "gpio-reset", "ti,tlv320aic3x" },
|
||||
{ "reset", "gpio-reset", "ti,tlv320aic33" },
|
||||
{ "reset", "gpio-reset", "ti,tlv320aic3007" },
|
||||
{ "reset", "gpio-reset", "ti,tlv320aic3104" },
|
||||
{ "reset", "gpio-reset", "ti,tlv320aic3106" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_SPI_GPIO)
|
||||
/*
|
||||
* The SPI GPIO bindings happened before we managed to
|
||||
* establish that GPIO properties should be named
|
||||
* "foo-gpios" so we have this special kludge for them.
|
||||
*/
|
||||
{ "miso", "gpio-miso", "spi-gpio" },
|
||||
{ "mosi", "gpio-mosi", "spi-gpio" },
|
||||
{ "sck", "gpio-sck", "spi-gpio" },
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Some regulator bindings happened before we managed to establish that GPIO
|
||||
* properties should be named "foo-gpios" so we have this special kludge for
|
||||
* them.
|
||||
*/
|
||||
static struct gpio_desc *of_find_regulator_gpio(struct device_node *np,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum of_gpio_flags *of_flags)
|
||||
{
|
||||
/* These are the connection IDs we accept as legacy GPIO phandles */
|
||||
const char *whitelist[] = {
|
||||
"wlf,ldoena", /* Arizona */
|
||||
"wlf,ldo1ena", /* WM8994 */
|
||||
"wlf,ldo2ena", /* WM8994 */
|
||||
/*
|
||||
* The old Freescale bindings use simply "gpios" as name
|
||||
* for the chip select lines rather than "cs-gpios" like
|
||||
* all other SPI hardware. Allow this specifically for
|
||||
* Freescale and PPC devices.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_SPI_FSL_SPI)
|
||||
{ "cs", "gpios", "fsl,spi" },
|
||||
{ "cs", "gpios", "aeroflexgaisler,spictrl" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_SPI_PPC4xx)
|
||||
{ "cs", "gpios", "ibm,ppc4xx-spi" },
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_TYPEC_FUSB302)
|
||||
/*
|
||||
* Fairchild FUSB302 host is using undocumented "fcs,int_n"
|
||||
* property without the compulsory "-gpios" suffix.
|
||||
*/
|
||||
{ "fcs,int_n", NULL, "fcs,fusb302" },
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_REGULATOR))
|
||||
return ERR_PTR(-ENOENT);
|
||||
struct gpio_desc *desc;
|
||||
const char *legacy_id;
|
||||
unsigned int i;
|
||||
|
||||
if (!con_id)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
i = match_string(whitelist, ARRAY_SIZE(whitelist), con_id);
|
||||
if (i < 0)
|
||||
return ERR_PTR(-ENOENT);
|
||||
for (i = 0; i < ARRAY_SIZE(gpios); i++) {
|
||||
if (strcmp(con_id, gpios[i].con_id))
|
||||
continue;
|
||||
|
||||
return of_get_named_gpiod_flags(np, con_id, idx, of_flags);
|
||||
if (gpios[i].compatible &&
|
||||
!of_device_is_compatible(np, gpios[i].compatible))
|
||||
continue;
|
||||
|
||||
legacy_id = gpios[i].legacy_id ?: gpios[i].con_id;
|
||||
desc = of_get_named_gpiod_flags(np, legacy_id, idx, of_flags);
|
||||
if (!gpiod_not_found(desc)) {
|
||||
pr_info("%s uses legacy gpio name '%s' instead of '%s-gpios'\n",
|
||||
of_node_full_name(np), legacy_id, con_id);
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
static struct gpio_desc *of_find_arizona_gpio(struct device_node *np,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum of_gpio_flags *of_flags)
|
||||
static struct gpio_desc *of_find_mt2701_gpio(struct device_node *np,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum of_gpio_flags *of_flags)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_MFD_ARIZONA))
|
||||
struct gpio_desc *desc;
|
||||
const char *legacy_id;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SND_SOC_MT2701_CS42448))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
if (!con_id || strcmp(con_id, "wlf,reset"))
|
||||
if (!of_device_is_compatible(np, "mediatek,mt2701-cs42448-machine"))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
return of_get_named_gpiod_flags(np, con_id, idx, of_flags);
|
||||
}
|
||||
|
||||
static struct gpio_desc *of_find_usb_gpio(struct device_node *np,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum of_gpio_flags *of_flags)
|
||||
{
|
||||
/*
|
||||
* Currently this USB quirk is only for the Fairchild FUSB302 host
|
||||
* which is using an undocumented DT GPIO line named "fcs,int_n"
|
||||
* without the compulsory "-gpios" suffix.
|
||||
*/
|
||||
if (!IS_ENABLED(CONFIG_TYPEC_FUSB302))
|
||||
if (!con_id || strcmp(con_id, "i2s1-in-sel"))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
if (!con_id || strcmp(con_id, "fcs,int_n"))
|
||||
if (idx == 0)
|
||||
legacy_id = "i2s1-in-sel-gpio1";
|
||||
else if (idx == 1)
|
||||
legacy_id = "i2s1-in-sel-gpio2";
|
||||
else
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
return of_get_named_gpiod_flags(np, con_id, idx, of_flags);
|
||||
desc = of_get_named_gpiod_flags(np, legacy_id, 0, of_flags);
|
||||
if (!gpiod_not_found(desc))
|
||||
pr_info("%s is using legacy gpio name '%s' instead of '%s-gpios'\n",
|
||||
of_node_full_name(np), legacy_id, con_id);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
typedef struct gpio_desc *(*of_find_gpio_quirk)(struct device_node *np,
|
||||
|
@ -493,15 +600,12 @@ typedef struct gpio_desc *(*of_find_gpio_quirk)(struct device_node *np,
|
|||
unsigned int idx,
|
||||
enum of_gpio_flags *of_flags);
|
||||
static const of_find_gpio_quirk of_find_gpio_quirks[] = {
|
||||
of_find_spi_gpio,
|
||||
of_find_spi_cs_gpio,
|
||||
of_find_regulator_gpio,
|
||||
of_find_arizona_gpio,
|
||||
of_find_usb_gpio,
|
||||
of_find_gpio_rename,
|
||||
of_find_mt2701_gpio,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
|
||||
struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
|
||||
unsigned int idx, unsigned long *flags)
|
||||
{
|
||||
char prop_name[32]; /* 32 is max size of property name */
|
||||
|
@ -519,8 +623,7 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
|
|||
snprintf(prop_name, sizeof(prop_name), "%s",
|
||||
gpio_suffixes[i]);
|
||||
|
||||
desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
|
||||
&of_flags);
|
||||
desc = of_get_named_gpiod_flags(np, prop_name, idx, &of_flags);
|
||||
|
||||
if (!gpiod_not_found(desc))
|
||||
break;
|
||||
|
@ -528,7 +631,7 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
|
|||
|
||||
/* Properly named GPIO was not found, try workarounds */
|
||||
for (q = of_find_gpio_quirks; gpiod_not_found(desc) && *q; q++)
|
||||
desc = (*q)(dev->of_node, con_id, idx, &of_flags);
|
||||
desc = (*q)(np, con_id, idx, &of_flags);
|
||||
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
|
@ -831,8 +934,8 @@ int of_mm_gpiochip_add_data(struct device_node *np,
|
|||
if (mm_gc->save_regs)
|
||||
mm_gc->save_regs(mm_gc);
|
||||
|
||||
of_node_put(mm_gc->gc.of_node);
|
||||
mm_gc->gc.of_node = of_node_get(np);
|
||||
fwnode_handle_put(mm_gc->gc.fwnode);
|
||||
mm_gc->gc.fwnode = fwnode_handle_get(of_fwnode_handle(np));
|
||||
|
||||
ret = gpiochip_add_data(gc, data);
|
||||
if (ret)
|
||||
|
@ -858,37 +961,12 @@ void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
|
|||
{
|
||||
struct gpio_chip *gc = &mm_gc->gc;
|
||||
|
||||
if (!mm_gc)
|
||||
return;
|
||||
|
||||
gpiochip_remove(gc);
|
||||
iounmap(mm_gc->regs);
|
||||
kfree(gc->label);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_mm_gpiochip_remove);
|
||||
|
||||
static void of_gpiochip_init_valid_mask(struct gpio_chip *chip)
|
||||
{
|
||||
int len, i;
|
||||
u32 start, count;
|
||||
struct device_node *np = chip->of_node;
|
||||
|
||||
len = of_property_count_u32_elems(np, "gpio-reserved-ranges");
|
||||
if (len < 0 || len % 2 != 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < len; i += 2) {
|
||||
of_property_read_u32_index(np, "gpio-reserved-ranges",
|
||||
i, &start);
|
||||
of_property_read_u32_index(np, "gpio-reserved-ranges",
|
||||
i + 1, &count);
|
||||
if (start >= chip->ngpio || start + count > chip->ngpio)
|
||||
continue;
|
||||
|
||||
bitmap_clear(chip->valid_mask, start, count);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
|
||||
{
|
||||
|
@ -982,9 +1060,11 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { return 0; }
|
|||
|
||||
int of_gpiochip_add(struct gpio_chip *chip)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
if (!chip->of_node)
|
||||
np = to_of_node(dev_fwnode(&chip->gpiodev->dev));
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
if (!chip->of_xlate) {
|
||||
|
@ -995,24 +1075,22 @@ int of_gpiochip_add(struct gpio_chip *chip)
|
|||
if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
|
||||
return -EINVAL;
|
||||
|
||||
of_gpiochip_init_valid_mask(chip);
|
||||
|
||||
ret = of_gpiochip_add_pin_range(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
of_node_get(chip->of_node);
|
||||
fwnode_handle_get(chip->fwnode);
|
||||
|
||||
ret = of_gpiochip_scan_gpios(chip);
|
||||
if (ret)
|
||||
of_node_put(chip->of_node);
|
||||
fwnode_handle_put(chip->fwnode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void of_gpiochip_remove(struct gpio_chip *chip)
|
||||
{
|
||||
of_node_put(chip->of_node);
|
||||
fwnode_handle_put(chip->fwnode);
|
||||
}
|
||||
|
||||
void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev)
|
||||
|
|
|
@ -16,17 +16,16 @@ struct gpio_desc;
|
|||
struct gpio_device;
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
struct gpio_desc *of_find_gpio(struct device *dev,
|
||||
struct gpio_desc *of_find_gpio(struct device_node *np,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
unsigned long *lookupflags);
|
||||
int of_gpiochip_add(struct gpio_chip *gc);
|
||||
void of_gpiochip_remove(struct gpio_chip *gc);
|
||||
int of_gpio_get_count(struct device *dev, const char *con_id);
|
||||
bool of_gpio_need_valid_mask(const struct gpio_chip *gc);
|
||||
void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev);
|
||||
#else
|
||||
static inline struct gpio_desc *of_find_gpio(struct device *dev,
|
||||
static inline struct gpio_desc *of_find_gpio(struct device_node *np,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
unsigned long *lookupflags)
|
||||
|
@ -39,10 +38,6 @@ static inline int of_gpio_get_count(struct device *dev, const char *con_id)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline void of_gpio_dev_init(struct gpio_chip *gc,
|
||||
struct gpio_device *gdev)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Software Node helpers for the GPIO API
|
||||
*
|
||||
* Copyright 2022 Google LLC
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
#include "gpiolib-swnode.h"
|
||||
|
||||
static void swnode_format_propname(const char *con_id, char *propname,
|
||||
size_t max_size)
|
||||
{
|
||||
/*
|
||||
* Note we do not need to try both -gpios and -gpio suffixes,
|
||||
* as, unlike OF and ACPI, we can fix software nodes to conform
|
||||
* to the proper binding.
|
||||
*/
|
||||
if (con_id)
|
||||
snprintf(propname, max_size, "%s-gpios", con_id);
|
||||
else
|
||||
strscpy(propname, "gpios", max_size);
|
||||
}
|
||||
|
||||
static int swnode_gpiochip_match_name(struct gpio_chip *chip, void *data)
|
||||
{
|
||||
return !strcmp(chip->label, data);
|
||||
}
|
||||
|
||||
static struct gpio_chip *swnode_get_chip(struct fwnode_handle *fwnode)
|
||||
{
|
||||
const struct software_node *chip_node;
|
||||
struct gpio_chip *chip;
|
||||
|
||||
chip_node = to_software_node(fwnode);
|
||||
if (!chip_node || !chip_node->name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
chip = gpiochip_find((void *)chip_node->name, swnode_gpiochip_match_name);
|
||||
return chip ?: ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
|
||||
const char *con_id, unsigned int idx,
|
||||
unsigned long *flags)
|
||||
{
|
||||
const struct software_node *swnode;
|
||||
struct fwnode_reference_args args;
|
||||
struct gpio_chip *chip;
|
||||
struct gpio_desc *desc;
|
||||
char propname[32]; /* 32 is max size of property name */
|
||||
int error;
|
||||
|
||||
swnode = to_software_node(fwnode);
|
||||
if (!swnode)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
swnode_format_propname(con_id, propname, sizeof(propname));
|
||||
|
||||
/*
|
||||
* We expect all swnode-described GPIOs have GPIO number and
|
||||
* polarity arguments, hence nargs is set to 2.
|
||||
*/
|
||||
error = fwnode_property_get_reference_args(fwnode, propname, NULL, 2, idx, &args);
|
||||
if (error) {
|
||||
pr_debug("%s: can't parse '%s' property of node '%pfwP[%d]'\n",
|
||||
__func__, propname, fwnode, idx);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
chip = swnode_get_chip(args.fwnode);
|
||||
fwnode_handle_put(args.fwnode);
|
||||
if (IS_ERR(chip))
|
||||
return ERR_CAST(chip);
|
||||
|
||||
desc = gpiochip_get_desc(chip, args.args[0]);
|
||||
*flags = args.args[1]; /* We expect native GPIO flags */
|
||||
|
||||
pr_debug("%s: parsed '%s' property of node '%pfwP[%d]' - status (%d)\n",
|
||||
__func__, propname, fwnode, idx, PTR_ERR_OR_ZERO(desc));
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* swnode_gpio_count - count the GPIOs associated with a device / function
|
||||
* @fwnode: firmware node of the GPIO consumer, can be %NULL for
|
||||
* system-global GPIOs
|
||||
* @con_id: function within the GPIO consumer
|
||||
*
|
||||
* Return:
|
||||
* The number of GPIOs associated with a device / function or %-ENOENT,
|
||||
* if no GPIO has been assigned to the requested function.
|
||||
*/
|
||||
int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
|
||||
{
|
||||
struct fwnode_reference_args args;
|
||||
char propname[32];
|
||||
int count;
|
||||
|
||||
swnode_format_propname(con_id, propname, sizeof(propname));
|
||||
|
||||
/*
|
||||
* This is not very efficient, but GPIO lists usually have only
|
||||
* 1 or 2 entries.
|
||||
*/
|
||||
count = 0;
|
||||
while (fwnode_property_get_reference_args(fwnode, propname, NULL, 0,
|
||||
count, &args) == 0) {
|
||||
fwnode_handle_put(args.fwnode);
|
||||
count++;
|
||||
}
|
||||
|
||||
return count ?: -ENOENT;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef GPIOLIB_SWNODE_H
|
||||
#define GPIOLIB_SWNODE_H
|
||||
|
||||
struct fwnode_handle;
|
||||
struct gpio_desc;
|
||||
|
||||
struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
|
||||
const char *con_id, unsigned int idx,
|
||||
unsigned long *flags);
|
||||
int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id);
|
||||
|
||||
#endif /* GPIOLIB_SWNODE_H */
|
|
@ -26,6 +26,7 @@
|
|||
#include "gpiolib.h"
|
||||
#include "gpiolib-of.h"
|
||||
#include "gpiolib-acpi.h"
|
||||
#include "gpiolib-swnode.h"
|
||||
#include "gpiolib-cdev.h"
|
||||
#include "gpiolib-sysfs.h"
|
||||
|
||||
|
@ -183,14 +184,14 @@ EXPORT_SYMBOL_GPL(gpiod_to_chip);
|
|||
static int gpiochip_find_base(int ngpio)
|
||||
{
|
||||
struct gpio_device *gdev;
|
||||
int base = ARCH_NR_GPIOS - ngpio;
|
||||
int base = GPIO_DYNAMIC_BASE;
|
||||
|
||||
list_for_each_entry_reverse(gdev, &gpio_devices, list) {
|
||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||
/* found a free space? */
|
||||
if (gdev->base + gdev->ngpio <= base)
|
||||
if (gdev->base >= base + ngpio)
|
||||
break;
|
||||
/* nope, check the space right before the chip */
|
||||
base = gdev->base - ngpio;
|
||||
/* nope, check the space right after the chip */
|
||||
base = gdev->base + gdev->ngpio;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(base)) {
|
||||
|
@ -366,12 +367,12 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|||
static int devprop_gpiochip_set_names(struct gpio_chip *chip)
|
||||
{
|
||||
struct gpio_device *gdev = chip->gpiodev;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
|
||||
struct device *dev = &gdev->dev;
|
||||
const char **names;
|
||||
int ret, i;
|
||||
int count;
|
||||
|
||||
count = fwnode_property_string_array_count(fwnode, "gpio-line-names");
|
||||
count = device_property_string_array_count(dev, "gpio-line-names");
|
||||
if (count < 0)
|
||||
return 0;
|
||||
|
||||
|
@ -384,7 +385,7 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip)
|
|||
* gpiochips.
|
||||
*/
|
||||
if (count <= chip->offset) {
|
||||
dev_warn(&gdev->dev, "gpio-line-names too short (length %d), cannot map names for the gpiochip at offset %u\n",
|
||||
dev_warn(dev, "gpio-line-names too short (length %d), cannot map names for the gpiochip at offset %u\n",
|
||||
count, chip->offset);
|
||||
return 0;
|
||||
}
|
||||
|
@ -393,10 +394,10 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip)
|
|||
if (!names)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = fwnode_property_read_string_array(fwnode, "gpio-line-names",
|
||||
ret = device_property_read_string_array(dev, "gpio-line-names",
|
||||
names, count);
|
||||
if (ret < 0) {
|
||||
dev_warn(&gdev->dev, "failed to read GPIO line names\n");
|
||||
dev_warn(dev, "failed to read GPIO line names\n");
|
||||
kfree(names);
|
||||
return ret;
|
||||
}
|
||||
|
@ -445,9 +446,22 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc)
|
|||
return p;
|
||||
}
|
||||
|
||||
static unsigned int gpiochip_count_reserved_ranges(struct gpio_chip *gc)
|
||||
{
|
||||
struct device *dev = &gc->gpiodev->dev;
|
||||
int size;
|
||||
|
||||
/* Format is "start, count, ..." */
|
||||
size = device_property_count_u32(dev, "gpio-reserved-ranges");
|
||||
if (size > 0 && size % 2 == 0)
|
||||
return size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
|
||||
{
|
||||
if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask))
|
||||
if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask))
|
||||
return 0;
|
||||
|
||||
gc->valid_mask = gpiochip_allocate_mask(gc);
|
||||
|
@ -457,8 +471,50 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc)
|
||||
{
|
||||
struct device *dev = &gc->gpiodev->dev;
|
||||
unsigned int size;
|
||||
u32 *ranges;
|
||||
int ret;
|
||||
|
||||
size = gpiochip_count_reserved_ranges(gc);
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
ranges = kmalloc_array(size, sizeof(*ranges), GFP_KERNEL);
|
||||
if (!ranges)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = device_property_read_u32_array(dev, "gpio-reserved-ranges",
|
||||
ranges, size);
|
||||
if (ret) {
|
||||
kfree(ranges);
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (size) {
|
||||
u32 count = ranges[--size];
|
||||
u32 start = ranges[--size];
|
||||
|
||||
if (start >= gc->ngpio || start + count > gc->ngpio)
|
||||
continue;
|
||||
|
||||
bitmap_clear(gc->valid_mask, start, count);
|
||||
}
|
||||
|
||||
kfree(ranges);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpiochip_init_valid_mask(struct gpio_chip *gc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_apply_reserved_ranges(gc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (gc->init_valid_mask)
|
||||
return gc->init_valid_mask(gc,
|
||||
gc->valid_mask,
|
||||
|
@ -493,7 +549,7 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
|
|||
|
||||
static void gpiodevice_release(struct device *dev)
|
||||
{
|
||||
struct gpio_device *gdev = container_of(dev, struct gpio_device, dev);
|
||||
struct gpio_device *gdev = to_gpio_device(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
@ -627,7 +683,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
* Assign fwnode depending on the result of the previous calls,
|
||||
* if none of them succeed, assign it to the parent's one.
|
||||
*/
|
||||
gdev->dev.fwnode = dev_fwnode(&gdev->dev) ?: fwnode;
|
||||
gc->fwnode = gdev->dev.fwnode = dev_fwnode(&gdev->dev) ?: fwnode;
|
||||
|
||||
gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
|
||||
if (gdev->id < 0) {
|
||||
|
@ -719,6 +775,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
* a poison instead.
|
||||
*/
|
||||
gc->base = base;
|
||||
} else {
|
||||
dev_warn(&gdev->dev,
|
||||
"Static allocation of GPIO base is deprecated, use dynamic allocation.\n");
|
||||
}
|
||||
gdev->base = base;
|
||||
|
||||
|
@ -735,6 +794,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier);
|
||||
init_rwsem(&gdev->sem);
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
INIT_LIST_HEAD(&gdev->pin_ranges);
|
||||
|
@ -875,6 +935,8 @@ void gpiochip_remove(struct gpio_chip *gc)
|
|||
unsigned long flags;
|
||||
unsigned int i;
|
||||
|
||||
down_write(&gdev->sem);
|
||||
|
||||
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
|
||||
gpiochip_sysfs_unregister(gdev);
|
||||
gpiochip_free_hogs(gc);
|
||||
|
@ -909,6 +971,7 @@ void gpiochip_remove(struct gpio_chip *gc)
|
|||
* gone.
|
||||
*/
|
||||
gcdev_unregister(gdev);
|
||||
up_write(&gdev->sem);
|
||||
put_device(&gdev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_remove);
|
||||
|
@ -3808,62 +3871,88 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
|
|||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* fwnode_get_named_gpiod - obtain a GPIO from firmware node
|
||||
* @fwnode: handle of the firmware node
|
||||
* @propname: name of the firmware property representing the GPIO
|
||||
* @index: index of the GPIO to obtain for the consumer
|
||||
* @dflags: GPIO initialization flags
|
||||
* @label: label to attach to the requested GPIO
|
||||
*
|
||||
* This function can be used for drivers that get their configuration
|
||||
* from opaque firmware.
|
||||
*
|
||||
* The function properly finds the corresponding GPIO using whatever is the
|
||||
* underlying firmware interface and then makes sure that the GPIO
|
||||
* descriptor is requested before it is returned to the caller.
|
||||
*
|
||||
* Returns:
|
||||
* On successful request the GPIO pin is configured in accordance with
|
||||
* provided @dflags.
|
||||
*
|
||||
* In case of error an ERR_PTR() is returned.
|
||||
*/
|
||||
static struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
||||
const char *propname, int index,
|
||||
enum gpiod_flags dflags,
|
||||
const char *label)
|
||||
static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode,
|
||||
struct device *consumer,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum gpiod_flags *flags,
|
||||
unsigned long *lookupflags)
|
||||
{
|
||||
unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
|
||||
struct gpio_desc *desc = ERR_PTR(-ENODEV);
|
||||
int ret;
|
||||
struct gpio_desc *desc = ERR_PTR(-ENOENT);
|
||||
|
||||
if (is_of_node(fwnode)) {
|
||||
desc = gpiod_get_from_of_node(to_of_node(fwnode),
|
||||
propname, index,
|
||||
dflags,
|
||||
label);
|
||||
return desc;
|
||||
dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n",
|
||||
fwnode, con_id);
|
||||
desc = of_find_gpio(to_of_node(fwnode), con_id, idx, lookupflags);
|
||||
} else if (is_acpi_node(fwnode)) {
|
||||
struct acpi_gpio_info info;
|
||||
|
||||
desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
|
||||
acpi_gpio_update_gpiod_flags(&dflags, &info);
|
||||
acpi_gpio_update_gpiod_lookup_flags(&lflags, &info);
|
||||
} else {
|
||||
return ERR_PTR(-EINVAL);
|
||||
dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n",
|
||||
fwnode, con_id);
|
||||
desc = acpi_find_gpio(fwnode, con_id, idx, flags, lookupflags);
|
||||
} else if (is_software_node(fwnode)) {
|
||||
dev_dbg(consumer, "using swnode '%pfw' for '%s' GPIO lookup\n",
|
||||
fwnode, con_id);
|
||||
desc = swnode_find_gpio(fwnode, con_id, idx, lookupflags);
|
||||
}
|
||||
|
||||
/* Currently only ACPI takes this path */
|
||||
ret = gpiod_request(desc, label);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
return desc;
|
||||
}
|
||||
|
||||
ret = gpiod_configure_flags(desc, propname, lflags, dflags);
|
||||
static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
|
||||
struct fwnode_handle *fwnode,
|
||||
const char *con_id,
|
||||
unsigned int idx,
|
||||
enum gpiod_flags flags,
|
||||
const char *label,
|
||||
bool platform_lookup_allowed)
|
||||
{
|
||||
struct gpio_desc *desc = ERR_PTR(-ENOENT);
|
||||
unsigned long lookupflags;
|
||||
int ret;
|
||||
|
||||
if (!IS_ERR_OR_NULL(fwnode))
|
||||
desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx,
|
||||
&flags, &lookupflags);
|
||||
|
||||
if (gpiod_not_found(desc) && platform_lookup_allowed) {
|
||||
/*
|
||||
* Either we are not using DT or ACPI, or their lookup did not
|
||||
* return a result. In that case, use platform lookup as a
|
||||
* fallback.
|
||||
*/
|
||||
dev_dbg(consumer, "using lookup tables for GPIO lookup\n");
|
||||
desc = gpiod_find(consumer, con_id, idx, &lookupflags);
|
||||
}
|
||||
|
||||
if (IS_ERR(desc)) {
|
||||
dev_dbg(consumer, "No GPIO consumer %s found\n", con_id);
|
||||
return desc;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a connection label was passed use that, else attempt to use
|
||||
* the device name as label
|
||||
*/
|
||||
ret = gpiod_request(desc, label);
|
||||
if (ret) {
|
||||
if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/*
|
||||
* This happens when there are several consumers for
|
||||
* the same GPIO line: we just return here without
|
||||
* further initialization. It is a bit of a hack.
|
||||
* This is necessary to support fixed regulators.
|
||||
*
|
||||
* FIXME: Make this more sane and safe.
|
||||
*/
|
||||
dev_info(consumer,
|
||||
"nonexclusive access to GPIO for %s\n", con_id);
|
||||
return desc;
|
||||
}
|
||||
|
||||
ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
|
||||
if (ret < 0) {
|
||||
dev_dbg(consumer, "setup of GPIO %s failed\n", con_id);
|
||||
gpiod_put(desc);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
@ -3896,29 +3985,12 @@ static struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
|||
* In case of error an ERR_PTR() is returned.
|
||||
*/
|
||||
struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode,
|
||||
const char *con_id, int index,
|
||||
const char *con_id,
|
||||
int index,
|
||||
enum gpiod_flags flags,
|
||||
const char *label)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
char prop_name[32]; /* 32 is max size of property name */
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
||||
if (con_id)
|
||||
snprintf(prop_name, sizeof(prop_name), "%s-%s",
|
||||
con_id, gpio_suffixes[i]);
|
||||
else
|
||||
snprintf(prop_name, sizeof(prop_name), "%s",
|
||||
gpio_suffixes[i]);
|
||||
|
||||
desc = fwnode_get_named_gpiod(fwnode, prop_name, index, flags,
|
||||
label);
|
||||
if (!gpiod_not_found(desc))
|
||||
break;
|
||||
}
|
||||
|
||||
return desc;
|
||||
return gpiod_find_and_request(NULL, fwnode, con_id, index, flags, label, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index);
|
||||
|
||||
|
@ -3937,6 +4009,8 @@ int gpiod_count(struct device *dev, const char *con_id)
|
|||
count = of_gpio_get_count(dev, con_id);
|
||||
else if (is_acpi_node(fwnode))
|
||||
count = acpi_gpio_count(dev, con_id);
|
||||
else if (is_software_node(fwnode))
|
||||
count = swnode_gpio_count(fwnode, con_id);
|
||||
|
||||
if (count < 0)
|
||||
count = platform_gpio_count(dev, con_id);
|
||||
|
@ -4072,70 +4146,11 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
|
|||
unsigned int idx,
|
||||
enum gpiod_flags flags)
|
||||
{
|
||||
unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT;
|
||||
struct gpio_desc *desc = NULL;
|
||||
int ret;
|
||||
/* Maybe we have a device name, maybe not */
|
||||
struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
|
||||
const char *devname = dev ? dev_name(dev) : "?";
|
||||
const struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
|
||||
const char *label = con_id ?: devname;
|
||||
|
||||
dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);
|
||||
|
||||
/* Using device tree? */
|
||||
if (is_of_node(fwnode)) {
|
||||
dev_dbg(dev, "using device tree for GPIO lookup\n");
|
||||
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
|
||||
} else if (is_acpi_node(fwnode)) {
|
||||
dev_dbg(dev, "using ACPI for GPIO lookup\n");
|
||||
desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Either we are not using DT or ACPI, or their lookup did not return
|
||||
* a result. In that case, use platform lookup as a fallback.
|
||||
*/
|
||||
if (!desc || gpiod_not_found(desc)) {
|
||||
dev_dbg(dev, "using lookup tables for GPIO lookup\n");
|
||||
desc = gpiod_find(dev, con_id, idx, &lookupflags);
|
||||
}
|
||||
|
||||
if (IS_ERR(desc)) {
|
||||
dev_dbg(dev, "No GPIO consumer %s found\n", con_id);
|
||||
return desc;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a connection label was passed use that, else attempt to use
|
||||
* the device name as label
|
||||
*/
|
||||
ret = gpiod_request(desc, con_id ?: devname);
|
||||
if (ret) {
|
||||
if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/*
|
||||
* This happens when there are several consumers for
|
||||
* the same GPIO line: we just return here without
|
||||
* further initialization. It is a bit of a hack.
|
||||
* This is necessary to support fixed regulators.
|
||||
*
|
||||
* FIXME: Make this more sane and safe.
|
||||
*/
|
||||
dev_info(dev, "nonexclusive access to GPIO for %s\n", con_id ?: devname);
|
||||
return desc;
|
||||
}
|
||||
|
||||
ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
|
||||
gpiod_put(desc);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
blocking_notifier_call_chain(&desc->gdev->notifier,
|
||||
GPIOLINE_CHANGED_REQUESTED, desc);
|
||||
|
||||
return desc;
|
||||
return gpiod_find_and_request(dev, fwnode, con_id, idx, flags, label, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_get_index);
|
||||
|
||||
|
|
|
@ -15,14 +15,15 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
#define GPIOCHIP_NAME "gpiochip"
|
||||
|
||||
/**
|
||||
* struct gpio_device - internal state container for GPIO devices
|
||||
* @id: numerical ID number for the GPIO chip
|
||||
* @dev: the GPIO device struct
|
||||
* @chrdev: character device for the GPIO device
|
||||
* @id: numerical ID number for the GPIO chip
|
||||
* @mockdev: class device used by the deprecated sysfs interface (may be
|
||||
* NULL)
|
||||
* @owner: helps prevent removal of modules exporting active GPIOs
|
||||
|
@ -39,6 +40,9 @@
|
|||
* @list: links gpio_device:s together for traversal
|
||||
* @notifier: used to notify subscribers about lines being requested, released
|
||||
* or reconfigured
|
||||
* @sem: protects the structure from a NULL-pointer dereference of @chip by
|
||||
* user-space operations when the device gets unregistered during
|
||||
* a hot-unplug event
|
||||
* @pin_ranges: range of pins served by the GPIO driver
|
||||
*
|
||||
* This state container holds most of the runtime variable data
|
||||
|
@ -47,9 +51,9 @@
|
|||
* userspace.
|
||||
*/
|
||||
struct gpio_device {
|
||||
int id;
|
||||
struct device dev;
|
||||
struct cdev chrdev;
|
||||
int id;
|
||||
struct device *mockdev;
|
||||
struct module *owner;
|
||||
struct gpio_chip *chip;
|
||||
|
@ -60,6 +64,7 @@ struct gpio_device {
|
|||
void *data;
|
||||
struct list_head list;
|
||||
struct blocking_notifier_head notifier;
|
||||
struct rw_semaphore sem;
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
/*
|
||||
|
@ -72,6 +77,11 @@ struct gpio_device {
|
|||
#endif
|
||||
};
|
||||
|
||||
static inline struct gpio_device *to_gpio_device(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct gpio_device, dev);
|
||||
}
|
||||
|
||||
/* gpio suffixes used for ACPI and device tree lookup */
|
||||
static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
|
||||
|
||||
|
|
|
@ -635,9 +635,8 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled)
|
|||
led->priv = priv;
|
||||
desc = &led->desc;
|
||||
|
||||
led->gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL,
|
||||
fwnode_child,
|
||||
GPIOD_ASIS, NULL);
|
||||
led->gpiod = devm_fwnode_gpiod_get(dev, fwnode_child, NULL,
|
||||
GPIOD_ASIS, NULL);
|
||||
if (IS_ERR(led->gpiod)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(led->gpiod), "led: get gpio fail!\n");
|
||||
goto __dt_err;
|
||||
|
|
|
@ -151,9 +151,8 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
|||
* will be updated after LED class device is registered,
|
||||
* Only then the final LED name is known.
|
||||
*/
|
||||
led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,
|
||||
GPIOD_ASIS,
|
||||
NULL);
|
||||
led.gpiod = devm_fwnode_gpiod_get(dev, child, NULL, GPIOD_ASIS,
|
||||
NULL);
|
||||
if (IS_ERR(led.gpiod)) {
|
||||
fwnode_handle_put(child);
|
||||
return ERR_CAST(led.gpiod);
|
||||
|
|
|
@ -11,40 +11,18 @@
|
|||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
/* Platforms may implement their GPIO interface with library code,
|
||||
/*
|
||||
* Platforms may implement their GPIO interface with library code,
|
||||
* at a small performance cost for non-inlined operations and some
|
||||
* extra memory (for code and for per-GPIO table entries).
|
||||
*
|
||||
* While the GPIO programming interface defines valid GPIO numbers
|
||||
* to be in the range 0..MAX_INT, this library restricts them to the
|
||||
* smaller range 0..ARCH_NR_GPIOS-1.
|
||||
*
|
||||
* ARCH_NR_GPIOS is somewhat arbitrary; it usually reflects the sum of
|
||||
* builtin/SoC GPIOs plus a number of GPIOs on expanders; the latter is
|
||||
* actually an estimate of a board-specific value.
|
||||
*/
|
||||
|
||||
#ifndef ARCH_NR_GPIOS
|
||||
#if defined(CONFIG_ARCH_NR_GPIO) && CONFIG_ARCH_NR_GPIO > 0
|
||||
#define ARCH_NR_GPIOS CONFIG_ARCH_NR_GPIO
|
||||
#else
|
||||
#define ARCH_NR_GPIOS 512
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "valid" GPIO numbers are nonnegative and may be passed to
|
||||
* setup routines like gpio_request(). only some valid numbers
|
||||
* can successfully be requested and used.
|
||||
*
|
||||
* Invalid GPIO numbers are useful for indicating no-such-GPIO in
|
||||
* platform data and other tables.
|
||||
* At the end we want all GPIOs to be dynamically allocated from 0.
|
||||
* However, some legacy drivers still perform fixed allocation.
|
||||
* Until they are all fixed, leave 0-512 space for them.
|
||||
*/
|
||||
|
||||
static inline bool gpio_is_valid(int number)
|
||||
{
|
||||
return number >= 0 && number < ARCH_NR_GPIOS;
|
||||
}
|
||||
#define GPIO_DYNAMIC_BASE 512
|
||||
|
||||
struct device;
|
||||
struct gpio;
|
||||
|
@ -140,12 +118,6 @@ static inline void gpio_unexport(unsigned gpio)
|
|||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
static inline bool gpio_is_valid(int number)
|
||||
{
|
||||
/* only non-negative numbers are valid */
|
||||
return number >= 0;
|
||||
}
|
||||
|
||||
/* platforms that don't directly support access to GPIOs through I2C, SPI,
|
||||
* or other blocking infrastructure can use these wrappers.
|
||||
*/
|
||||
|
@ -169,4 +141,19 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value)
|
|||
|
||||
#endif /* !CONFIG_GPIOLIB */
|
||||
|
||||
/*
|
||||
* "valid" GPIO numbers are nonnegative and may be passed to
|
||||
* setup routines like gpio_request(). only some valid numbers
|
||||
* can successfully be requested and used.
|
||||
*
|
||||
* Invalid GPIO numbers are useful for indicating no-such-GPIO in
|
||||
* platform data and other tables.
|
||||
*/
|
||||
|
||||
static inline bool gpio_is_valid(int number)
|
||||
{
|
||||
/* only non-negative numbers are valid */
|
||||
return number >= 0;
|
||||
}
|
||||
|
||||
#endif /* _ASM_GENERIC_GPIO_H */
|
||||
|
|
|
@ -581,27 +581,6 @@ struct gpio_desc *devm_fwnode_gpiod_get(struct device *dev,
|
|||
flags, label);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev,
|
||||
const char *con_id, int index,
|
||||
struct fwnode_handle *child,
|
||||
enum gpiod_flags flags,
|
||||
const char *label)
|
||||
{
|
||||
return devm_fwnode_gpiod_get_index(dev, child, con_id, index,
|
||||
flags, label);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct gpio_desc *devm_fwnode_get_gpiod_from_child(struct device *dev,
|
||||
const char *con_id,
|
||||
struct fwnode_handle *child,
|
||||
enum gpiod_flags flags,
|
||||
const char *label)
|
||||
{
|
||||
return devm_fwnode_gpiod_get_index(dev, child, con_id, 0, flags, label);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_OF_GPIO)
|
||||
struct device_node;
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
#ifndef __LINUX_GPIO_PROPERTY_H
|
||||
#define __LINUX_GPIO_PROPERTY_H
|
||||
|
||||
#include <dt-bindings/gpio/gpio.h> /* for GPIO_* flags */
|
||||
#include <linux/property.h>
|
||||
|
||||
#define PROPERTY_ENTRY_GPIO(_name_, _chip_node_, _idx_, _flags_) \
|
||||
PROPERTY_ENTRY_REF(_name_, _chip_node_, _idx_, _flags_)
|
||||
|
||||
#endif /* __LINUX_GPIO_PROPERTY_H */
|
|
@ -34,7 +34,7 @@ enum of_gpio_flags {
|
|||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/container_of.h>
|
||||
|
||||
/*
|
||||
* OF GPIO chip for memory mapped banks
|
||||
|
|
Loading…
Reference in New Issue