diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 21:00:46 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 21:00:46 +0300 |
commit | 882d6edfc45cd2b6e33cf973eab9a1ae1dbad5d1 (patch) | |
tree | 034ebc487adeb29cef2c546b5e0b61d70a3bc339 | |
parent | 0328b5f2ef4af8ba060e64baa928c94037e7308f (diff) | |
parent | a8002a35935aaefcd6a42ad3289f62bab947f2ca (diff) | |
download | linux-882d6edfc45cd2b6e33cf973eab9a1ae1dbad5d1.tar.xz |
Merge tag 'gpio-updates-for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski:
"It's been a relatively calm release cycle and we're actually removing
more code than we're adding.
Summary:
- new driver for the Toshiba Visconti platform
- rework of interrupt handling in gpio-tegra
- updates for GPIO selftests: we're now using the character device to
perform the subsystem checks
- support for a new rcar variant + some code refactoring
- refactoring of gpio-ep93xx
- SPDX License identifier has been updated in the uapi header so that
userspace programs bundling it can become fully REUSE-compliant
- improvements to pwm handling in gpio-mvebu
- support for interrupt handling and power management for gpio-xilinx
as well as some code refactoring
- support for a new chip variant in gpio-pca953x
- removal of drivers: zte xs & intel-mid and removal of leftovers
from intel-msic
- impovements to intel drivers pulled from Andy Shevchenko
- improvements to the gpio-aggregator virtual GPIO driver
- and several minor tweaks and fixes to code and documentation all
over the place"
* tag 'gpio-updates-for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (71 commits)
gpio: pcf857x: Fix missing first interrupt
gpio: ep93xx: refactor base IRQ number
gpio: ep93xx: refactor ep93xx_gpio_add_bank
gpio: ep93xx: Fix typo s/hierarchial/hierarchical
gpio: ep93xx: drop to_irq binding
gpio: ep93xx: Fix wrong irq numbers in port F
gpio: uapi: use the preferred SPDX license identifier
gpio: gpio-xilinx: Add check if width exceeds 32
gpio: gpio-xilinx: Add support for suspend and resume
gpio: gpio-xilinx: Add interrupt support
gpio: gpio-xilinx: Reduce spinlock array to array
gpio: gpio-xilinx: Simplify with dev_err_probe()
gpio: msic: Drop driver from Makefile
gpio: wcove: Split out to_ireg() helper and deduplicate the code
gpio: wcove: Switch to use regmap_set_bits(), regmap_clear_bits()
gpio: wcove: Get rid of error prone casting in IRQ handler
gpio: intel-mid: Remove driver for deprecated platform
gpio: msic: Remove driver for deprecated platform
gpio: aggregator: Remove trailing comma in terminator entries
gpio: aggregator: Use compound literal from the header
...
44 files changed, 1811 insertions, 1397 deletions
diff --git a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt index e31511255d8e..052a967c1f28 100644 --- a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt @@ -80,6 +80,11 @@ Required properties: - offset: offset address inside the syscon block +Optional properties: + +- marvell,pwm-offset: offset address of PWM duration control registers inside + the syscon block + Example: ap_syscon: system-controller@6f4000 { compatible = "syscon", "simple-mfd"; @@ -101,6 +106,9 @@ ap_syscon: system-controller@6f4000 { gpio-controller; #gpio-cells = <2>; gpio-ranges = <&ap_pinctrl 0 0 19>; + marvell,pwm-offset = <0x10c0>; + #pwm-cells = <2>; + clocks = <&ap_clk 3>; }; }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-davinci.txt b/Documentation/devicetree/bindings/gpio/gpio-davinci.txt index cd91d61eac31..696ea46227d1 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-davinci.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-davinci.txt @@ -7,6 +7,7 @@ Required Properties: "ti,k2g-gpio", "ti,keystone-gpio": for 66AK2G "ti,am654-gpio", "ti,keystone-gpio": for TI K3 AM654 "ti,j721e-gpio", "ti,keystone-gpio": for J721E SoCs + "ti,am64-gpio", "ti,keystone-gpio": for AM64 SoCs - reg: Physical base address of the controller and the size of memory mapped registers. diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index f5ee23c2df60..246bae53be8b 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -32,6 +32,7 @@ properties: - maxim,max7327 - nxp,pca6416 - nxp,pca9505 + - nxp,pca9506 - nxp,pca9534 - nxp,pca9535 - nxp,pca9536 @@ -70,7 +71,7 @@ properties: gpio-line-names: minItems: 1 - maxItems: 32 + maxItems: 40 interrupts: maxItems: 1 diff --git a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml index 5026662e4508..f2541739ee3b 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml @@ -48,6 +48,9 @@ properties: - renesas,gpio-r8a77995 # R-Car D3 - const: renesas,rcar-gen3-gpio # R-Car Gen3 or RZ/G2 + - items: + - const: renesas,gpio-r8a779a0 # R-Car V3U + reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml b/Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml new file mode 100644 index 000000000000..9ad470e01953 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/toshiba,gpio-visconti.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Toshiba Visconti ARM SoCs GPIO controller + +maintainers: + - Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp> + +properties: + compatible: + items: + - const: toshiba,gpio-tmpv7708 + + reg: + maxItems: 1 + + "#gpio-cells": + const: 2 + + gpio-ranges: true + + gpio-controller: true + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + interrupts: + description: + interrupt mapping one per GPIO. + minItems: 16 + maxItems: 16 + +required: + - compatible + - reg + - "#gpio-cells" + - gpio-ranges + - gpio-controller + - interrupt-controller + - "#interrupt-cells" + - interrupt-parent + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + gpio: gpio@28020000 { + compatible = "toshiba,gpio-tmpv7708"; + reg = <0 0x28020000 0 0x1000>; + #gpio-cells = <0x2>; + gpio-ranges = <&pmux 0 0 32>; + gpio-controller; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/gpio/zx296702-gpio.txt b/Documentation/devicetree/bindings/gpio/zx296702-gpio.txt deleted file mode 100644 index 0dab156fcf41..000000000000 --- a/Documentation/devicetree/bindings/gpio/zx296702-gpio.txt +++ /dev/null @@ -1,24 +0,0 @@ -ZTE ZX296702 GPIO controller - -Required properties: -- compatible : "zte,zx296702-gpio" -- #gpio-cells : Should be two. The first cell is the pin number and the - second cell is used to specify optional parameters: - - bit 0 specifies polarity (0 for normal, 1 for inverted) -- gpio-controller : Marks the device node as a GPIO controller. -- interrupts : Interrupt mapping for GPIO IRQ. -- gpio-ranges : Interaction with the PINCTRL subsystem. - -gpio1: gpio@b008040 { - compatible = "zte,zx296702-gpio"; - reg = <0xb008040 0x40>; - gpio-controller; - #gpio-cells = <2>; - gpio-ranges = < &pmx0 0 54 2 &pmx0 2 59 14>; - interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>; - interrupt-parent = <&intc>; - interrupt-controller; - #interrupt-cells = <2>; - clock-names = "gpio_pclk"; - clocks = <&lsp0clk ZX296702_GPIO_CLK>; -}; diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst index 173e4c7b037d..22271c342d92 100644 --- a/Documentation/driver-api/gpio/consumer.rst +++ b/Documentation/driver-api/gpio/consumer.rst @@ -361,12 +361,13 @@ corresponding chip driver. In that case a significantly improved performance can be expected. If simultaneous access is not possible the GPIOs will be accessed sequentially. -The functions take three arguments: +The functions take four arguments: + * array_size - the number of array elements * desc_array - an array of GPIO descriptors * array_info - optional information obtained from gpiod_get_array() * value_bitmap - a bitmap to store the GPIOs' values (get) or - a bitmap of values to assign to the GPIOs (set) + a bitmap of values to assign to the GPIOs (set) The descriptor array can be obtained using the gpiod_get_array() function or one of its variants. If the group of descriptors returned by that function diff --git a/Documentation/driver-api/gpio/intro.rst b/Documentation/driver-api/gpio/intro.rst index 74591489d0b5..94dd7185e76e 100644 --- a/Documentation/driver-api/gpio/intro.rst +++ b/Documentation/driver-api/gpio/intro.rst @@ -106,11 +106,11 @@ don't. When you need open drain signaling but your hardware doesn't directly support it, there's a common idiom you can use to emulate it with any GPIO pin that can be used as either an input or an output: - LOW: gpiod_direction_output(gpio, 0) ... this drives the signal and overrides - the pullup. + **LOW**: ``gpiod_direction_output(gpio, 0)`` ... this drives the signal and + overrides the pullup. - HIGH: gpiod_direction_input(gpio) ... this turns off the output, so the pullup - (or some other device) controls the signal. + **HIGH**: ``gpiod_direction_input(gpio)`` ... this turns off the output, so + the pullup (or some other device) controls the signal. The same logic can be applied to emulate open source signaling, by driving the high signal and configuring the GPIO as input for low. This open drain/open diff --git a/MAINTAINERS b/MAINTAINERS index acd1867a9a97..7ab5f04d4e5b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2609,10 +2609,12 @@ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwamatsu/linux-visconti.git F: Documentation/devicetree/bindings/arm/toshiba.yaml F: Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml +F: Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml F: Documentation/devicetree/bindings/pinctrl/toshiba,tmpv7700-pinctrl.yaml F: Documentation/devicetree/bindings/watchdog/toshiba,visconti-wdt.yaml F: arch/arm64/boot/dts/toshiba/ F: drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c +F: drivers/gpio/gpio-visconti.c F: drivers/pinctrl/visconti/ F: drivers/watchdog/visconti_wdt.c N: visconti diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f62f0b6b0bc0..e3607ec4c2e8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -487,11 +487,11 @@ config GPIO_PXA Say yes here to support the PXA GPIO device config GPIO_RCAR - tristate "Renesas R-Car GPIO" + tristate "Renesas R-Car and RZ/G GPIO support" depends on ARCH_RENESAS || COMPILE_TEST select GPIOLIB_IRQCHIP help - Say yes here to support GPIO on Renesas R-Car SoCs. + Say yes here to support GPIO on Renesas R-Car or RZ/G SoCs. config GPIO_RDA bool "RDA Micro GPIO controller support" @@ -595,7 +595,7 @@ config GPIO_TB10X select OF_GPIO config GPIO_TEGRA - bool "NVIDIA Tegra GPIO support" + tristate "NVIDIA Tegra GPIO support" default ARCH_TEGRA depends on ARCH_TEGRA || COMPILE_TEST depends on OF_GPIO @@ -648,6 +648,16 @@ config GPIO_VF610 help Say yes here to support Vybrid vf610 GPIOs. +config GPIO_VISCONTI + tristate "Toshiba Visconti GPIO support" + depends on ARCH_VISCONTI || COMPILE_TEST + depends on OF_GPIO + select GPIOLIB_IRQCHIP + select GPIO_GENERIC + select IRQ_DOMAIN_HIERARCHY + help + Say yes here to support GPIO on Tohisba Visconti. + config GPIO_VR41XX tristate "NEC VR4100 series General-purpose I/O Uint support" depends on CPU_VR41XX @@ -670,7 +680,7 @@ config GPIO_WCD934X tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver" depends on MFD_WCD934X && OF_GPIO help - This driver is to supprot GPIO block found on the Qualcomm Technologies + This driver is to support GPIO block found on the Qualcomm Technologies Inc WCD9340/WCD9341 Audio Codec. config GPIO_XGENE @@ -694,6 +704,8 @@ config GPIO_XGENE_SB config GPIO_XILINX tristate "Xilinx GPIO support" + select GPIOLIB_IRQCHIP + depends on OF_GPIO help Say yes here to support the Xilinx FPGA GPIO device @@ -731,13 +743,6 @@ config GPIO_ZYNQ help Say yes here to support Xilinx Zynq GPIO controller. -config GPIO_ZX - bool "ZTE ZX GPIO support" - depends on ARCH_ZX || COMPILE_TEST - select GPIOLIB_IRQCHIP - help - Say yes here to support the GPIO device on ZTE ZX SoCs. - config GPIO_LOONGSON1 tristate "Loongson1 GPIO support" depends on MACH_LOONGSON32 @@ -1623,8 +1628,7 @@ config GPIO_MOCKUP select IRQ_SIM help This enables GPIO Testing driver, which provides a way to test GPIO - subsystem through sysfs(or char device) and debugfs. GPIO_SYSFS - must be selected for this test. + subsystem through sysfs (or char device) and debugfs. User could use it through the script in tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in it. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a2106d667fb3..c58a90a3c3b1 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -102,7 +102,6 @@ obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o -obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o @@ -163,6 +162,7 @@ obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o +obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o @@ -179,5 +179,4 @@ obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o -obj-$(CONFIG_GPIO_ZX) += gpio-zx.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index dfd8a4876a27..08171431bb8f 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -62,34 +62,6 @@ static char *get_arg(char **args) return start; } -static bool isrange(const char *s) -{ - size_t n; - - if (IS_ERR_OR_NULL(s)) - return false; - - while (1) { - n = strspn(s, "0123456789"); - if (!n) - return false; - - s += n; - - switch (*s++) { - case '\0': - return true; - - case '-': - case ',': - break; - - default: - return false; - } - } -} - static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, int hwnum, unsigned int *n) { @@ -100,8 +72,7 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, if (!lookups) return -ENOMEM; - lookups->table[*n] = - (struct gpiod_lookup)GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0); + lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0); (*n)++; memset(&lookups->table[*n], 0, sizeof(lookups->table[*n])); @@ -112,10 +83,10 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, static int aggr_parse(struct gpio_aggregator *aggr) { + char *name, *offsets, *p; char *args = aggr->args; unsigned long *bitmap; unsigned int i, n = 0; - char *name, *offsets; int error = 0; bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL); @@ -130,7 +101,8 @@ static int aggr_parse(struct gpio_aggregator *aggr) goto free_bitmap; } - if (!isrange(offsets)) { + p = get_options(offsets, 0, &error); + if (error == 0 || *p) { /* Named GPIO line */ error = aggr_add_gpio(aggr, name, U16_MAX, &n); if (error) @@ -271,7 +243,7 @@ static DRIVER_ATTR_WO(delete_device); static struct attribute *gpio_aggregator_attrs[] = { &driver_attr_new_device.attr, &driver_attr_delete_device.attr, - NULL, + NULL }; ATTRIBUTE_GROUPS(gpio_aggregator); @@ -545,7 +517,7 @@ static const struct of_device_id gpio_aggregator_dt_ids[] = { * Add GPIO-operated devices controlled from userspace below, * or use "driver_override" in sysfs */ - {}, + {} }; MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids); #endif diff --git a/drivers/gpio/gpio-bd70528.c b/drivers/gpio/gpio-bd70528.c index 45b3da8da336..397a50d6bc65 100644 --- a/drivers/gpio/gpio-bd70528.c +++ b/drivers/gpio/gpio-bd70528.c @@ -12,7 +12,8 @@ #define GPIO_OUT_REG(offset) (BD70528_REG_GPIO1_OUT + (offset) * 2) struct bd70528_gpio { - struct rohm_regmap_dev chip; + struct regmap *regmap; + struct device *dev; struct gpio_chip gpio; }; @@ -35,11 +36,11 @@ static int bd70528_set_debounce(struct bd70528_gpio *bdgpio, val = BD70528_DEBOUNCE_50MS; break; default: - dev_err(bdgpio->chip.dev, + dev_err(bdgpio->dev, "Invalid debounce value %u\n", debounce); return -EINVAL; } - return regmap_update_bits(bdgpio->chip.regmap, GPIO_IN_REG(offset), + return regmap_update_bits(bdgpio->regmap, GPIO_IN_REG(offset), BD70528_DEBOUNCE_MASK, val); } @@ -49,9 +50,9 @@ static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset) int val, ret; /* Do we need to do something to IRQs here? */ - ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val); + ret = regmap_read(bdgpio->regmap, GPIO_OUT_REG(offset), &val); if (ret) { - dev_err(bdgpio->chip.dev, "Could not read gpio direction\n"); + dev_err(bdgpio->dev, "Could not read gpio direction\n"); return ret; } if (val & BD70528_GPIO_OUT_EN_MASK) @@ -67,13 +68,13 @@ static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset, switch (pinconf_to_config_param(config)) { case PIN_CONFIG_DRIVE_OPEN_DRAIN: - return regmap_update_bits(bdgpio->chip.regmap, + return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), BD70528_GPIO_DRIVE_MASK, BD70528_GPIO_OPEN_DRAIN); break; case PIN_CONFIG_DRIVE_PUSH_PULL: - return regmap_update_bits(bdgpio->chip.regmap, + return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), BD70528_GPIO_DRIVE_MASK, BD70528_GPIO_PUSH_PULL); @@ -93,7 +94,7 @@ static int bd70528_direction_input(struct gpio_chip *chip, unsigned int offset) struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); /* Do we need to do something to IRQs here? */ - return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), BD70528_GPIO_OUT_EN_MASK, BD70528_GPIO_OUT_DISABLE); } @@ -105,10 +106,10 @@ static void bd70528_gpio_set(struct gpio_chip *chip, unsigned int offset, struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); u8 val = (value) ? BD70528_GPIO_OUT_HI : BD70528_GPIO_OUT_LO; - ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + ret = regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), BD70528_GPIO_OUT_MASK, val); if (ret) - dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value); + dev_err(bdgpio->dev, "Could not set gpio to %d\n", value); } static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset, @@ -117,7 +118,7 @@ static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset, struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); bd70528_gpio_set(chip, offset, value); - return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), BD70528_GPIO_OUT_EN_MASK, BD70528_GPIO_OUT_ENABLE); } @@ -129,11 +130,11 @@ static int bd70528_gpio_get_o(struct bd70528_gpio *bdgpio, unsigned int offset) int ret; unsigned int val; - ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val); + ret = regmap_read(bdgpio->regmap, GPIO_OUT_REG(offset), &val); if (!ret) ret = !!(val & BD70528_GPIO_OUT_MASK); else - dev_err(bdgpio->chip.dev, "GPIO (out) state read failed\n"); + dev_err(bdgpio->dev, "GPIO (out) state read failed\n"); return ret; } @@ -143,12 +144,12 @@ static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset) unsigned int val; int ret; - ret = regmap_read(bdgpio->chip.regmap, BD70528_REG_GPIO_STATE, &val); + ret = regmap_read(bdgpio->regmap, BD70528_REG_GPIO_STATE, &val); if (!ret) ret = !(val & GPIO_IN_STATE_MASK(offset)); else - dev_err(bdgpio->chip.dev, "GPIO (in) state read failed\n"); + dev_err(bdgpio->dev, "GPIO (in) state read failed\n"); return ret; } @@ -173,29 +174,22 @@ static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset) else if (ret == GPIO_LINE_DIRECTION_IN) ret = bd70528_gpio_get_i(bdgpio, offset); else - dev_err(bdgpio->chip.dev, "failed to read GPIO direction\n"); + dev_err(bdgpio->dev, "failed to read GPIO direction\n"); return ret; } static int bd70528_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct bd70528_gpio *bdgpio; - struct rohm_regmap_dev *bd70528; int ret; - bd70528 = dev_get_drvdata(pdev->dev.parent); - if (!bd70528) { - dev_err(&pdev->dev, "No MFD driver data\n"); - return -EINVAL; - } - - bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio), - GFP_KERNEL); + bdgpio = devm_kzalloc(dev, sizeof(*bdgpio), GFP_KERNEL); if (!bdgpio) return -ENOMEM; - bdgpio->chip.dev = &pdev->dev; - bdgpio->gpio.parent = pdev->dev.parent; + bdgpio->dev = dev; + bdgpio->gpio.parent = dev->parent; bdgpio->gpio.label = "bd70528-gpio"; bdgpio->gpio.owner = THIS_MODULE; bdgpio->gpio.get_direction = bd70528_get_direction; @@ -208,14 +202,15 @@ static int bd70528_probe(struct platform_device *pdev) bdgpio->gpio.ngpio = 4; bdgpio->gpio.base = -1; #ifdef CONFIG_OF_GPIO - bdgpio->gpio.of_node = pdev->dev.parent->of_node; + bdgpio->gpio.of_node = dev->parent->of_node; #endif - bdgpio->chip.regmap = bd70528->regmap; + bdgpio->regmap = dev_get_regmap(dev->parent, NULL); + if (!bdgpio->regmap) + return -ENODEV; - ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio, - bdgpio); + ret = devm_gpiochip_add_data(dev, &bdgpio->gpio, bdgpio); if (ret) - dev_err(&pdev->dev, "gpio_init: Failed to add bd70528-gpio\n"); + dev_err(dev, "gpio_init: Failed to add bd70528-gpio\n"); return ret; } diff --git a/drivers/gpio/gpio-bd71828.c b/drivers/gpio/gpio-bd71828.c index 3dbbc638e9a9..c8e382b53f2f 100644 --- a/drivers/gpio/gpio-bd71828.c +++ b/drivers/gpio/gpio-bd71828.c @@ -11,7 +11,8 @@ #define HALL_GPIO_OFFSET 3 struct bd71828_gpio { - struct rohm_regmap_dev chip; + struct regmap *regmap; + struct device *dev; struct gpio_chip gpio; }; @@ -29,10 +30,10 @@ static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset, if (offset == HALL_GPIO_OFFSET) return; - ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + ret = regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), BD71828_GPIO_OUT_MASK, val); if (ret) - dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value); + dev_err(bdgpio->dev, "Could not set gpio to %d\n", value); } static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -42,10 +43,10 @@ static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset) struct bd71828_gpio *bdgpio = gpiochip_get_data(chip); if (offset == HALL_GPIO_OFFSET) - ret = regmap_read(bdgpio->chip.regmap, BD71828_REG_IO_STAT, + ret = regmap_read(bdgpio->regmap, BD71828_REG_IO_STAT, &val); else - ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + ret = regmap_read(bdgpio->regmap, GPIO_OUT_REG(offset), &val); if (!ret) ret = (val & BD71828_GPIO_OUT_MASK); @@ -63,12 +64,12 @@ static int bd71828_gpio_set_config(struct gpio_chip *chip, unsigned int offset, switch (pinconf_to_config_param(config)) { case PIN_CONFIG_DRIVE_OPEN_DRAIN: - return regmap_update_bits(bdgpio->chip.regmap, + return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), BD71828_GPIO_DRIVE_MASK, BD71828_GPIO_OPEN_DRAIN); case PIN_CONFIG_DRIVE_PUSH_PULL: - return regmap_update_bits(bdgpio->chip.regmap, + return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), BD71828_GPIO_DRIVE_MASK, BD71828_GPIO_PUSH_PULL); @@ -96,22 +97,15 @@ static int bd71828_get_direction(struct gpio_chip *chip, unsigned int offset) static int bd71828_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct bd71828_gpio *bdgpio; - struct rohm_regmap_dev *bd71828; - bd71828 = dev_get_drvdata(pdev->dev.parent); - if (!bd71828) { - dev_err(&pdev->dev, "No MFD driver data\n"); - return -EINVAL; - } - - bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio), - GFP_KERNEL); + bdgpio = devm_kzalloc(dev, sizeof(*bdgpio), GFP_KERNEL); if (!bdgpio) return -ENOMEM; - bdgpio->chip.dev = &pdev->dev; - bdgpio->gpio.parent = pdev->dev.parent; + bdgpio->dev = dev; + bdgpio->gpio.parent = dev->parent; bdgpio->gpio.label = "bd71828-gpio"; bdgpio->gpio.owner = THIS_MODULE; bdgpio->gpio.get_direction = bd71828_get_direction; @@ -127,11 +121,12 @@ static int bd71828_probe(struct platform_device *pdev) * "gpio-reserved-ranges" and exclude them from control */ bdgpio->gpio.ngpio = 4; - bdgpio->gpio.of_node = pdev->dev.parent->of_node; - bdgpio->chip.regmap = bd71828->regmap; + bdgpio->gpio.of_node = dev->parent->of_node; + bdgpio->regmap = dev_get_regmap(dev->parent, NULL); + if (!bdgpio->regmap) + return -ENODEV; - return devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio, - bdgpio); + return devm_gpiochip_add_data(dev, &bdgpio->gpio, bdgpio); } static struct platform_driver bd71828_gpio = { diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 94d9fa0d6aa7..ef148b26b587 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -31,6 +31,8 @@ /* Maximum value for irq capable line identifiers */ #define EP93XX_GPIO_LINE_MAX_IRQ 23 +#define EP93XX_GPIO_A_IRQ_BASE 64 +#define EP93XX_GPIO_B_IRQ_BASE 72 /* * Static mapping of GPIO bank F IRQS: * F0..F7 (16..24) to irq 80..87. @@ -292,14 +294,14 @@ struct ep93xx_gpio_bank { static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { /* Bank A has 8 IRQs */ - EP93XX_GPIO_BANK("A", 0x00, 0x10, 0x90, 0, true, false, 64), + EP93XX_GPIO_BANK("A", 0x00, 0x10, 0x90, 0, true, false, EP93XX_GPIO_A_IRQ_BASE), /* Bank B has 8 IRQs */ - EP93XX_GPIO_BANK("B", 0x04, 0x14, 0xac, 8, true, false, 72), + EP93XX_GPIO_BANK("B", 0x04, 0x14, 0xac, 8, true, false, EP93XX_GPIO_B_IRQ_BASE), EP93XX_GPIO_BANK("C", 0x08, 0x18, 0x00, 40, false, false, 0), EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 0x00, 24, false, false, 0), EP93XX_GPIO_BANK("E", 0x20, 0x24, 0x00, 32, false, false, 0), /* Bank F has 8 IRQs */ - EP93XX_GPIO_BANK("F", 0x30, 0x34, 0x4c, 16, false, true, 0), + EP93XX_GPIO_BANK("F", 0x30, 0x34, 0x4c, 16, false, true, EP93XX_GPIO_F_IRQ_BASE), EP93XX_GPIO_BANK("G", 0x38, 0x3c, 0x00, 48, false, false, 0), EP93XX_GPIO_BANK("H", 0x40, 0x44, 0x00, 56, false, false, 0), }; @@ -318,11 +320,6 @@ static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, return 0; } -static int ep93xx_gpio_f_to_irq(struct gpio_chip *gc, unsigned offset) -{ - return EP93XX_GPIO_F_IRQ_BASE + offset; -} - static void ep93xx_init_irq_chip(struct device *dev, struct irq_chip *ic) { ic->irq_ack = ep93xx_gpio_irq_ack; @@ -375,7 +372,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc, girq->parent_handler = ep93xx_gpio_ab_irq_handler; girq->num_parents = 1; - girq->parents = devm_kcalloc(dev, 1, + girq->parents = devm_kcalloc(dev, girq->num_parents, sizeof(*girq->parents), GFP_KERNEL); if (!girq->parents) @@ -393,20 +390,19 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc, /* * FIXME: convert this to use hierarchical IRQ support! - * this requires fixing the root irqchip to be hierarchial. + * this requires fixing the root irqchip to be hierarchical. */ girq->parent_handler = ep93xx_gpio_f_irq_handler; girq->num_parents = 8; - girq->parents = devm_kcalloc(dev, 8, + girq->parents = devm_kcalloc(dev, girq->num_parents, sizeof(*girq->parents), GFP_KERNEL); if (!girq->parents) return -ENOMEM; /* Pick resources 1..8 for these IRQs */ - for (i = 1; i <= 8; i++) - girq->parents[i - 1] = platform_get_irq(pdev, i); - for (i = 0; i < 8; i++) { - gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i; + for (i = 0; i < girq->num_parents; i++) { + girq->parents[i] = platform_get_irq(pdev, i + 1); + gpio_irq = bank->irq_base + i; irq_set_chip_data(gpio_irq, &epg->gc[5]); irq_set_chip_and_handler(gpio_irq, girq->chip, @@ -415,7 +411,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc, } girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_level_irq; - gc->to_irq = ep93xx_gpio_f_to_irq; + girq->first = bank->irq_base; } return devm_gpiochip_add_data(dev, gc, epg); diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 7c0a9ef0b500..82b3a913005d 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -325,7 +325,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) girq->parents = NULL; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_edge_irq; - girq->init_hw = max77620_gpio_irq_init_hw, + girq->init_hw = max77620_gpio_irq_init_hw; girq->threaded = true; platform_set_drvdata(pdev, mgpio); diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 706687fab634..22f3ce218f5d 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -194,6 +194,11 @@ static int mrfld_gpio_set_config(struct gpio_chip *chip, unsigned int offset, { u32 debounce; + if ((pinconf_to_config_param(config) == PIN_CONFIG_BIAS_DISABLE) || + (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_UP) || + (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_DOWN)) + return gpiochip_generic_config(chip, offset, config); + if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) return -ENOTSUPP; diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index a912a8fed197..8f429d9f3661 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -70,7 +70,12 @@ */ #define PWM_BLINK_ON_DURATION_OFF 0x0 #define PWM_BLINK_OFF_DURATION_OFF 0x4 +#define PWM_BLINK_COUNTER_B_OFF 0x8 +/* Armada 8k variant gpios register offsets */ +#define AP80X_GPIO0_OFF_A8K 0x1040 +#define CP11X_GPIO0_OFF_A8K 0x100 +#define CP11X_GPIO1_OFF_A8K 0x140 /* The MV78200 has per-CPU registers for edge mask and level mask */ #define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18) @@ -93,6 +98,7 @@ struct mvebu_pwm { struct regmap *regs; + u32 offset; unsigned long clk_rate; struct gpio_desc *gpiod; struct pwm_chip chip; @@ -283,12 +289,12 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val) */ static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm) { - return PWM_BLINK_ON_DURATION_OFF; + return mvpwm->offset + PWM_BLINK_ON_DURATION_OFF; } static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) { - return PWM_BLINK_OFF_DURATION_OFF; + return mvpwm->offset + PWM_BLINK_OFF_DURATION_OFF; } /* @@ -667,26 +673,21 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, spin_lock_irqsave(&mvpwm->lock, flags); regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), &u); - val = (unsigned long long) u * NSEC_PER_SEC; - do_div(val, mvpwm->clk_rate); - if (val > UINT_MAX) - state->duty_cycle = UINT_MAX; - else if (val) - state->duty_cycle = val; + /* Hardware treats zero as 2^32. See mvebu_pwm_apply(). */ + if (u > 0) + val = u; else - state->duty_cycle = 1; + val = UINT_MAX + 1ULL; + state->duty_cycle = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, + mvpwm->clk_rate); - val = (unsigned long long) u; /* on duration */ regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u); - val += (unsigned long long) u; /* period = on + off duration */ - val *= NSEC_PER_SEC; - do_div(val, mvpwm->clk_rate); - if (val > UINT_MAX) - state->period = UINT_MAX; - else if (val) - state->period = val; + /* period = on + off duration */ + if (u > 0) + val += u; else - state->period = 1; + val += UINT_MAX + 1ULL; + state->period = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, mvpwm->clk_rate); regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u); if (u) @@ -708,19 +709,27 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, val = (unsigned long long) mvpwm->clk_rate * state->duty_cycle; do_div(val, NSEC_PER_SEC); - if (val > UINT_MAX) + if (val > UINT_MAX + 1ULL) return -EINVAL; - if (val) + /* + * Zero on/off values don't work as expected. Experimentation shows + * that zero value is treated as 2^32. This behavior is not documented. + */ + if (val == UINT_MAX + 1ULL) + on = 0; + else if (val) on = val; else on = 1; - val = (unsigned long long) mvpwm->clk_rate * - (state->period - state->duty_cycle); + val = (unsigned long long) mvpwm->clk_rate * state->period; do_div(val, NSEC_PER_SEC); - if (val > UINT_MAX) + val -= on; + if (val > UINT_MAX + 1ULL) return -EINVAL; - if (val) + if (val == UINT_MAX + 1ULL) + off = 0; + else if (val) off = val; else off = 1; @@ -778,51 +787,80 @@ static int mvebu_pwm_probe(struct platform_device *pdev, struct device *dev = &pdev->dev; struct mvebu_pwm *mvpwm; void __iomem *base; + u32 offset; u32 set; - if (!of_device_is_compatible(mvchip->chip.of_node, - "marvell,armada-370-gpio")) - return 0; - - /* - * There are only two sets of PWM configuration registers for - * all the GPIO lines on those SoCs which this driver reserves - * for the first two GPIO chips. So if the resource is missing - * we can't treat it as an error. - */ - if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm")) + if (of_device_is_compatible(mvchip->chip.of_node, + "marvell,armada-370-gpio")) { + /* + * There are only two sets of PWM configuration registers for + * all the GPIO lines on those SoCs which this driver reserves + * for the first two GPIO chips. So if the resource is missing + * we can't treat it as an error. + */ + if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm")) + return 0; + offset = 0; + } else if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) { + int ret = of_property_read_u32(dev->of_node, + "marvell,pwm-offset", &offset); + if (ret < 0) + return 0; + } else { return 0; + } if (IS_ERR(mvchip->clk)) return PTR_ERR(mvchip->clk); - /* - * Use set A for lines of GPIO chip with id 0, B for GPIO chip - * with id 1. Don't allow further GPIO chips to be used for PWM. - */ - if (id == 0) - set = 0; - else if (id == 1) - set = U32_MAX; - else - return -EINVAL; - regmap_write(mvchip->regs, - GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set); - mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); if (!mvpwm) return -ENOMEM; mvchip->mvpwm = mvpwm; mvpwm->mvchip = mvchip; + mvpwm->offset = offset; + + if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) { + mvpwm->regs = mvchip->regs; + + switch (mvchip->offset) { + case AP80X_GPIO0_OFF_A8K: + case CP11X_GPIO0_OFF_A8K: + /* Blink counter A */ + set = 0; + break; + case CP11X_GPIO1_OFF_A8K: + /* Blink counter B */ + set = U32_MAX; + mvpwm->offset += PWM_BLINK_COUNTER_B_OFF; + break; + default: + return -EINVAL; + } + } else { + base = devm_platform_ioremap_resource_byname(pdev, "pwm"); + if (IS_ERR(base)) + return PTR_ERR(base); - base = devm_platform_ioremap_resource_byname(pdev, "pwm"); - if (IS_ERR(base)) - return PTR_ERR(base); + mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvpwm->regs)) + return PTR_ERR(mvpwm->regs); - mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base, - &mvebu_gpio_regmap_config); - if (IS_ERR(mvpwm->regs)) - return PTR_ERR(mvpwm->regs); + /* + * Use set A for lines of GPIO chip with id 0, B for GPIO chip + * with id 1. Don't allow further GPIO chips to be used for PWM. + */ + if (id == 0) + set = 0; + else if (id == 1) + set = U32_MAX; + else + return -EINVAL; + } + + regmap_write(mvchip->regs, + GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set); mvpwm->clk_rate = clk_get_rate(mvchip->clk); if (!mvpwm->clk_rate) { diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 825b362eb4b7..5ea09fd01544 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -73,6 +73,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca6416", 16 | PCA953X_TYPE | PCA_INT, }, { "pca9505", 40 | PCA953X_TYPE | PCA_INT, }, + { "pca9506", 40 | PCA953X_TYPE | PCA_INT, }, { "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, { "pca9536", 4 | PCA953X_TYPE, }, @@ -1236,6 +1237,7 @@ static int pca953x_resume(struct device *dev) static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pca6416", .data = OF_953X(16, PCA_INT), }, { .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), }, + { .compatible = "nxp,pca9506", .data = OF_953X(40, PCA_INT), }, { .compatible = "nxp,pca9534", .data = OF_953X( 8, PCA_INT), }, { .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), }, { .compatible = "nxp,pca9536", .data = OF_953X( 4, 0), }, diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index a2a8d155c75e..b7568ee33696 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -332,7 +332,7 @@ static int pcf857x_probe(struct i2c_client *client, * reset state. Otherwise it flags pins to be driven low. */ gpio->out = ~n_latch; - gpio->status = gpio->out; + gpio->status = gpio->read(gpio->client); /* Enable irqchip if we have an interrupt */ if (client->irq) { diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 0b572dbc4a36..e7092d5fe700 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -35,6 +35,8 @@ struct gpio_rcar_bank_info { struct gpio_rcar_info { bool has_outdtsel; bool has_both_edge_trigger; + bool has_always_in; + bool has_inen; }; struct gpio_rcar_priv { @@ -62,6 +64,7 @@ struct gpio_rcar_priv { #define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ #define OUTDTSEL 0x40 /* Output Data Select Register */ #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ +#define INEN 0x50 /* General Input Enable Register */ #define RCAR_MAX_GPIO_PER_BANK 32 @@ -302,9 +305,11 @@ static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) struct gpio_rcar_priv *p = gpiochip_get_data(chip); u32 bit = BIT(offset); - /* testing on r8a7790 shows that INDT does not show correct pin state - * when configured as output, so use OUTDT in case of output pins */ - if (gpio_rcar_read(p, INOUTSEL) & bit) + /* + * Before R-Car Gen3, INDT does not show correct pin state when + * configured as output, so use OUTDT in case of output pins + */ + if (!p->info.has_always_in && (gpio_rcar_read(p, INOUTSEL) & bit)) return !!(gpio_rcar_read(p, OUTDT) & bit); else return !!(gpio_rcar_read(p, INDT) & bit); @@ -324,6 +329,11 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, if (!bankmask) return 0; + if (p->info.has_always_in) { + bits[0] = gpio_rcar_read(p, INDT) & bankmask; + return 0; + } + spin_lock_irqsave(&p->lock, flags); outputs = gpio_rcar_read(p, INOUTSEL); m = outputs & bankmask; @@ -383,41 +393,35 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, static const struct gpio_rcar_info gpio_rcar_info_gen1 = { .has_outdtsel = false, .has_both_edge_trigger = false, + .has_always_in = false, + .has_inen = false, }; static const struct gpio_rcar_info gpio_rcar_info_gen2 = { .has_outdtsel = true, .has_both_edge_trigger = true, + .has_always_in = false, + .has_inen = false, +}; + +static const struct gpio_rcar_info gpio_rcar_info_gen3 = { + .has_outdtsel = true, + .has_both_edge_trigger = true, + .has_always_in = true, + .has_inen = false, +}; + +static const struct gpio_rcar_info gpio_rcar_info_v3u = { + .has_outdtsel = true, + .has_both_edge_trigger = true, + .has_always_in = true, + .has_inen = true, }; static const struct of_device_id gpio_rcar_of_table[] = { { - .compatible = "renesas,gpio-r8a7743", - /* RZ/G1 GPIO is identical to R-Car Gen2. */ - .data = &gpio_rcar_info_gen2, - }, { - .compatible = "renesas,gpio-r8a7790", - .data = &gpio_rcar_info_gen2, - }, { - .compatible = "renesas,gpio-r8a7791", - .data = &gpio_rcar_info_gen2, - }, { - .compatible = "renesas,gpio-r8a7792", - .data = &gpio_rcar_info_gen2, - }, { - .compatible = "renesas,gpio-r8a7793", - .data = &gpio_rcar_info_gen2, - }, { - .compatible = "renesas,gpio-r8a7794", - .data = &gpio_rcar_info_gen2, - }, { - .compatible = "renesas,gpio-r8a7795", - /* Gen3 GPIO is identical to Gen2. */ - .data = &gpio_rcar_info_gen2, - }, { - .compatible = "renesas,gpio-r8a7796", - /* Gen3 GPIO is identical to Gen2. */ - .data = &gpio_rcar_info_gen2, + .compatible = "renesas,gpio-r8a779a0", + .data = &gpio_rcar_info_v3u, }, { .compatible = "renesas,rcar-gen1-gpio", .data = &gpio_rcar_info_gen1, @@ -426,8 +430,7 @@ static const struct of_device_id gpio_rcar_of_table[] = { .data = &gpio_rcar_info_gen2, }, { .compatible = "renesas,rcar-gen3-gpio", - /* Gen3 GPIO is identical to Gen2. */ - .data = &gpio_rcar_info_gen2, + .data = &gpio_rcar_info_gen3, }, { .compatible = "renesas,gpio-rcar", .data = &gpio_rcar_info_gen1, @@ -460,6 +463,17 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) return 0; } +static void gpio_rcar_enable_inputs(struct gpio_rcar_priv *p) +{ + u32 mask = GENMASK(p->gpio_chip.ngpio - 1, 0); + + /* Select "Input Enable" in INEN */ + if (p->gpio_chip.valid_mask) + mask &= p->gpio_chip.valid_mask[0]; + if (mask) + gpio_rcar_write(p, INEN, gpio_rcar_read(p, INEN) | mask); +} + static int gpio_rcar_probe(struct platform_device *pdev) { struct gpio_rcar_priv *p; @@ -549,6 +563,12 @@ static int gpio_rcar_probe(struct platform_device *pdev) goto err1; } + if (p->info.has_inen) { + pm_runtime_get_sync(p->dev); + gpio_rcar_enable_inputs(p); + pm_runtime_put(p->dev); + } + dev_info(dev, "driving %d GPIOs\n", npins); return 0; @@ -624,6 +644,9 @@ static int gpio_rcar_resume(struct device *dev) } } + if (p->info.has_inen) + gpio_rcar_enable_inputs(p); + return 0; } #endif /* CONFIG_PM_SLEEP*/ diff --git a/drivers/gpio/gpio-sl28cpld.c b/drivers/gpio/gpio-sl28cpld.c index 889b8f5622c2..52404736ac86 100644 --- a/drivers/gpio/gpio-sl28cpld.c +++ b/drivers/gpio/gpio-sl28cpld.c @@ -65,13 +65,13 @@ static int sl28cpld_gpio_irq_init(struct platform_device *pdev, if (!irq_chip) return -ENOMEM; - irq_chip->name = "sl28cpld-gpio-irq", + irq_chip->name = "sl28cpld-gpio-irq"; irq_chip->irqs = sl28cpld_gpio_irqs; irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs); irq_chip->num_regs = 1; irq_chip->status_base = base + GPIO_REG_IP; irq_chip->mask_base = base + GPIO_REG_IE; - irq_chip->mask_invert = true, + irq_chip->mask_invert = true; irq_chip->ack_base = base + GPIO_REG_IP; ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev), diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index e19ebff6018c..0025f613d9b3 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -60,7 +60,6 @@ struct tegra_gpio_info; struct tegra_gpio_bank { unsigned int bank; - unsigned int irq; /* * IRQ-core code uses raw locking, and thus, nested locking also @@ -81,7 +80,6 @@ struct tegra_gpio_bank { u32 dbc_enb[4]; #endif u32 dbc_cnt[4]; - struct tegra_gpio_info *tgi; }; struct tegra_gpio_soc_config { @@ -93,12 +91,12 @@ struct tegra_gpio_soc_config { struct tegra_gpio_info { struct device *dev; void __iomem *regs; - struct irq_domain *irq_domain; struct tegra_gpio_bank *bank_info; const struct tegra_gpio_soc_config *soc; struct gpio_chip gc; struct irq_chip ic; u32 bank_count; + unsigned int *irqs; }; static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, @@ -274,17 +272,10 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset, return tegra_gpio_set_debounce(chip, offset, debounce); } -static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) -{ - struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - - return irq_find_mapping(tgi->irq_domain, offset); -} - static void tegra_gpio_irq_ack(struct irq_data *d) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); unsigned int gpio = d->hwirq; tegra_gpio_writel(tgi, 1 << GPIO_BIT(gpio), GPIO_INT_CLR(tgi, gpio)); @@ -292,8 +283,8 @@ static void tegra_gpio_irq_ack(struct irq_data *d) static void tegra_gpio_irq_mask(struct irq_data *d) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); unsigned int gpio = d->hwirq; tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0); @@ -301,8 +292,8 @@ static void tegra_gpio_irq_mask(struct irq_data *d) static void tegra_gpio_irq_unmask(struct irq_data *d) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); unsigned int gpio = d->hwirq; tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1); @@ -311,11 +302,14 @@ static void tegra_gpio_irq_unmask(struct irq_data *d) static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) { unsigned int gpio = d->hwirq, port = GPIO_PORT(gpio), lvl_type; - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + struct tegra_gpio_bank *bank; unsigned long flags; - u32 val; int ret; + u32 val; + + bank = &tgi->bank_info[GPIO_BANK(d->hwirq)]; switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_RISING: @@ -367,13 +361,16 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) irq_set_handler_locked(d, handle_edge_irq); - return 0; + if (d->parent_data) + ret = irq_chip_set_type_parent(d, type); + + return ret; } static void tegra_gpio_irq_shutdown(struct irq_data *d) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); unsigned int gpio = d->hwirq; tegra_gpio_irq_mask(d); @@ -382,13 +379,25 @@ static void tegra_gpio_irq_shutdown(struct irq_data *d) static void tegra_gpio_irq_handler(struct irq_desc *desc) { - unsigned int port, pin, gpio; + struct tegra_gpio_info *tgi = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct irq_domain *domain = tgi->gc.irq.domain; + unsigned int irq = irq_desc_get_irq(desc); + struct tegra_gpio_bank *bank = NULL; + unsigned int port, pin, gpio, i; bool unmasked = false; - u32 lvl; unsigned long sta; - struct irq_chip *chip = irq_desc_get_chip(desc); - struct tegra_gpio_bank *bank = irq_desc_get_handler_data(desc); - struct tegra_gpio_info *tgi = bank->tgi; + u32 lvl; + + for (i = 0; i < tgi->bank_count; i++) { + if (tgi->irqs[i] == irq) { + bank = &tgi->bank_info[i]; + break; + } + } + + if (WARN_ON(bank == NULL)) + return; chained_irq_enter(chip, desc); @@ -411,14 +420,47 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } - generic_handle_irq(irq_find_mapping(tgi->irq_domain, - gpio + pin)); + irq = irq_find_mapping(domain, gpio + pin); + if (WARN_ON(irq == 0)) + continue; + + generic_handle_irq(irq); } } if (!unmasked) chained_irq_exit(chip, desc); +} + +static int tegra_gpio_child_to_parent_hwirq(struct gpio_chip *chip, + unsigned int hwirq, + unsigned int type, + unsigned int *parent_hwirq, + unsigned int *parent_type) +{ + *parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq); + *parent_type = type; + + return 0; +} + +static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + fwspec->fwnode = chip->irq.parent_domain->fwnode; + fwspec->param_count = 3; + fwspec->param[0] = 0; + fwspec->param[1] = parent_hwirq; + fwspec->param[2] = parent_type; + + return fwspec; } #ifdef CONFIG_PM_SLEEP @@ -497,19 +539,31 @@ static int tegra_gpio_suspend(struct device *dev) static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + struct tegra_gpio_bank *bank; unsigned int gpio = d->hwirq; u32 port, bit, mask; int err; - err = irq_set_irq_wake(bank->irq, enable); - if (err) - return err; + bank = &tgi->bank_info[GPIO_BANK(d->hwirq)]; port = GPIO_PORT(gpio); bit = GPIO_BIT(gpio); mask = BIT(bit); + err = irq_set_irq_wake(tgi->irqs[bank->bank], enable); + if (err) + return err; + + if (d->parent_data) { + err = irq_chip_set_wake_parent(d, enable); + if (err) { + irq_set_irq_wake(tgi->irqs[bank->bank], !enable); + return err; + } + } + if (enable) bank->wake_enb[port] |= mask; else @@ -519,6 +573,35 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) } #endif +static int tegra_gpio_irq_set_affinity(struct irq_data *data, + const struct cpumask *dest, + bool force) +{ + if (data->parent_data) + return irq_chip_set_affinity_parent(data, dest, force); + + return -EINVAL; +} + +static int tegra_gpio_irq_request_resources(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + + tegra_gpio_enable(tgi, d->hwirq); + + return gpiochip_reqres_irq(chip, d->hwirq); +} + +static void tegra_gpio_irq_release_resources(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + + gpiochip_relres_irq(chip, d->hwirq); + tegra_gpio_enable(tgi, d->hwirq); +} + #ifdef CONFIG_DEBUG_FS #include <linux/debugfs.h> @@ -526,7 +609,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) static int tegra_dbg_gpio_show(struct seq_file *s, void *unused) { - struct tegra_gpio_info *tgi = s->private; + struct tegra_gpio_info *tgi = dev_get_drvdata(s->private); unsigned int i, j; for (i = 0; i < tgi->bank_count; i++) { @@ -548,12 +631,10 @@ static int tegra_dbg_gpio_show(struct seq_file *s, void *unused) return 0; } -DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio); - static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) { - debugfs_create_file("tegra_gpio", 0444, NULL, tgi, - &tegra_dbg_gpio_fops); + debugfs_create_devm_seqfile(tgi->dev, "tegra_gpio", NULL, + tegra_dbg_gpio_show); } #else @@ -568,14 +649,18 @@ static const struct dev_pm_ops tegra_gpio_pm_ops = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) }; -static struct lock_class_key gpio_lock_class; -static struct lock_class_key gpio_request_class; +static const struct of_device_id tegra_pmc_of_match[] = { + { .compatible = "nvidia,tegra210-pmc", }, + { /* sentinel */ }, +}; static int tegra_gpio_probe(struct platform_device *pdev) { - struct tegra_gpio_info *tgi; struct tegra_gpio_bank *bank; - unsigned int gpio, i, j; + struct tegra_gpio_info *tgi; + struct gpio_irq_chip *irq; + struct device_node *np; + unsigned int i, j; int ret; tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL); @@ -604,7 +689,6 @@ static int tegra_gpio_probe(struct platform_device *pdev) tgi->gc.direction_output = tegra_gpio_direction_output; tgi->gc.set = tegra_gpio_set; tgi->gc.get_direction = tegra_gpio_get_direction; - tgi->gc.to_irq = tegra_gpio_to_irq; tgi->gc.base = 0; tgi->gc.ngpio = tgi->bank_count * 32; tgi->gc.parent = &pdev->dev; @@ -619,6 +703,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake; #endif + tgi->ic.irq_request_resources = tegra_gpio_irq_request_resources; + tgi->ic.irq_release_resources = tegra_gpio_irq_release_resources; platform_set_drvdata(pdev, tgi); @@ -630,11 +716,10 @@ static int tegra_gpio_probe(struct platform_device *pdev) if (!tgi->bank_info) return -ENOMEM; - tgi->irq_domain = irq_domain_add_linear(pdev->dev.of_node, - tgi->gc.ngpio, - &irq_domain_simple_ops, NULL); - if (!tgi->irq_domain) - return -ENODEV; + tgi->irqs = devm_kcalloc(&pdev->dev, tgi->bank_count, + sizeof(*tgi->irqs), GFP_KERNEL); + if (!tgi->irqs) + return -ENOMEM; for (i = 0; i < tgi->bank_count; i++) { ret = platform_get_irq(pdev, i); @@ -643,8 +728,36 @@ static int tegra_gpio_probe(struct platform_device *pdev) bank = &tgi->bank_info[i]; bank->bank = i; - bank->irq = ret; - bank->tgi = tgi; + + tgi->irqs[i] = ret; + + for (j = 0; j < 4; j++) { + raw_spin_lock_init(&bank->lvl_lock[j]); + spin_lock_init(&bank->dbc_lock[j]); + } + } + + irq = &tgi->gc.irq; + irq->chip = &tgi->ic; + irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); + irq->child_to_parent_hwirq = tegra_gpio_child_to_parent_hwirq; + irq->populate_parent_alloc_arg = tegra_gpio_populate_parent_fwspec; + irq->handler = handle_simple_irq; + irq->default_type = IRQ_TYPE_NONE; + irq->parent_handler = tegra_gpio_irq_handler; + irq->parent_handler_data = tgi; + irq->num_parents = tgi->bank_count; + irq->parents = tgi->irqs; + + np = of_find_matching_node(NULL, tegra_pmc_of_match); + if (np) { + irq->parent_domain = irq_find_host(np); + of_node_put(np); + + if (!irq->parent_domain) + return -EPROBE_DEFER; + + tgi->ic.irq_set_affinity = tegra_gpio_irq_set_affinity; } tgi->regs = devm_platform_ioremap_resource(pdev, 0); @@ -660,33 +773,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) } ret = devm_gpiochip_add_data(&pdev->dev, &tgi->gc, tgi); - if (ret < 0) { - irq_domain_remove(tgi->irq_domain); + if (ret < 0) return ret; - } - - for (gpio = 0; gpio < tgi->gc.ngpio; gpio++) { - int irq = irq_create_mapping(tgi->irq_domain, gpio); - /* No validity check; all Tegra GPIOs are valid IRQs */ - - bank = &tgi->bank_info[GPIO_BANK(gpio)]; - - irq_set_chip_data(irq, bank); - irq_set_lockdep_class(irq, &gpio_lock_class, &gpio_request_class); - irq_set_chip_and_handler(irq, &tgi->ic, handle_simple_irq); - } - - for (i = 0; i < tgi->bank_count; i++) { - bank = &tgi->bank_info[i]; - - irq_set_chained_handler_and_data(bank->irq, - tegra_gpio_irq_handler, bank); - - for (j = 0; j < 4; j++) { - raw_spin_lock_init(&bank->lvl_lock[j]); - spin_lock_init(&bank->dbc_lock[j]); - } - } tegra_gpio_debuginit(tgi); @@ -715,18 +803,21 @@ static const struct of_device_id tegra_gpio_of_match[] = { { .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config }, { }, }; +MODULE_DEVICE_TABLE(of, tegra_gpio_of_match); static struct platform_driver tegra_gpio_driver = { - .driver = { - .name = "tegra-gpio", - .pm = &tegra_gpio_pm_ops, + .driver = { + .name = "tegra-gpio", + .pm = &tegra_gpio_pm_ops, .of_match_table = tegra_gpio_of_match, }, - .probe = tegra_gpio_probe, + .probe = tegra_gpio_probe, }; - -static int __init tegra_gpio_init(void) -{ - return platform_driver_register(&tegra_gpio_driver); -} -subsys_initcall(tegra_gpio_init); +module_platform_driver(tegra_gpio_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra GPIO controller driver"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_AUTHOR("Erik Gilling <konkers@google.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 286e0b1f46e4..1bd9e44df718 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -657,7 +657,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.get_direction = tegra186_gpio_get_direction; gpio->gpio.direction_input = tegra186_gpio_direction_input; gpio->gpio.direction_output = tegra186_gpio_direction_output; - gpio->gpio.get = tegra186_gpio_get, + gpio->gpio.get = tegra186_gpio_get; gpio->gpio.set = tegra186_gpio_set; gpio->gpio.set_config = tegra186_gpio_set_config; gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; diff --git a/drivers/gpio/gpio-visconti.c b/drivers/gpio/gpio-visconti.c new file mode 100644 index 000000000000..0e3d19828eb1 --- /dev/null +++ b/drivers/gpio/gpio-visconti.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Toshiba Visconti GPIO Support + * + * (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation + * (C) Copyright 2020 TOSHIBA CORPORATION + * + * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp> + */ + +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/bitops.h> + +/* register offset */ +#define GPIO_DIR 0x00 +#define GPIO_IDATA 0x08 +#define GPIO_ODATA 0x10 +#define GPIO_OSET 0x18 +#define GPIO_OCLR 0x20 +#define GPIO_INTMODE 0x30 + +#define BASE_HW_IRQ 24 + +struct visconti_gpio { + void __iomem *base; + spinlock_t lock; /* protect gpio register */ + struct gpio_chip gpio_chip; + struct irq_chip irq_chip; +}; + +static int visconti_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct visconti_gpio *priv = gpiochip_get_data(gc); + u32 offset = irqd_to_hwirq(d); + u32 bit = BIT(offset); + u32 intc_type = IRQ_TYPE_EDGE_RISING; + u32 intmode, odata; + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + odata = readl(priv->base + GPIO_ODATA); + intmode = readl(priv->base + GPIO_INTMODE); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + odata &= ~bit; + intmode &= ~bit; + break; + case IRQ_TYPE_EDGE_FALLING: + odata |= bit; + intmode &= ~bit; + break; + case IRQ_TYPE_EDGE_BOTH: + intmode |= bit; + break; + case IRQ_TYPE_LEVEL_HIGH: + intc_type = IRQ_TYPE_LEVEL_HIGH; + odata &= ~bit; + intmode &= ~bit; + break; + case IRQ_TYPE_LEVEL_LOW: + intc_type = IRQ_TYPE_LEVEL_HIGH; + odata |= bit; + intmode &= ~bit; + break; + default: + ret = -EINVAL; + goto err; + } + + writel(odata, priv->base + GPIO_ODATA); + writel(intmode, priv->base + GPIO_INTMODE); + irq_set_irq_type(offset, intc_type); + + ret = irq_chip_set_type_parent(d, type); +err: + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} + +static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) +{ + /* Interrupts 0..15 mapped to interrupts 24..39 on the GIC */ + if (child < 16) { + /* All these interrupts are level high in the CPU */ + *parent_type = IRQ_TYPE_LEVEL_HIGH; + *parent = child + BASE_HW_IRQ; + return 0; + } + return -EINVAL; +} + +static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; + fwspec->param_count = 3; + fwspec->param[0] = 0; + fwspec->param[1] = parent_hwirq; + fwspec->param[2] = parent_type; + + return fwspec; +} + +static int visconti_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct visconti_gpio *priv; + struct irq_chip *irq_chip; + struct gpio_irq_chip *girq; + struct irq_domain *parent; + struct device_node *irq_parent; + struct fwnode_handle *fwnode; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + irq_parent = of_irq_find_parent(dev->of_node); + if (!irq_parent) { + dev_err(dev, "No IRQ parent node\n"); + return -ENODEV; + } + + parent = irq_find_host(irq_parent); + if (!parent) { + dev_err(dev, "No IRQ parent domain\n"); + return -ENODEV; + } + + fwnode = of_node_to_fwnode(irq_parent); + of_node_put(irq_parent); + + ret = bgpio_init(&priv->gpio_chip, dev, 4, + priv->base + GPIO_IDATA, + priv->base + GPIO_OSET, + priv->base + GPIO_OCLR, + priv->base + GPIO_DIR, + NULL, + 0); + if (ret) { + dev_err(dev, "unable to init generic GPIO\n"); + return ret; + } + + irq_chip = &priv->irq_chip; + irq_chip->name = dev_name(dev); + irq_chip->irq_mask = irq_chip_mask_parent; + irq_chip->irq_unmask = irq_chip_unmask_parent; + irq_chip->irq_eoi = irq_chip_eoi_parent; + irq_chip->irq_set_type = visconti_gpio_irq_set_type; + irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND; + + girq = &priv->gpio_chip.irq; + girq->chip = irq_chip; + girq->fwnode = fwnode; + girq->parent_domain = parent; + girq->child_to_parent_hwirq = visconti_gpio_child_to_parent_hwirq; + girq->populate_parent_alloc_arg = visconti_gpio_populate_parent_fwspec; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + + ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv); + if (ret) { + dev_err(dev, "failed to add GPIO chip\n"); + return ret; + } + + platform_set_drvdata(pdev, priv); + + return ret; +} + +static const struct of_device_id visconti_gpio_of_match[] = { + { .compatible = "toshiba,gpio-tmpv7708", }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, visconti_gpio_of_match); + +static struct platform_driver visconti_gpio_driver = { + .probe = visconti_gpio_probe, + .driver = { + .name = "visconti_gpio", + .of_match_table = of_match_ptr(visconti_gpio_of_match), + } +}; +module_platform_driver(visconti_gpio_driver); + +MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>"); +MODULE_DESCRIPTION("Toshiba Visconti GPIO Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 3bf397b8dfbc..69713fd5485b 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -216,7 +216,7 @@ static void vx855gpio_gpio_setup(struct vx855_gpio *vg) c->direction_output = vx855gpio_direction_output; c->get = vx855gpio_get; c->set = vx855gpio_set; - c->set_config = vx855gpio_set_config, + c->set_config = vx855gpio_set_config; c->dbg_show = NULL; c->base = 0; c->ngpio = NR_VX855_GP; diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index b5fbba5a783a..a19eeef6cf1e 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -73,6 +73,8 @@ enum ctrl_register { CTRL_IN, CTRL_OUT, + IRQ_STATUS, + IRQ_MASK, }; /* @@ -112,22 +114,29 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type) return reg; } -static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) +static inline int to_ireg(int gpio, enum ctrl_register type, unsigned int *mask) { - unsigned int reg, mask; + unsigned int reg = type == IRQ_STATUS ? IRQ_STATUS_BASE : IRQ_MASK_BASE; if (gpio < GROUP0_NR_IRQS) { - reg = IRQ_MASK_BASE; - mask = BIT(gpio % GROUP0_NR_IRQS); + reg += 0; + *mask = BIT(gpio); } else { - reg = IRQ_MASK_BASE + 1; - mask = BIT((gpio - GROUP0_NR_IRQS) % GROUP1_NR_IRQS); + reg += 1; + *mask = BIT(gpio - GROUP0_NR_IRQS); } + return reg; +} + +static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) +{ + unsigned int mask, reg = to_ireg(gpio, IRQ_MASK, &mask); + if (wg->set_irq_mask) - regmap_update_bits(wg->regmap, reg, mask, mask); + regmap_set_bits(wg->regmap, reg, mask); else - regmap_update_bits(wg->regmap, reg, mask, 0); + regmap_clear_bits(wg->regmap, reg, mask); } static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) @@ -207,9 +216,9 @@ static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) return; if (value) - regmap_update_bits(wg->regmap, reg, 1, 1); + regmap_set_bits(wg->regmap, reg, 1); else - regmap_update_bits(wg->regmap, reg, 1, 0); + regmap_clear_bits(wg->regmap, reg, 1); } static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, @@ -324,7 +333,8 @@ static struct irq_chip wcove_irqchip = { static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) { struct wcove_gpio *wg = (struct wcove_gpio *)data; - unsigned int pending, virq, gpio, mask, offset; + unsigned int virq, gpio; + unsigned long pending; u8 p[2]; if (regmap_bulk_read(wg->regmap, IRQ_STATUS_BASE, p, 2)) { @@ -339,15 +349,12 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) /* Iterate until no interrupt is pending */ while (pending) { /* One iteration is for all pending bits */ - for_each_set_bit(gpio, (const unsigned long *)&pending, - WCOVE_GPIO_NUM) { - offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0; - mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) : - BIT(gpio); + for_each_set_bit(gpio, &pending, WCOVE_GPIO_NUM) { + unsigned int mask, reg = to_ireg(gpio, IRQ_STATUS, &mask); + virq = irq_find_mapping(wg->chip.irq.domain, gpio); handle_nested_irq(virq); - regmap_update_bits(wg->regmap, IRQ_STATUS_BASE + offset, - mask, mask); + regmap_set_bits(wg->regmap, reg, mask); } /* Next iteration */ @@ -367,30 +374,26 @@ static void wcove_gpio_dbg_show(struct seq_file *s, { unsigned int ctlo, ctli, irq_mask, irq_status; struct wcove_gpio *wg = gpiochip_get_data(chip); - int gpio, offset, group, ret = 0; + int gpio, mask, ret = 0; for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) { - group = gpio < GROUP0_NR_IRQS ? 0 : 1; ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo); ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli); - ret += regmap_read(wg->regmap, IRQ_MASK_BASE + group, - &irq_mask); - ret += regmap_read(wg->regmap, IRQ_STATUS_BASE + group, - &irq_status); + ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_MASK, &mask), &irq_mask); + ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_STATUS, &mask), &irq_status); if (ret) { pr_err("Failed to read registers: ctrl out/in or irq status/mask\n"); break; } - offset = gpio % 8; seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s\n", gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ", ctli & 0x1 ? "hi" : "lo", ctli & CTLI_INTCNT_NE ? "fall" : " ", ctli & CTLI_INTCNT_PE ? "rise" : " ", ctlo, - irq_mask & BIT(offset) ? "mask " : "unmask", - irq_status & BIT(offset) ? "pending" : " "); + irq_mask & mask ? "mask " : "unmask", + irq_status & mask ? "pending" : " "); } } @@ -434,7 +437,7 @@ static int wcove_gpio_probe(struct platform_device *pdev) wg->chip.get_direction = wcove_gpio_get_direction; wg->chip.get = wcove_gpio_get; wg->chip.set = wcove_gpio_set; - wg->chip.set_config = wcove_gpio_set_config, + wg->chip.set_config = wcove_gpio_set_config; wg->chip.base = -1; wg->chip.ngpio = WCOVE_VGPIO_NUM; wg->chip.can_sleep = true; @@ -473,14 +476,12 @@ static int wcove_gpio_probe(struct platform_device *pdev) } /* Enable GPIO0 interrupts */ - ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE, GPIO_IRQ0_MASK, - 0x00); + ret = regmap_clear_bits(wg->regmap, IRQ_MASK_BASE + 0, GPIO_IRQ0_MASK); if (ret) return ret; /* Enable GPIO1 interrupts */ - ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK, - 0x00); + ret = regmap_clear_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK); if (ret) return ret; diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index be539381fd82..b411d3156e0b 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -10,10 +10,13 @@ #include <linux/errno.h> #include <linux/gpio/driver.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/io.h> +#include <linux/irq.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/of_platform.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> /* Register Offset Definitions */ @@ -22,6 +25,11 @@ #define XGPIO_CHANNEL_OFFSET 0x8 +#define XGPIO_GIER_OFFSET 0x11c /* Global Interrupt Enable */ +#define XGPIO_GIER_IE BIT(31) +#define XGPIO_IPISR_OFFSET 0x120 /* IP Interrupt Status */ +#define XGPIO_IPIER_OFFSET 0x128 /* IP Interrupt Enable */ + /* Read/Write access to the GPIO registers */ #if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_X86) # define xgpio_readreg(offset) readl(offset) @@ -36,9 +44,15 @@ * @gc: GPIO chip * @regs: register block * @gpio_width: GPIO width for every channel - * @gpio_state: GPIO state shadow register + * @gpio_state: GPIO write state shadow register + * @gpio_last_irq_read: GPIO read state register from last interrupt * @gpio_dir: GPIO direction shadow register * @gpio_lock: Lock used for synchronization + * @irq: IRQ used by GPIO device + * @irqchip: IRQ chip + * @irq_enable: GPIO IRQ enable/disable bitfield + * @irq_rising_edge: GPIO IRQ rising edge enable/disable bitfield + * @irq_falling_edge: GPIO IRQ falling edge enable/disable bitfield * @clk: clock resource for this driver */ struct xgpio_instance { @@ -46,8 +60,14 @@ struct xgpio_instance { void __iomem *regs; unsigned int gpio_width[2]; u32 gpio_state[2]; + u32 gpio_last_irq_read[2]; u32 gpio_dir[2]; - spinlock_t gpio_lock[2]; + spinlock_t gpio_lock; /* For serializing operations */ + int irq; + struct irq_chip irqchip; + u32 irq_enable[2]; + u32 irq_rising_edge[2]; + u32 irq_falling_edge[2]; struct clk *clk; }; @@ -113,7 +133,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) int index = xgpio_index(chip, gpio); int offset = xgpio_offset(chip, gpio); - spin_lock_irqsave(&chip->gpio_lock[index], flags); + spin_lock_irqsave(&chip->gpio_lock, flags); /* Write to GPIO signal and set its direction to output */ if (val) @@ -124,7 +144,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET + xgpio_regoffset(chip, gpio), chip->gpio_state[index]); - spin_unlock_irqrestore(&chip->gpio_lock[index], flags); + spin_unlock_irqrestore(&chip->gpio_lock, flags); } /** @@ -144,7 +164,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, int index = xgpio_index(chip, 0); int offset, i; - spin_lock_irqsave(&chip->gpio_lock[index], flags); + spin_lock_irqsave(&chip->gpio_lock, flags); /* Write to GPIO signals */ for (i = 0; i < gc->ngpio; i++) { @@ -155,9 +175,9 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET + index * XGPIO_CHANNEL_OFFSET, chip->gpio_state[index]); - spin_unlock_irqrestore(&chip->gpio_lock[index], flags); + spin_unlock_irqrestore(&chip->gpio_lock, flags); index = xgpio_index(chip, i); - spin_lock_irqsave(&chip->gpio_lock[index], flags); + spin_lock_irqsave(&chip->gpio_lock, flags); } if (__test_and_clear_bit(i, mask)) { offset = xgpio_offset(chip, i); @@ -171,7 +191,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET + index * XGPIO_CHANNEL_OFFSET, chip->gpio_state[index]); - spin_unlock_irqrestore(&chip->gpio_lock[index], flags); + spin_unlock_irqrestore(&chip->gpio_lock, flags); } /** @@ -190,14 +210,14 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) int index = xgpio_index(chip, gpio); int offset = xgpio_offset(chip, gpio); - spin_lock_irqsave(&chip->gpio_lock[index], flags); + spin_lock_irqsave(&chip->gpio_lock, flags); /* Set the GPIO bit in shadow register and set direction as input */ chip->gpio_dir[index] |= BIT(offset); xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET + xgpio_regoffset(chip, gpio), chip->gpio_dir[index]); - spin_unlock_irqrestore(&chip->gpio_lock[index], flags); + spin_unlock_irqrestore(&chip->gpio_lock, flags); return 0; } @@ -221,7 +241,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) int index = xgpio_index(chip, gpio); int offset = xgpio_offset(chip, gpio); - spin_lock_irqsave(&chip->gpio_lock[index], flags); + spin_lock_irqsave(&chip->gpio_lock, flags); /* Write state of GPIO signal */ if (val) @@ -236,7 +256,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET + xgpio_regoffset(chip, gpio), chip->gpio_dir[index]); - spin_unlock_irqrestore(&chip->gpio_lock[index], flags); + spin_unlock_irqrestore(&chip->gpio_lock, flags); return 0; } @@ -259,6 +279,39 @@ static void xgpio_save_regs(struct xgpio_instance *chip) chip->gpio_dir[1]); } +static int xgpio_request(struct gpio_chip *chip, unsigned int offset) +{ + int ret; + + ret = pm_runtime_get_sync(chip->parent); + /* + * If the device is already active pm_runtime_get() will return 1 on + * success, but gpio_request still needs to return 0. + */ + return ret < 0 ? ret : 0; +} + +static void xgpio_free(struct gpio_chip *chip, unsigned int offset) +{ + pm_runtime_put(chip->parent); +} + +static int __maybe_unused xgpio_suspend(struct device *dev) +{ + struct xgpio_instance *gpio = dev_get_drvdata(dev); + struct irq_data *data = irq_get_irq_data(gpio->irq); + + if (!data) { + dev_err(dev, "irq_get_irq_data() failed\n"); + return -EINVAL; + } + + if (!irqd_is_wakeup_set(data)) + return pm_runtime_force_suspend(dev); + + return 0; +} + /** * xgpio_remove - Remove method for the GPIO device. * @pdev: pointer to the platform device @@ -271,12 +324,224 @@ static int xgpio_remove(struct platform_device *pdev) { struct xgpio_instance *gpio = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); clk_disable_unprepare(gpio->clk); return 0; } /** + * xgpio_irq_ack - Acknowledge a child GPIO interrupt. + * @irq_data: per IRQ and chip data passed down to chip functions + * This currently does nothing, but irq_ack is unconditionally called by + * handle_edge_irq and therefore must be defined. + */ +static void xgpio_irq_ack(struct irq_data *irq_data) +{ +} + +static int __maybe_unused xgpio_resume(struct device *dev) +{ + struct xgpio_instance *gpio = dev_get_drvdata(dev); + struct irq_data *data = irq_get_irq_data(gpio->irq); + + if (!data) { + dev_err(dev, "irq_get_irq_data() failed\n"); + return -EINVAL; + } + + if (!irqd_is_wakeup_set(data)) + return pm_runtime_force_resume(dev); + + return 0; +} + +static int __maybe_unused xgpio_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct xgpio_instance *gpio = platform_get_drvdata(pdev); + + clk_disable(gpio->clk); + + return 0; +} + +static int __maybe_unused xgpio_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct xgpio_instance *gpio = platform_get_drvdata(pdev); + + return clk_enable(gpio->clk); +} + +static const struct dev_pm_ops xgpio_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume) + SET_RUNTIME_PM_OPS(xgpio_runtime_suspend, + xgpio_runtime_resume, NULL) +}; + +/** + * xgpio_irq_mask - Write the specified signal of the GPIO device. + * @irq_data: per IRQ and chip data passed down to chip functions + */ +static void xgpio_irq_mask(struct irq_data *irq_data) +{ + unsigned long flags; + struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); + int irq_offset = irqd_to_hwirq(irq_data); + int index = xgpio_index(chip, irq_offset); + int offset = xgpio_offset(chip, irq_offset); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + chip->irq_enable[index] &= ~BIT(offset); + + if (!chip->irq_enable[index]) { + /* Disable per channel interrupt */ + u32 temp = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET); + + temp &= ~BIT(index); + xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, temp); + } + spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +/** + * xgpio_irq_unmask - Write the specified signal of the GPIO device. + * @irq_data: per IRQ and chip data passed down to chip functions + */ +static void xgpio_irq_unmask(struct irq_data *irq_data) +{ + unsigned long flags; + struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); + int irq_offset = irqd_to_hwirq(irq_data); + int index = xgpio_index(chip, irq_offset); + int offset = xgpio_offset(chip, irq_offset); + u32 old_enable = chip->irq_enable[index]; + + spin_lock_irqsave(&chip->gpio_lock, flags); + + chip->irq_enable[index] |= BIT(offset); + + if (!old_enable) { + /* Clear any existing per-channel interrupts */ + u32 val = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET) & + BIT(index); + + if (val) + xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, val); + + /* Update GPIO IRQ read data before enabling interrupt*/ + val = xgpio_readreg(chip->regs + XGPIO_DATA_OFFSET + + index * XGPIO_CHANNEL_OFFSET); + chip->gpio_last_irq_read[index] = val; + + /* Enable per channel interrupt */ + val = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET); + val |= BIT(index); + xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, val); + } + + spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +/** + * xgpio_set_irq_type - Write the specified signal of the GPIO device. + * @irq_data: Per IRQ and chip data passed down to chip functions + * @type: Interrupt type that is to be set for the gpio pin + * + * Return: + * 0 if interrupt type is supported otherwise -EINVAL + */ +static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type) +{ + struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); + int irq_offset = irqd_to_hwirq(irq_data); + int index = xgpio_index(chip, irq_offset); + int offset = xgpio_offset(chip, irq_offset); + + /* + * The Xilinx GPIO hardware provides a single interrupt status + * indication for any state change in a given GPIO channel (bank). + * Therefore, only rising edge or falling edge triggers are + * supported. + */ + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_BOTH: + chip->irq_rising_edge[index] |= BIT(offset); + chip->irq_falling_edge[index] |= BIT(offset); + break; + case IRQ_TYPE_EDGE_RISING: + chip->irq_rising_edge[index] |= BIT(offset); + chip->irq_falling_edge[index] &= ~BIT(offset); + break; + case IRQ_TYPE_EDGE_FALLING: + chip->irq_rising_edge[index] &= ~BIT(offset); + chip->irq_falling_edge[index] |= BIT(offset); + break; + default: + return -EINVAL; + } + + irq_set_handler_locked(irq_data, handle_edge_irq); + return 0; +} + +/** + * xgpio_irqhandler - Gpio interrupt service routine + * @desc: Pointer to interrupt description + */ +static void xgpio_irqhandler(struct irq_desc *desc) +{ + struct xgpio_instance *chip = irq_desc_get_handler_data(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + u32 num_channels = chip->gpio_width[1] ? 2 : 1; + u32 offset = 0, index; + u32 status = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET); + + xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, status); + + chained_irq_enter(irqchip, desc); + for (index = 0; index < num_channels; index++) { + if ((status & BIT(index))) { + unsigned long rising_events, falling_events, all_events; + unsigned long flags; + u32 data, bit; + unsigned int irq; + + spin_lock_irqsave(&chip->gpio_lock, flags); + data = xgpio_readreg(chip->regs + XGPIO_DATA_OFFSET + + index * XGPIO_CHANNEL_OFFSET); + rising_events = data & + ~chip->gpio_last_irq_read[index] & + chip->irq_enable[index] & + chip->irq_rising_edge[index]; + falling_events = ~data & + chip->gpio_last_irq_read[index] & + chip->irq_enable[index] & + chip->irq_falling_edge[index]; + dev_dbg(chip->gc.parent, + "IRQ chan %u rising 0x%lx falling 0x%lx\n", + index, rising_events, falling_events); + all_events = rising_events | falling_events; + chip->gpio_last_irq_read[index] = data; + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + for_each_set_bit(bit, &all_events, 32) { + irq = irq_find_mapping(chip->gc.irq.domain, + offset + bit); + generic_handle_irq(irq); + } + } + offset += chip->gpio_width[index]; + } + + chained_irq_exit(irqchip, desc); +} + +/** * xgpio_of_probe - Probe method for the GPIO device. * @pdev: pointer to the platform device * @@ -289,7 +554,10 @@ static int xgpio_probe(struct platform_device *pdev) struct xgpio_instance *chip; int status = 0; struct device_node *np = pdev->dev.of_node; - u32 is_dual; + u32 is_dual = 0; + u32 cells = 2; + struct gpio_irq_chip *girq; + u32 temp; chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -305,6 +573,15 @@ static int xgpio_probe(struct platform_device *pdev) if (of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir[0])) chip->gpio_dir[0] = 0xFFFFFFFF; + /* Update cells with gpio-cells value */ + if (of_property_read_u32(np, "#gpio-cells", &cells)) + dev_dbg(&pdev->dev, "Missing gpio-cells property\n"); + + if (cells != 2) { + dev_err(&pdev->dev, "#gpio-cells mismatch\n"); + return -EINVAL; + } + /* * Check device node and parent device node for device width * and assume default width of 32 @@ -312,7 +589,10 @@ static int xgpio_probe(struct platform_device *pdev) if (of_property_read_u32(np, "xlnx,gpio-width", &chip->gpio_width[0])) chip->gpio_width[0] = 32; - spin_lock_init(&chip->gpio_lock[0]); + if (chip->gpio_width[0] > 32) + return -EINVAL; + + spin_lock_init(&chip->gpio_lock); if (of_property_read_u32(np, "xlnx,is-dual", &is_dual)) is_dual = 0; @@ -336,7 +616,8 @@ static int xgpio_probe(struct platform_device *pdev) &chip->gpio_width[1])) chip->gpio_width[1] = 32; - spin_lock_init(&chip->gpio_lock[1]); + if (chip->gpio_width[1] > 32) + return -EINVAL; } chip->gc.base = -1; @@ -344,8 +625,11 @@ static int xgpio_probe(struct platform_device *pdev) chip->gc.parent = &pdev->dev; chip->gc.direction_input = xgpio_dir_in; chip->gc.direction_output = xgpio_dir_out; + chip->gc.of_gpio_n_cells = cells; chip->gc.get = xgpio_get; chip->gc.set = xgpio_set; + chip->gc.request = xgpio_request; + chip->gc.free = xgpio_free; chip->gc.set_multiple = xgpio_set_multiple; chip->gc.label = dev_name(&pdev->dev); @@ -357,28 +641,68 @@ static int xgpio_probe(struct platform_device *pdev) } chip->clk = devm_clk_get_optional(&pdev->dev, NULL); - if (IS_ERR(chip->clk)) { - if (PTR_ERR(chip->clk) != -EPROBE_DEFER) - dev_dbg(&pdev->dev, "Input clock not found\n"); - return PTR_ERR(chip->clk); - } + if (IS_ERR(chip->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk), "input clock not found.\n"); status = clk_prepare_enable(chip->clk); if (status < 0) { dev_err(&pdev->dev, "Failed to prepare clk\n"); return status; } + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); xgpio_save_regs(chip); + chip->irq = platform_get_irq_optional(pdev, 0); + if (chip->irq <= 0) + goto skip_irq; + + chip->irqchip.name = "gpio-xilinx"; + chip->irqchip.irq_ack = xgpio_irq_ack; + chip->irqchip.irq_mask = xgpio_irq_mask; + chip->irqchip.irq_unmask = xgpio_irq_unmask; + chip->irqchip.irq_set_type = xgpio_set_irq_type; + + /* Disable per-channel interrupts */ + xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, 0); + /* Clear any existing per-channel interrupts */ + temp = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET); + xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, temp); + /* Enable global interrupts */ + xgpio_writereg(chip->regs + XGPIO_GIER_OFFSET, XGPIO_GIER_IE); + + girq = &chip->gc.irq; + girq->chip = &chip->irqchip; + girq->parent_handler = xgpio_irqhandler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) { + status = -ENOMEM; + goto err_pm_put; + } + girq->parents[0] = chip->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + +skip_irq: status = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip); if (status) { dev_err(&pdev->dev, "failed to add GPIO chip\n"); - clk_disable_unprepare(chip->clk); - return status; + goto err_pm_put; } + pm_runtime_put(&pdev->dev); return 0; + +err_pm_put: + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + clk_disable_unprepare(chip->clk); + return status; } static const struct of_device_id xgpio_of_match[] = { @@ -394,6 +718,7 @@ static struct platform_driver xgpio_plat_driver = { .driver = { .name = "gpio-xilinx", .of_match_table = xgpio_of_match, + .pm = &xgpio_dev_pm_ops, }, }; diff --git a/drivers/gpio/gpio-zx.c b/drivers/gpio/gpio-zx.c deleted file mode 100644 index 64bfb722756a..000000000000 --- a/drivers/gpio/gpio-zx.c +++ /dev/null @@ -1,289 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ZTE ZX296702 GPIO driver - * - * Author: Jun Nie <jun.nie@linaro.org> - * - * Copyright (C) 2015 Linaro Ltd. - */ -#include <linux/bitops.h> -#include <linux/device.h> -#include <linux/errno.h> -#include <linux/gpio/driver.h> -#include <linux/irqchip/chained_irq.h> -#include <linux/init.h> -#include <linux/of.h> -#include <linux/pinctrl/consumer.h> -#include <linux/platform_device.h> -#include <linux/pm.h> -#include <linux/slab.h> -#include <linux/spinlock.h> - -#define ZX_GPIO_DIR 0x00 -#define ZX_GPIO_IVE 0x04 -#define ZX_GPIO_IV 0x08 -#define ZX_GPIO_IEP 0x0C -#define ZX_GPIO_IEN 0x10 -#define ZX_GPIO_DI 0x14 -#define ZX_GPIO_DO1 0x18 -#define ZX_GPIO_DO0 0x1C -#define ZX_GPIO_DO 0x20 - -#define ZX_GPIO_IM 0x28 -#define ZX_GPIO_IE 0x2C - -#define ZX_GPIO_MIS 0x30 -#define ZX_GPIO_IC 0x34 - -#define ZX_GPIO_NR 16 - -struct zx_gpio { - raw_spinlock_t lock; - - void __iomem *base; - struct gpio_chip gc; -}; - -static int zx_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct zx_gpio *chip = gpiochip_get_data(gc); - unsigned long flags; - u16 gpiodir; - - if (offset >= gc->ngpio) - return -EINVAL; - - raw_spin_lock_irqsave(&chip->lock, flags); - gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR); - gpiodir &= ~BIT(offset); - writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR); - raw_spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int zx_direction_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct zx_gpio *chip = gpiochip_get_data(gc); - unsigned long flags; - u16 gpiodir; - - if (offset >= gc->ngpio) - return -EINVAL; - - raw_spin_lock_irqsave(&chip->lock, flags); - gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR); - gpiodir |= BIT(offset); - writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR); - - if (value) - writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1); - else - writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0); - raw_spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int zx_get_value(struct gpio_chip *gc, unsigned offset) -{ - struct zx_gpio *chip = gpiochip_get_data(gc); - - return !!(readw_relaxed(chip->base + ZX_GPIO_DI) & BIT(offset)); -} - -static void zx_set_value(struct gpio_chip *gc, unsigned offset, int value) -{ - struct zx_gpio *chip = gpiochip_get_data(gc); - - if (value) - writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1); - else - writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0); -} - -static int zx_irq_type(struct irq_data *d, unsigned trigger) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct zx_gpio *chip = gpiochip_get_data(gc); - int offset = irqd_to_hwirq(d); - unsigned long flags; - u16 gpiois, gpioi_epos, gpioi_eneg, gpioiev; - u16 bit = BIT(offset); - - if (offset < 0 || offset >= ZX_GPIO_NR) - return -EINVAL; - - raw_spin_lock_irqsave(&chip->lock, flags); - - gpioiev = readw_relaxed(chip->base + ZX_GPIO_IV); - gpiois = readw_relaxed(chip->base + ZX_GPIO_IVE); - gpioi_epos = readw_relaxed(chip->base + ZX_GPIO_IEP); - gpioi_eneg = readw_relaxed(chip->base + ZX_GPIO_IEN); - - if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { - gpiois |= bit; - if (trigger & IRQ_TYPE_LEVEL_HIGH) - gpioiev |= bit; - else - gpioiev &= ~bit; - } else - gpiois &= ~bit; - - if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { - gpioi_epos |= bit; - gpioi_eneg |= bit; - } else { - if (trigger & IRQ_TYPE_EDGE_RISING) { - gpioi_epos |= bit; - gpioi_eneg &= ~bit; - } else if (trigger & IRQ_TYPE_EDGE_FALLING) { - gpioi_eneg |= bit; - gpioi_epos &= ~bit; - } - } - - writew_relaxed(gpiois, chip->base + ZX_GPIO_IVE); - writew_relaxed(gpioi_epos, chip->base + ZX_GPIO_IEP); - writew_relaxed(gpioi_eneg, chip->base + ZX_GPIO_IEN); - writew_relaxed(gpioiev, chip->base + ZX_GPIO_IV); - raw_spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static void zx_irq_handler(struct irq_desc *desc) -{ - unsigned long pending; - int offset; - struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct zx_gpio *chip = gpiochip_get_data(gc); - struct irq_chip *irqchip = irq_desc_get_chip(desc); - - chained_irq_enter(irqchip, desc); - - pending = readw_relaxed(chip->base + ZX_GPIO_MIS); - writew_relaxed(pending, chip->base + ZX_GPIO_IC); - if (pending) { - for_each_set_bit(offset, &pending, ZX_GPIO_NR) - generic_handle_irq(irq_find_mapping(gc->irq.domain, - offset)); - } - - chained_irq_exit(irqchip, desc); -} - -static void zx_irq_mask(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct zx_gpio *chip = gpiochip_get_data(gc); - u16 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR); - u16 gpioie; - - raw_spin_lock(&chip->lock); - gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) | mask; - writew_relaxed(gpioie, chip->base + ZX_GPIO_IM); - gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) & ~mask; - writew_relaxed(gpioie, chip->base + ZX_GPIO_IE); - raw_spin_unlock(&chip->lock); -} - -static void zx_irq_unmask(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct zx_gpio *chip = gpiochip_get_data(gc); - u16 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR); - u16 gpioie; - - raw_spin_lock(&chip->lock); - gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) & ~mask; - writew_relaxed(gpioie, chip->base + ZX_GPIO_IM); - gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) | mask; - writew_relaxed(gpioie, chip->base + ZX_GPIO_IE); - raw_spin_unlock(&chip->lock); -} - -static struct irq_chip zx_irqchip = { - .name = "zx-gpio", - .irq_mask = zx_irq_mask, - .irq_unmask = zx_irq_unmask, - .irq_set_type = zx_irq_type, -}; - -static int zx_gpio_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct zx_gpio *chip; - struct gpio_irq_chip *girq; - int irq, id, ret; - - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(chip->base)) - return PTR_ERR(chip->base); - - id = of_alias_get_id(dev->of_node, "gpio"); - - raw_spin_lock_init(&chip->lock); - chip->gc.request = gpiochip_generic_request; - chip->gc.free = gpiochip_generic_free; - chip->gc.direction_input = zx_direction_input; - chip->gc.direction_output = zx_direction_output; - chip->gc.get = zx_get_value; - chip->gc.set = zx_set_value; - chip->gc.base = ZX_GPIO_NR * id; - chip->gc.ngpio = ZX_GPIO_NR; - chip->gc.label = dev_name(dev); - chip->gc.parent = dev; - chip->gc.owner = THIS_MODULE; - - /* - * irq_chip support - */ - writew_relaxed(0xffff, chip->base + ZX_GPIO_IM); - writew_relaxed(0, chip->base + ZX_GPIO_IE); - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - girq = &chip->gc.irq; - girq->chip = &zx_irqchip; - girq->parent_handler = zx_irq_handler; - girq->num_parents = 1; - girq->parents = devm_kcalloc(&pdev->dev, 1, - sizeof(*girq->parents), - GFP_KERNEL); - if (!girq->parents) - return -ENOMEM; - girq->parents[0] = irq; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_simple_irq; - - ret = gpiochip_add_data(&chip->gc, chip); - if (ret) - return ret; - - platform_set_drvdata(pdev, chip); - dev_info(dev, "ZX GPIO chip registered\n"); - - return 0; -} - -static const struct of_device_id zx_gpio_match[] = { - { - .compatible = "zte,zx296702-gpio", - }, - { }, -}; - -static struct platform_driver zx_gpio_driver = { - .probe = zx_gpio_probe, - .driver = { - .name = "zx_gpio", - .of_match_table = of_match_ptr(zx_gpio_match), - }, -}; -builtin_platform_driver(zx_gpio_driver) diff --git a/drivers/pinctrl/visconti/pinctrl-common.c b/drivers/pinctrl/visconti/pinctrl-common.c index 0cb10b7b4430..21c7e0d18fea 100644 --- a/drivers/pinctrl/visconti/pinctrl-common.c +++ b/drivers/pinctrl/visconti/pinctrl-common.c @@ -245,11 +245,34 @@ static int visconti_set_mux(struct pinctrl_dev *pctldev, return 0; } +static int visconti_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); + const struct visconti_mux *gpio_mux = &priv->devdata->gpio_mux[pin]; + unsigned long flags; + unsigned int val; + + dev_dbg(priv->dev, "%s: pin = %d\n", __func__, pin); + + /* update mux */ + spin_lock_irqsave(&priv->lock, flags); + val = readl(priv->base + gpio_mux->offset); + val &= ~gpio_mux->mask; + val |= gpio_mux->val; + writel(val, priv->base + gpio_mux->offset); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + static const struct pinmux_ops visconti_pinmux_ops = { .get_functions_count = visconti_get_functions_count, .get_function_name = visconti_get_function_name, .get_function_groups = visconti_get_function_groups, .set_mux = visconti_set_mux, + .gpio_request_enable = visconti_gpio_request_enable, .strict = true, }; diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index 781a053abbb9..d755e529c1e3 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -75,7 +75,7 @@ struct gpiod_hog { * gpiod_get_index() */ #define GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, _idx, _flags) \ -{ \ +(struct gpiod_lookup) { \ .key = _key, \ .chip_hwnum = _chip_hwnum, \ .con_id = _con_id, \ @@ -87,7 +87,7 @@ struct gpiod_hog { * Simple definition of a single GPIO hog in an array. */ #define GPIO_HOG(_chip_label, _chip_hwnum, _line_name, _lflags, _dflags) \ -{ \ +(struct gpiod_hog) { \ .chip_label = _chip_label, \ .chip_hwnum = _chip_hwnum, \ .line_name = _line_name, \ diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index e4eb0b8c5cf9..eaaea3d8e6b4 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ /* * <linux/gpio.h> - userspace ABI for the GPIO character devices * @@ -212,7 +212,7 @@ struct gpio_v2_line_request { * @offset: the local offset on this GPIO chip, fill this in when * requesting the line information from the kernel * @num_attrs: the number of attributes in @attrs - * @flags: flags for the GPIO lines, with values from &enum + * @flags: flags for this GPIO line, with values from &enum * gpio_v2_line_flag, such as %GPIO_V2_LINE_FLAG_ACTIVE_LOW, * %GPIO_V2_LINE_FLAG_OUTPUT etc, added together. * @attrs: the configuration attributes associated with the line diff --git a/lib/cmdline.c b/lib/cmdline.c index b390dd03363b..dfd4c4423f9a 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c @@ -83,7 +83,7 @@ EXPORT_SYMBOL(get_option); * get_options - Parse a string into a list of integers * @str: String to be parsed * @nints: size of integer array - * @ints: integer array + * @ints: integer array (must have room for at least one element) * * This function parses a string containing a comma-separated * list of integers, a hyphen-separated range of _positive_ integers, @@ -91,6 +91,14 @@ EXPORT_SYMBOL(get_option); * full, or when no more numbers can be retrieved from the * string. * + * When @nints is 0, the function just validates the given @str and + * returns the amount of parseable integers as described below. + * + * Returns: + * + * The first element is filled by the number of collected integers + * in the range. The rest is what was parsed from the @str. + * * Return value is the character in the string which caused * the parse to end (typically a null terminator, if @str is * completely parseable). @@ -98,15 +106,20 @@ EXPORT_SYMBOL(get_option); char *get_options(const char *str, int nints, int *ints) { + bool validate = (nints == 0); int res, i = 1; - while (i < nints) { - res = get_option((char **)&str, ints + i); + while (i < nints || validate) { + int *pint = validate ? ints : ints + i; + + res = get_option((char **)&str, pint); if (res == 0) break; if (res == 3) { + int n = validate ? 0 : nints - i; int range_nums; - range_nums = get_range((char **)&str, ints + i, nints - i); + + range_nums = get_range((char **)&str, pint, n); if (range_nums < 0) break; /* diff --git a/lib/cmdline_kunit.c b/lib/cmdline_kunit.c index 550e7a47fd24..018bfc8113c4 100644 --- a/lib/cmdline_kunit.c +++ b/lib/cmdline_kunit.c @@ -18,6 +18,26 @@ static const int cmdline_test_values[] = { 1, 3, 2, 1, 1, 1, 3, 1, }; +static_assert(ARRAY_SIZE(cmdline_test_strings) == ARRAY_SIZE(cmdline_test_values)); + +static const char *cmdline_test_range_strings[] = { + "-7" , "--7" , "-1-2" , "7--9", + "7-" , "-7--9", "7-9," , "9-7" , + "5-a", "a-5" , "5-8" , ",8-5", + "+,1", "-,4" , "-3,0-1,6", "4,-" , + " +2", " -9" , "0-1,-3,6", "- 9" , +}; + +static const int cmdline_test_range_values[][16] = { + { 1, -7, }, { 0, -0, }, { 4, -1, 0, +1, 2, }, { 0, 7, }, + { 0, +7, }, { 0, -7, }, { 3, +7, 8, +9, 0, }, { 0, 9, }, + { 0, +5, }, { 0, -0, }, { 4, +5, 6, +7, 8, }, { 0, 0, }, + { 0, +0, }, { 0, -0, }, { 4, -3, 0, +1, 6, }, { 1, 4, }, + { 0, +0, }, { 0, -0, }, { 4, +0, 1, -3, 6, }, { 0, 0, }, +}; + +static_assert(ARRAY_SIZE(cmdline_test_range_strings) == ARRAY_SIZE(cmdline_test_range_values)); + static void cmdline_do_one_test(struct kunit *test, const char *in, int rc, int offset) { const char *fmt = "Pattern: %s"; @@ -84,10 +104,46 @@ static void cmdline_test_tail_int(struct kunit *test) } while (++i < ARRAY_SIZE(cmdline_test_strings)); } +static void cmdline_do_one_range_test(struct kunit *test, const char *in, + unsigned int n, const int *e) +{ + unsigned int i; + int r[16]; + int *p; + + memset(r, 0, sizeof(r)); + get_options(in, ARRAY_SIZE(r), r); + KUNIT_EXPECT_EQ_MSG(test, r[0], e[0], "in test %u (parsed) expected %d numbers, got %d", + n, e[0], r[0]); + for (i = 1; i < ARRAY_SIZE(r); i++) + KUNIT_EXPECT_EQ_MSG(test, r[i], e[i], "in test %u at %u", n, i); + + memset(r, 0, sizeof(r)); + get_options(in, 0, r); + KUNIT_EXPECT_EQ_MSG(test, r[0], e[0], "in test %u (validated) expected %d numbers, got %d", + n, e[0], r[0]); + + p = memchr_inv(&r[1], 0, sizeof(r) - sizeof(r[0])); + KUNIT_EXPECT_PTR_EQ_MSG(test, p, (int *)0, "in test %u at %u out of bound", n, p - r); +} + +static void cmdline_test_range(struct kunit *test) +{ + unsigned int i = 0; + + do { + const char *str = cmdline_test_range_strings[i]; + const int *e = cmdline_test_range_values[i]; + + cmdline_do_one_range_test(test, str, i, e); + } while (++i < ARRAY_SIZE(cmdline_test_range_strings)); +} + static struct kunit_case cmdline_test_cases[] = { KUNIT_CASE(cmdline_test_noint), KUNIT_CASE(cmdline_test_lead_int), KUNIT_CASE(cmdline_test_tail_int), + KUNIT_CASE(cmdline_test_range), {} }; diff --git a/tools/gpio/gpio-utils.c b/tools/gpio/gpio-utils.c index 37187e056c8b..1639b4d832cd 100644 --- a/tools/gpio/gpio-utils.c +++ b/tools/gpio/gpio-utils.c @@ -32,74 +32,6 @@ * following api will request gpio lines, do the operation and then * release these lines. */ -/** - * gpiotools_request_linehandle() - request gpio lines in a gpiochip - * @device_name: The name of gpiochip without prefix "/dev/", - * such as "gpiochip0" - * @lines: An array desired lines, specified by offset - * index for the associated GPIO device. - * @num_lines: The number of lines to request. - * @flag: The new flag for requsted gpio. Reference - * "linux/gpio.h" for the meaning of flag. - * @data: Default value will be set to gpio when flag is - * GPIOHANDLE_REQUEST_OUTPUT. - * @consumer_label: The name of consumer, such as "sysfs", - * "powerkey". This is useful for other users to - * know who is using. - * - * Request gpio lines through the ioctl provided by chardev. User - * could call gpiotools_set_values() and gpiotools_get_values() to - * read and write respectively through the returned fd. Call - * gpiotools_release_linehandle() to release these lines after that. - * - * Return: On success return the fd; - * On failure return the errno. - */ -int gpiotools_request_linehandle(const char *device_name, unsigned int *lines, - unsigned int num_lines, unsigned int flag, - struct gpiohandle_data *data, - const char *consumer_label) -{ - struct gpiohandle_request req; - char *chrdev_name; - int fd; - int i; - int ret; - - ret = asprintf(&chrdev_name, "/dev/%s", device_name); - if (ret < 0) - return -ENOMEM; - - fd = open(chrdev_name, 0); - if (fd == -1) { - ret = -errno; - fprintf(stderr, "Failed to open %s, %s\n", - chrdev_name, strerror(errno)); - goto exit_free_name; - } - - for (i = 0; i < num_lines; i++) - req.lineoffsets[i] = lines[i]; - - req.flags = flag; - strcpy(req.consumer_label, consumer_label); - req.lines = num_lines; - if (flag & GPIOHANDLE_REQUEST_OUTPUT) - memcpy(req.default_values, data, sizeof(req.default_values)); - - ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); - if (ret == -1) { - ret = -errno; - fprintf(stderr, "Failed to issue %s (%d), %s\n", - "GPIO_GET_LINEHANDLE_IOCTL", ret, strerror(errno)); - } - - if (close(fd) == -1) - perror("Failed to close GPIO character device file"); -exit_free_name: - free(chrdev_name); - return ret < 0 ? ret : req.fd; -} /** * gpiotools_request_line() - request gpio lines in a gpiochip @@ -216,27 +148,6 @@ int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values) } /** - * gpiotools_release_linehandle(): Release the line(s) of gpiochip - * @fd: The fd returned by - * gpiotools_request_linehandle(). - * - * Return: On success return 0; - * On failure return the errno. - */ -int gpiotools_release_linehandle(const int fd) -{ - int ret; - - ret = close(fd); - if (ret == -1) { - perror("Failed to close GPIO LINEHANDLE device file"); - ret = -errno; - } - - return ret; -} - -/** * gpiotools_release_line(): Release the line(s) of gpiochip * @fd: The fd returned by * gpiotools_request_line(). diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h index 6c69a9f1c253..8af7c8ee19ce 100644 --- a/tools/gpio/gpio-utils.h +++ b/tools/gpio/gpio-utils.h @@ -24,12 +24,6 @@ static inline int check_prefix(const char *str, const char *prefix) strncmp(str, prefix, strlen(prefix)) == 0; } -int gpiotools_request_linehandle(const char *device_name, unsigned int *lines, - unsigned int num_lines, unsigned int flag, - struct gpiohandle_data *data, - const char *consumer_label); -int gpiotools_release_linehandle(const int fd); - int gpiotools_request_line(const char *device_name, unsigned int *lines, unsigned int num_lines, diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index c42aacec5038..41f0a0adbb80 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -126,15 +126,6 @@ ARCH ?= $(SUBARCH) export KSFT_KHDR_INSTALL_DONE := 1 export BUILD -# build and run gpio when output directory is the src dir. -# gpio has dependency on tools/gpio and builds tools/gpio -# objects in the src directory in all cases making the src -# repo dirty even when objects are relocated. -ifneq (1,$(DEFAULT_INSTALL_HDR_PATH)) - TMP := $(filter-out gpio, $(TARGETS)) - TARGETS := $(TMP) -endif - # set default goal to all, so make without a target runs all, even when # all isn't the first target in the file. .DEFAULT_GOAL := all diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index 41582fe485ee..39f2bbe8dd3d 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -1,31 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -VAR_CFLAGS := $(shell pkg-config --cflags mount 2>/dev/null) -VAR_LDLIBS := $(shell pkg-config --libs mount 2>/dev/null) -ifeq ($(VAR_LDLIBS),) -VAR_LDLIBS := -lmount -I/usr/include/libmount -endif - -CFLAGS += -O2 -g -std=gnu99 -Wall -I../../../../usr/include/ $(VAR_CFLAGS) -LDLIBS += $(VAR_LDLIBS) - TEST_PROGS := gpio-mockup.sh TEST_FILES := gpio-mockup-sysfs.sh -TEST_GEN_PROGS_EXTENDED := gpio-mockup-chardev +TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev -KSFT_KHDR_INSTALL := 1 include ../lib.mk - -GPIODIR := $(realpath ../../../gpio) -GPIOOUT := $(OUTPUT)/tools-gpio/ -GPIOOBJ := $(GPIOOUT)/gpio-utils.o - -CLEAN += ; $(RM) -rf $(GPIOOUT) - -$(TEST_GEN_PROGS_EXTENDED): $(GPIOOBJ) - -$(GPIOOUT): - mkdir -p $@ - -$(GPIOOBJ): $(GPIOOUT) - $(MAKE) OUTPUT=$(GPIOOUT) -C $(GPIODIR) diff --git a/tools/testing/selftests/gpio/config b/tools/testing/selftests/gpio/config index abaa6902b7b6..ce100342c20b 100644 --- a/tools/testing/selftests/gpio/config +++ b/tools/testing/selftests/gpio/config @@ -1,2 +1,3 @@ CONFIG_GPIOLIB=y +CONFIG_GPIO_CDEV=y CONFIG_GPIO_MOCKUP=m diff --git a/tools/testing/selftests/gpio/gpio-mockup-cdev.c b/tools/testing/selftests/gpio/gpio-mockup-cdev.c new file mode 100644 index 000000000000..e83eac71621a --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-mockup-cdev.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GPIO mockup cdev test helper + * + * Copyright (C) 2020 Kent Gibson + */ + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <linux/gpio.h> + +#define CONSUMER "gpio-mockup-cdev" + +static int request_line_v2(int cfd, unsigned int offset, + uint64_t flags, unsigned int val) +{ + struct gpio_v2_line_request req; + int ret; + + memset(&req, 0, sizeof(req)); + req.num_lines = 1; + req.offsets[0] = offset; + req.config.flags = flags; + strcpy(req.consumer, CONSUMER); + if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { + req.config.num_attrs = 1; + req.config.attrs[0].mask = 1; + req.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES; + if (val) + req.config.attrs[0].attr.values = 1; + } + ret = ioctl(cfd, GPIO_V2_GET_LINE_IOCTL, &req); + if (ret == -1) + return -errno; + return req.fd; +} + + +static int get_value_v2(int lfd) +{ + struct gpio_v2_line_values vals; + int ret; + + memset(&vals, 0, sizeof(vals)); + vals.mask = 1; + ret = ioctl(lfd, GPIO_V2_LINE_GET_VALUES_IOCTL, &vals); + if (ret == -1) + return -errno; + return vals.bits & 0x1; +} + +static int request_line_v1(int cfd, unsigned int offset, + uint32_t flags, unsigned int val) +{ + struct gpiohandle_request req; + int ret; + + memset(&req, 0, sizeof(req)); + req.lines = 1; + req.lineoffsets[0] = offset; + req.flags = flags; + strcpy(req.consumer_label, CONSUMER); + if (flags & GPIOHANDLE_REQUEST_OUTPUT) + req.default_values[0] = val; + + ret = ioctl(cfd, GPIO_GET_LINEHANDLE_IOCTL, &req); + if (ret == -1) + return -errno; + return req.fd; +} + +static int get_value_v1(int lfd) +{ + struct gpiohandle_data vals; + int ret; + + memset(&vals, 0, sizeof(vals)); + ret = ioctl(lfd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &vals); + if (ret == -1) + return -errno; + return vals.values[0]; +} + +static void usage(char *prog) +{ + printf("Usage: %s [-l] [-b <bias>] [-s <value>] [-u <uAPI>] <gpiochip> <offset>\n", prog); + printf(" -b: set line bias to one of pull-down, pull-up, disabled\n"); + printf(" (default is to leave bias unchanged):\n"); + printf(" -l: set line active low (default is active high)\n"); + printf(" -s: set line value (default is to get line value)\n"); + printf(" -u: uAPI version to use (default is 2)\n"); + exit(-1); +} + +static int wait_signal(void) +{ + int sig; + sigset_t wset; + + sigemptyset(&wset); + sigaddset(&wset, SIGHUP); + sigaddset(&wset, SIGINT); + sigaddset(&wset, SIGTERM); + sigwait(&wset, &sig); + + return sig; +} + +int main(int argc, char *argv[]) +{ + char *chip; + int opt, ret, cfd, lfd; + unsigned int offset, val, abiv; + uint32_t flags_v1; + uint64_t flags_v2; + + abiv = 2; + ret = 0; + flags_v1 = GPIOHANDLE_REQUEST_INPUT; + flags_v2 = GPIO_V2_LINE_FLAG_INPUT; + + while ((opt = getopt(argc, argv, "lb:s:u:")) != -1) { + switch (opt) { + case 'l': + flags_v1 |= GPIOHANDLE_REQUEST_ACTIVE_LOW; + flags_v2 |= GPIO_V2_LINE_FLAG_ACTIVE_LOW; + break; + case 'b': + if (strcmp("pull-up", optarg) == 0) { + flags_v1 |= GPIOHANDLE_REQUEST_BIAS_PULL_UP; + flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP; + } else if (strcmp("pull-down", optarg) == 0) { + flags_v1 |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; + flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN; + } else if (strcmp("disabled", optarg) == 0) { + flags_v1 |= GPIOHANDLE_REQUEST_BIAS_DISABLE; + flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_DISABLED; + } + break; + case 's': + val = atoi(optarg); + flags_v1 &= ~GPIOHANDLE_REQUEST_INPUT; + flags_v1 |= GPIOHANDLE_REQUEST_OUTPUT; + flags_v2 &= ~GPIO_V2_LINE_FLAG_INPUT; + flags_v2 |= GPIO_V2_LINE_FLAG_OUTPUT; + break; + case 'u': + abiv = atoi(optarg); + break; + default: + usage(argv[0]); + } + } + + if (argc < optind + 2) + usage(argv[0]); + + chip = argv[optind]; + offset = atoi(argv[optind + 1]); + + cfd = open(chip, 0); + if (cfd == -1) { + fprintf(stderr, "Failed to open %s: %s\n", chip, strerror(errno)); + return -errno; + } + + if (abiv == 1) + lfd = request_line_v1(cfd, offset, flags_v1, val); + else + lfd = request_line_v2(cfd, offset, flags_v2, val); + + close(cfd); + + if (lfd < 0) { + fprintf(stderr, "Failed to request %s:%d: %s\n", chip, offset, strerror(-lfd)); + return lfd; + } + + if (flags_v2 & GPIO_V2_LINE_FLAG_OUTPUT) { + wait_signal(); + } else { + if (abiv == 1) + ret = get_value_v1(lfd); + else + ret = get_value_v2(lfd); + } + + close(lfd); + + return ret; +} diff --git a/tools/testing/selftests/gpio/gpio-mockup-chardev.c b/tools/testing/selftests/gpio/gpio-mockup-chardev.c deleted file mode 100644 index 73ead8828d3a..000000000000 --- a/tools/testing/selftests/gpio/gpio-mockup-chardev.c +++ /dev/null @@ -1,323 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * GPIO chardev test helper - * - * Copyright (C) 2016 Bamvor Jian Zhang - */ - -#define _GNU_SOURCE -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <getopt.h> -#include <sys/ioctl.h> -#include <libmount.h> -#include <err.h> -#include <dirent.h> -#include <linux/gpio.h> -#include "../../../gpio/gpio-utils.h" - -#define CONSUMER "gpio-selftest" -#define GC_NUM 10 -enum direction { - OUT, - IN -}; - -static int get_debugfs(char **path) -{ - struct libmnt_context *cxt; - struct libmnt_table *tb; - struct libmnt_iter *itr = NULL; - struct libmnt_fs *fs; - int found = 0, ret; - - cxt = mnt_new_context(); - if (!cxt) - err(EXIT_FAILURE, "libmount context allocation failed"); - - itr = mnt_new_iter(MNT_ITER_FORWARD); - if (!itr) - err(EXIT_FAILURE, "failed to initialize libmount iterator"); - - if (mnt_context_get_mtab(cxt, &tb)) - err(EXIT_FAILURE, "failed to read mtab"); - - while (mnt_table_next_fs(tb, itr, &fs) == 0) { - const char *type = mnt_fs_get_fstype(fs); - - if (!strcmp(type, "debugfs")) { - found = 1; - break; - } - } - if (found) { - ret = asprintf(path, "%s/gpio", mnt_fs_get_target(fs)); - if (ret < 0) - err(EXIT_FAILURE, "failed to format string"); - } - - mnt_free_iter(itr); - mnt_free_context(cxt); - - if (!found) - return -1; - - return 0; -} - -static int gpio_debugfs_get(const char *consumer, int *dir, int *value) -{ - char *debugfs; - FILE *f; - char *line = NULL; - size_t len = 0; - char *cur; - int found = 0; - - if (get_debugfs(&debugfs) != 0) - err(EXIT_FAILURE, "debugfs is not mounted"); - - f = fopen(debugfs, "r"); - if (!f) - err(EXIT_FAILURE, "read from gpio debugfs failed"); - - /* - * gpio-2 ( |gpio-selftest ) in lo - */ - while (getline(&line, &len, f) != -1) { - cur = strstr(line, consumer); - if (cur == NULL) - continue; - - cur = strchr(line, ')'); - if (!cur) - continue; - - cur += 2; - if (!strncmp(cur, "out", 3)) { - *dir = OUT; - cur += 4; - } else if (!strncmp(cur, "in", 2)) { - *dir = IN; - cur += 4; - } - - if (!strncmp(cur, "hi", 2)) - *value = 1; - else if (!strncmp(cur, "lo", 2)) - *value = 0; - - found = 1; - break; - } - free(debugfs); - fclose(f); - free(line); - - if (!found) - return -1; - - return 0; -} - -static struct gpiochip_info *list_gpiochip(const char *gpiochip_name, int *ret) -{ - struct gpiochip_info *cinfo; - struct gpiochip_info *current; - const struct dirent *ent; - DIR *dp; - char *chrdev_name; - int fd; - int i = 0; - - cinfo = calloc(sizeof(struct gpiochip_info) * 4, GC_NUM + 1); - if (!cinfo) - err(EXIT_FAILURE, "gpiochip_info allocation failed"); - - current = cinfo; - dp = opendir("/dev"); - if (!dp) { - *ret = -errno; - goto error_out; - } else { - *ret = 0; - } - - while (ent = readdir(dp), ent) { - if (check_prefix(ent->d_name, "gpiochip")) { - *ret = asprintf(&chrdev_name, "/dev/%s", ent->d_name); - if (*ret < 0) - goto error_out; - - fd = open(chrdev_name, 0); - if (fd == -1) { - *ret = -errno; - fprintf(stderr, "Failed to open %s\n", - chrdev_name); - goto error_close_dir; - } - *ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, current); - if (*ret == -1) { - perror("Failed to issue CHIPINFO IOCTL\n"); - goto error_close_dir; - } - close(fd); - if (strcmp(current->label, gpiochip_name) == 0 - || check_prefix(current->label, gpiochip_name)) { - *ret = 0; - current++; - i++; - } - } - } - - if ((!*ret && i == 0) || *ret < 0) { - free(cinfo); - cinfo = NULL; - } - if (!*ret && i > 0) { - cinfo = realloc(cinfo, sizeof(struct gpiochip_info) * 4 * i); - *ret = i; - } - -error_close_dir: - closedir(dp); -error_out: - if (*ret < 0) - err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret)); - - return cinfo; -} - -int gpio_pin_test(struct gpiochip_info *cinfo, int line, int flag, int value) -{ - struct gpiohandle_data data; - unsigned int lines[] = {line}; - int fd; - int debugfs_dir = IN; - int debugfs_value = 0; - int ret; - - data.values[0] = value; - ret = gpiotools_request_linehandle(cinfo->name, lines, 1, flag, &data, - CONSUMER); - if (ret < 0) - goto fail_out; - else - fd = ret; - - ret = gpio_debugfs_get(CONSUMER, &debugfs_dir, &debugfs_value); - if (ret) { - ret = -EINVAL; - goto fail_out; - } - if (flag & GPIOHANDLE_REQUEST_INPUT) { - if (debugfs_dir != IN) { - errno = -EINVAL; - ret = -errno; - } - } else if (flag & GPIOHANDLE_REQUEST_OUTPUT) { - if (flag & GPIOHANDLE_REQUEST_ACTIVE_LOW) - debugfs_value = !debugfs_value; - - if (!(debugfs_dir == OUT && value == debugfs_value)) { - errno = -EINVAL; - ret = -errno; - } - } - gpiotools_release_linehandle(fd); - -fail_out: - if (ret) - err(EXIT_FAILURE, "gpio<%s> line<%d> test flag<0x%x> value<%d>", - cinfo->name, line, flag, value); - - return ret; -} - -void gpio_pin_tests(struct gpiochip_info *cinfo, unsigned int line) -{ - printf("line<%d>", line); - gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 0); - printf("."); - gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 1); - printf("."); - gpio_pin_test(cinfo, line, - GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW, - 0); - printf("."); - gpio_pin_test(cinfo, line, - GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW, - 1); - printf("."); - gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_INPUT, 0); - printf("."); -} - -/* - * ./gpio-mockup-chardev gpio_chip_name_prefix is_valid_gpio_chip - * Return 0 if successful or exit with EXIT_FAILURE if test failed. - * gpio_chip_name_prefix: The prefix of gpiochip you want to test. E.g. - * gpio-mockup - * is_valid_gpio_chip: Whether the gpio_chip is valid. 1 means valid, - * 0 means invalid which could not be found by - * list_gpiochip. - */ -int main(int argc, char *argv[]) -{ - char *prefix; - int valid; - struct gpiochip_info *cinfo; - struct gpiochip_info *current; - int i; - int ret; - - if (argc < 3) { - printf("Usage: %s prefix is_valid", argv[0]); - exit(EXIT_FAILURE); - } - - prefix = argv[1]; - valid = strcmp(argv[2], "true") == 0 ? 1 : 0; - - printf("Test gpiochip %s: ", prefix); - cinfo = list_gpiochip(prefix, &ret); - if (!cinfo) { - if (!valid && ret == 0) { - printf("Invalid test successful\n"); - ret = 0; - goto out; - } else { - ret = -EINVAL; - goto out; - } - } else if (cinfo && !valid) { - ret = -EINVAL; - goto out; - } - current = cinfo; - for (i = 0; i < ret; i++) { - gpio_pin_tests(current, 0); - gpio_pin_tests(current, current->lines - 1); - gpio_pin_tests(current, random() % current->lines); - current++; - } - ret = 0; - printf("successful\n"); - -out: - if (ret) - fprintf(stderr, "gpio<%s> test failed\n", prefix); - - if (cinfo) - free(cinfo); - - if (ret) - exit(EXIT_FAILURE); - - return ret; -} diff --git a/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh b/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh index dd269d877562..2d2e5d8763b6 100755 --- a/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh +++ b/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh @@ -1,135 +1,77 @@ # SPDX-License-Identifier: GPL-2.0 -is_consistent() -{ - val= - - active_low_sysfs=`cat $GPIO_SYSFS/gpio$nr/active_low` - val_sysfs=`cat $GPIO_SYSFS/gpio$nr/value` - dir_sysfs=`cat $GPIO_SYSFS/gpio$nr/direction` - gpio_this_debugfs=`cat $GPIO_DEBUGFS |grep "gpio-$nr" | sed "s/(.*)//g"` - dir_debugfs=`echo $gpio_this_debugfs | awk '{print $2}'` - val_debugfs=`echo $gpio_this_debugfs | awk '{print $3}'` - if [ $val_debugfs = "lo" ]; then - val=0 - elif [ $val_debugfs = "hi" ]; then - val=1 - fi +# Overrides functions in gpio-mockup.sh to test using the GPIO SYSFS uAPI - if [ $active_low_sysfs = "1" ]; then - if [ $val = "0" ]; then - val="1" - else - val="0" - fi - fi +SYSFS=`grep -w sysfs /proc/mounts | cut -f2 -d' '` +[ -d "$SYSFS" ] || skip "sysfs is not mounted" - if [ $val_sysfs = $val ] && [ $dir_sysfs = $dir_debugfs ]; then - echo -n "." - else - echo "test fail, exit" - die - fi -} +GPIO_SYSFS="${SYSFS}/class/gpio" +[ -d "$GPIO_SYSFS" ] || skip "CONFIG_GPIO_SYSFS is not selected" -test_pin_logic() -{ - nr=$1 - direction=$2 - active_low=$3 - value=$4 +PLATFORM_SYSFS=$SYSFS/devices/platform - echo $direction > $GPIO_SYSFS/gpio$nr/direction - echo $active_low > $GPIO_SYSFS/gpio$nr/active_low - if [ $direction = "out" ]; then - echo $value > $GPIO_SYSFS/gpio$nr/value - fi - is_consistent $nr -} +sysfs_nr= +sysfs_ldir= -test_one_pin() +# determine the sysfs GPIO number given the $chip and $offset +# e.g. gpiochip1:32 +find_sysfs_nr() { - nr=$1 - - echo -n "test pin<$nr>" - - echo $nr > $GPIO_SYSFS/export 2>/dev/null - - if [ X$? != X0 ]; then - echo "test GPIO pin $nr failed" - die - fi - - #"Checking if the sysfs is consistent with debugfs: " - is_consistent $nr - - #"Checking the logic of active_low: " - test_pin_logic $nr out 1 1 - test_pin_logic $nr out 1 0 - test_pin_logic $nr out 0 1 - test_pin_logic $nr out 0 0 - - #"Checking the logic of direction: " - test_pin_logic $nr in 1 1 - test_pin_logic $nr out 1 0 - test_pin_logic $nr low 0 1 - test_pin_logic $nr high 0 0 - - echo $nr > $GPIO_SYSFS/unexport - - echo "successful" + # e.g. /sys/devices/platform/gpio-mockup.1/gpiochip1 + local platform=$(find $PLATFORM_SYSFS -mindepth 2 -maxdepth 2 -type d -name $chip) + [ "$platform" ] || fail "can't find platform of $chip" + # e.g. /sys/devices/platform/gpio-mockup.1/gpio/gpiochip508/base + local base=$(find ${platform%/*}/gpio/ -mindepth 2 -maxdepth 2 -type f -name base) + [ "$base" ] || fail "can't find base of $chip" + sysfs_nr=$(($(< "$base") + $offset)) + sysfs_ldir="$GPIO_SYSFS/gpio$sysfs_nr" } -test_one_pin_fail() +acquire_line() { - nr=$1 - - echo $nr > $GPIO_SYSFS/export 2>/dev/null - - if [ X$? != X0 ]; then - echo "test invalid pin $nr successful" - else - echo "test invalid pin $nr failed" - echo $nr > $GPIO_SYSFS/unexport 2>/dev/null - die - fi + [ "$sysfs_nr" ] && return + find_sysfs_nr + echo "$sysfs_nr" > "$GPIO_SYSFS/export" } -list_chip() +# The helpers being overridden... +get_line() { - echo `ls -d $GPIO_DRV_SYSFS/gpiochip* 2>/dev/null` + [ -e "$sysfs_ldir/value" ] && echo $(< "$sysfs_ldir/value") } -test_chip() +set_line() { - chip=$1 - name=`basename $chip` - base=`cat $chip/base` - ngpio=`cat $chip/ngpio` - printf "%-10s %-5s %-5s\n" $name $base $ngpio - if [ $ngpio = "0" ]; then - echo "number of gpio is zero is not allowed". - fi - test_one_pin $base - test_one_pin $(($base + $ngpio - 1)) - test_one_pin $((( RANDOM % $ngpio ) + $base )) + acquire_line + + for option in $*; do + case $option in + active-high) + echo 0 > "$sysfs_ldir/active_low" + ;; + active-low) + echo 1 > "$sysfs_ldir/active_low" + ;; + input) + echo "in" > "$sysfs_ldir/direction" + ;; + 0) + echo "out" > "$sysfs_ldir/direction" + echo 0 > "$sysfs_ldir/value" + ;; + 1) + echo "out" > "$sysfs_ldir/direction" + echo 1 > "$sysfs_ldir/value" + ;; + esac + done } -test_chips_sysfs() +release_line() { - gpiochip=`list_chip $module` - if [ X"$gpiochip" = X ]; then - if [ X"$valid" = Xfalse ]; then - echo "successful" - else - echo "fail" - die - fi - else - for chip in $gpiochip; do - test_chip $chip - done - fi + [ "$sysfs_nr" ] || return 0 + echo "$sysfs_nr" > "$GPIO_SYSFS/unexport" + sysfs_nr= + sysfs_ldir= } - diff --git a/tools/testing/selftests/gpio/gpio-mockup.sh b/tools/testing/selftests/gpio/gpio-mockup.sh index 7f35b9880485..0d6c5f7f95d2 100755 --- a/tools/testing/selftests/gpio/gpio-mockup.sh +++ b/tools/testing/selftests/gpio/gpio-mockup.sh @@ -1,72 +1,57 @@ -#!/bin/bash +#!/bin/bash -efu # SPDX-License-Identifier: GPL-2.0 #exit status -#1: Internal error -#2: sysfs/debugfs not mount -#3: insert module fail when gpio-mockup is a module. -#4: Skip test including run as non-root user. -#5: other reason. - -SYSFS= -GPIO_SYSFS= -GPIO_DRV_SYSFS= +#0: success +#1: fail +#4: skip test - including run as non-root user + +BASE=${0%/*} DEBUGFS= GPIO_DEBUGFS= -dev_type= -module= +dev_type="cdev" +module="gpio-mockup" +verbose= +full_test= +random= +uapi_opt= +active_opt= +bias_opt= +line_set_pid= -# Kselftest framework requirement - SKIP code is 4. +# Kselftest return codes +ksft_fail=1 ksft_skip=4 usage() { echo "Usage:" - echo "$0 [-f] [-m name] [-t type]" - echo "-f: full test. It maybe conflict with existence gpio device." - echo "-m: module name, default name is gpio-mockup. It could also test" - echo " other gpio device." - echo "-t: interface type: chardev(char device) and sysfs(being" - echo " deprecated). The first one is default" - echo "" - echo "$0 -h" - echo "This usage" + echo "$0 [-frv] [-t type]" + echo "-f: full test (minimal set run by default)" + echo "-r: test random lines as well as fence posts" + echo "-t: interface type:" + echo " cdev (character device ABI) - default" + echo " cdev_v1 (deprecated character device ABI)" + echo " sysfs (deprecated SYSFS ABI)" + echo "-v: verbose progress reporting" + exit $ksft_fail } -prerequisite() +skip() { - msg="skip all tests:" - if [ $UID != 0 ]; then - echo $msg must be run as root >&2 - exit $ksft_skip - fi - SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'` - if [ ! -d "$SYSFS" ]; then - echo $msg sysfs is not mounted >&2 - exit 2 - fi - GPIO_SYSFS=`echo $SYSFS/class/gpio` - GPIO_DRV_SYSFS=`echo $SYSFS/devices/platform/$module/gpio` - DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'` - if [ ! -d "$DEBUGFS" ]; then - echo $msg debugfs is not mounted >&2 - exit 2 - fi - GPIO_DEBUGFS=`echo $DEBUGFS/gpio` - source gpio-mockup-sysfs.sh + echo "$*" >&2 + echo "GPIO $module test SKIP" + exit $ksft_skip } -try_insert_module() +prerequisite() { - if [ -d "$GPIO_DRV_SYSFS" ]; then - echo "$GPIO_DRV_SYSFS exist. Skip insert module" - else - modprobe -q $module $1 - if [ X$? != X0 ]; then - echo $msg insmod $module failed >&2 - exit 3 - fi - fi + [ $(id -u) -eq 0 ] || skip "must be run as root" + + DEBUGFS=$(grep -w debugfs /proc/mounts | cut -f2 -d' ') + [ -d "$DEBUGFS" ] || skip "debugfs is not mounted" + + GPIO_DEBUGFS=$DEBUGFS/$module } remove_module() @@ -74,133 +59,345 @@ remove_module() modprobe -r -q $module } -die() +cleanup() { + set +e + release_line remove_module - exit 5 + jobs -p | xargs -r kill > /dev/null 2>&1 } -test_chips() +fail() { - if [ X$dev_type = Xsysfs ]; then - echo "WARNING: sysfs ABI of gpio is going to deprecated." - test_chips_sysfs $* - else - $BASE/gpio-mockup-chardev $* - fi + echo "test failed: $*" >&2 + echo "GPIO $module test FAIL" + exit $ksft_fail +} + +try_insert_module() +{ + modprobe -q $module "$1" || fail "insert $module failed with error $?" +} + +log() +{ + [ -z "$verbose" ] || echo "$*" } -gpio_test() +# The following line helpers, release_Line, get_line and set_line, all +# make use of the global $chip and $offset variables. +# +# This implementation drives the GPIO character device (cdev) uAPI. +# Other implementations may override these to test different uAPIs. + +# Release any resources related to the line +release_line() { - param=$1 - valid=$2 + [ "$line_set_pid" ] && kill $line_set_pid && wait $line_set_pid || true + line_set_pid= +} - if [ X"$param" = X ]; then - die +# Read the current value of the line +get_line() +{ + release_line + + local cdev_opts=${uapi_opt}${active_opt} + $BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset + echo $? +} + +# Set the state of the line +# +# Changes to line configuration are provided as parameters. +# The line is assumed to be an output if the line value 0 or 1 is +# specified, else an input. +set_line() +{ + local val= + + release_line + + # parse config options... + for option in $*; do + case $option in + active-low) + active_opt="-l " + ;; + active-high) + active_opt= + ;; + bias-none) + bias_opt= + ;; + pull-down) + bias_opt="-bpull-down " + ;; + pull-up) + bias_opt="-bpull-up " + ;; + 0) + val=0 + ;; + 1) + val=1 + ;; + esac + done + + local cdev_opts=${uapi_opt}${active_opt} + if [ "$val" ]; then + $BASE/gpio-mockup-cdev $cdev_opts -s$val /dev/$chip $offset & + # failure to set is detected by reading mockup and toggling values + line_set_pid=$! + # allow for gpio-mockup-cdev to launch and request line + # (there is limited value in checking if line has been requested) + sleep 0.01 + elif [ "$bias_opt" ]; then + cdev_opts=${cdev_opts}${bias_opt} + $BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset || true fi - try_insert_module "gpio_mockup_ranges=$param" - echo -n "GPIO $module test with ranges: <" - echo "$param>: " - printf "%-10s %s\n" $param - test_chips $module $valid - remove_module } -BASE=`dirname $0` +assert_line() +{ + local val + # don't need any retry here as set_mock allows for propagation + val=$(get_line) + [ "$val" = "$1" ] || fail "line value is ${val:-empty} when $1 was expected" +} + +# The following mockup helpers all make use of the $mock_line +assert_mock() +{ + local backoff_wait=10 + local retry=0 + local val + # retry allows for set propagation from uAPI to mockup + while true; do + val=$(< $mock_line) + [ "$val" = "$1" ] && break + retry=$((retry + 1)) + [ $retry -lt 5 ] || fail "mockup $mock_line value ${val:-empty} when $1 expected" + sleep $(printf "%0.2f" $((backoff_wait))e-3) + backoff_wait=$((backoff_wait * 2)) + done +} + +set_mock() +{ + echo "$1" > $mock_line + # allow for set propagation - so we won't be in a race with set_line + assert_mock "$1" +} -dev_type= -TEMP=`getopt -o fhm:t: -n '$0' -- "$@"` +# test the functionality of a line +# +# The line is set from the mockup side and is read from the userspace side +# (input), and is set from the userspace side and is read from the mockup side +# (output). +# +# Setting the mockup pull using the userspace interface bias settings is +# tested where supported by the userspace interface (cdev). +test_line() +{ + chip=$1 + offset=$2 + log "test_line $chip $offset" + mock_line=$GPIO_DEBUGFS/$chip/$offset + [ -e "$mock_line" ] || fail "missing line $chip:$offset" -if [ "$?" != "0" ]; then - echo "Parameter process failed, Terminating..." >&2 - exit 1 -fi + # test input active-high + set_mock 1 + set_line input active-high + assert_line 1 + set_mock 0 + assert_line 0 + set_mock 1 + assert_line 1 + + if [ "$full_test" ]; then + if [ "$dev_type" != "sysfs" ]; then + # test pulls + set_mock 0 + set_line input pull-up + assert_line 1 + set_mock 0 + assert_line 0 + + set_mock 1 + set_line input pull-down + assert_line 0 + set_mock 1 + assert_line 1 + + set_line bias-none + fi + + # test input active-low + set_mock 0 + set_line active-low + assert_line 1 + set_mock 1 + assert_line 0 + set_mock 0 + assert_line 1 + + # test output active-high + set_mock 1 + set_line active-high 0 + assert_mock 0 + set_line 1 + assert_mock 1 + set_line 0 + assert_mock 0 + fi + + # test output active-low + set_mock 0 + set_line active-low 0 + assert_mock 1 + set_line 1 + assert_mock 0 + set_line 0 + assert_mock 1 + + release_line +} + +test_no_line() +{ + log test_no_line "$*" + [ ! -e "$GPIO_DEBUGFS/$1/$2" ] || fail "unexpected line $1:$2" +} -# Note the quotes around `$TEMP': they are essential! -eval set -- "$TEMP" +# Load the module and check that the expected number of gpiochips, with the +# expected number of lines, are created and are functional. +# +# $1 is the gpio_mockup_ranges parameter for the module +# The remaining parameters are the number of lines, n, expected for each of +# the gpiochips expected to be created. +# +# For each gpiochip the fence post lines, 0 and n-1, are tested, and the +# line on the far side of the fence post, n, is tested to not exist. +# +# If the $random flag is set then a random line in the middle of the +# gpiochip is tested as well. +insmod_test() +{ + local ranges= + local gc= + local width= -while true; do - case $1 in - -f) + [ "${1:-}" ] || fail "missing ranges" + ranges=$1 ; shift + try_insert_module "gpio_mockup_ranges=$ranges" + log "GPIO $module test with ranges: <$ranges>:" + # e.g. /sys/kernel/debug/gpio-mockup/gpiochip1 + gpiochip=$(find "$DEBUGFS/$module/" -name gpiochip* -type d | sort) + for chip in $gpiochip; do + gc=${chip##*/} + [ "${1:-}" ] || fail "unexpected chip - $gc" + width=$1 ; shift + test_line $gc 0 + if [ "$random" -a $width -gt 2 ]; then + test_line $gc $((RANDOM % ($width - 2) + 1)) + fi + test_line $gc $(($width - 1)) + test_no_line $gc $width + done + [ "${1:-}" ] && fail "missing expected chip of width $1" + remove_module || fail "failed to remove module with error $?" +} + +while getopts ":frvt:" opt; do + case $opt in + f) full_test=true - shift - ;; - -h) - usage - exit ;; - -m) - module=$2 - shift 2 + r) + random=true ;; - -t) - dev_type=$2 - shift 2 + t) + dev_type=$OPTARG ;; - --) - shift - break + v) + verbose=true ;; *) - echo "Internal error!" - exit 1 + usage ;; esac done +shift $((OPTIND - 1)) -if [ X"$module" = X ]; then - module="gpio-mockup" -fi - -if [ X$dev_type != Xsysfs ]; then - dev_type="chardev" -fi +[ "${1:-}" ] && fail "unknown argument '$1'" prerequisite -echo "1. Test dynamic allocation of gpio successful means insert gpiochip and" -echo " manipulate gpio pin successful" -gpio_test "-1,32" true -gpio_test "-1,32,-1,32" true -gpio_test "-1,32,-1,32,-1,32" true -if [ X$full_test = Xtrue ]; then - gpio_test "-1,32,32,64" true - gpio_test "-1,32,40,64,-1,5" true - gpio_test "-1,32,32,64,-1,32" true - gpio_test "0,32,32,64,-1,32,-1,32" true - gpio_test "-1,32,-1,32,0,32,32,64" true - echo "2. Do basic test: successful means insert gpiochip and" - echo " manipulate gpio pin successful" - gpio_test "0,32" true - gpio_test "0,32,32,64" true - gpio_test "0,32,40,64,64,96" true +trap 'exit $ksft_fail' SIGTERM SIGINT +trap cleanup EXIT + +case "$dev_type" in +sysfs) + source $BASE/gpio-mockup-sysfs.sh + echo "WARNING: gpio sysfs ABI is deprecated." + ;; +cdev_v1) + echo "WARNING: gpio cdev ABI v1 is deprecated." + uapi_opt="-u1 " + ;; +cdev) + ;; +*) + fail "unknown interface type: $dev_type" + ;; +esac + +remove_module || fail "can't remove existing $module module" + +# manual gpio allocation tests fail if a physical chip already exists +[ "$full_test" -a -e "/dev/gpiochip0" ] && skip "full tests conflict with gpiochip0" + +echo "1. Module load tests" +echo "1.1. dynamic allocation of gpio" +insmod_test "-1,32" 32 +insmod_test "-1,23,-1,32" 23 32 +insmod_test "-1,23,-1,26,-1,32" 23 26 32 +if [ "$full_test" ]; then + echo "1.2. manual allocation of gpio" + insmod_test "0,32" 32 + insmod_test "0,32,32,60" 32 28 + insmod_test "0,32,40,64,64,96" 32 24 32 + echo "1.3. dynamic and manual allocation of gpio" + insmod_test "-1,32,32,62" 32 30 + insmod_test "-1,22,-1,23,0,24,32,64" 22 23 24 32 + insmod_test "-1,32,32,60,-1,29" 32 28 29 + insmod_test "-1,32,40,64,-1,5" 32 24 5 + insmod_test "0,32,32,44,-1,22,-1,31" 32 12 22 31 fi -echo "3. Error test: successful means insert gpiochip failed" -echo "3.1 Test number of gpio overflow" -#Currently: The max number of gpio(1024) is defined in arm architecture. -gpio_test "-1,32,-1,1024" false -if [ X$full_test = Xtrue ]; then - echo "3.2 Test zero line of gpio" - gpio_test "0,0" false - echo "3.3 Test range overlap" - echo "3.3.1 Test corner case" - gpio_test "0,32,0,1" false - gpio_test "0,32,32,64,32,40" false - gpio_test "0,32,35,64,35,45" false - gpio_test "0,32,31,32" false - gpio_test "0,32,32,64,36,37" false - gpio_test "0,32,35,64,34,36" false - echo "3.3.2 Test inserting invalid second gpiochip" - gpio_test "0,32,30,35" false - gpio_test "0,32,1,5" false - gpio_test "10,32,9,14" false - gpio_test "10,32,30,35" false - echo "3.3.3 Test others" - gpio_test "0,32,40,56,39,45" false - gpio_test "0,32,40,56,30,33" false - gpio_test "0,32,40,56,30,41" false - gpio_test "0,32,40,56,20,21" false +echo "2. Module load error tests" +echo "2.1 gpio overflow" +# Currently: The max number of gpio(1024) is defined in arm architecture. +insmod_test "-1,1024" +if [ "$full_test" ]; then + echo "2.2 no lines defined" + insmod_test "0,0" + echo "2.3 ignore range overlap" + insmod_test "0,32,0,1" 32 + insmod_test "0,32,1,5" 32 + insmod_test "0,32,30,35" 32 + insmod_test "0,32,31,32" 32 + insmod_test "10,32,30,35" 22 + insmod_test "10,32,9,14" 22 + insmod_test "0,32,20,21,40,56" 32 16 + insmod_test "0,32,32,64,32,40" 32 32 + insmod_test "0,32,32,64,36,37" 32 32 + insmod_test "0,32,35,64,34,36" 32 29 + insmod_test "0,30,35,64,35,45" 30 29 + insmod_test "0,32,40,56,30,33" 32 16 + insmod_test "0,32,40,56,30,41" 32 16 + insmod_test "0,32,40,56,39,45" 32 16 fi -echo GPIO test PASS - +echo "GPIO $module test PASS" diff --git a/tools/testing/selftests/kselftest_deps.sh b/tools/testing/selftests/kselftest_deps.sh index bbc04646346b..00e60d6eb16b 100755 --- a/tools/testing/selftests/kselftest_deps.sh +++ b/tools/testing/selftests/kselftest_deps.sh @@ -129,13 +129,11 @@ l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \ grep -v "VAR_LDLIBS" | awk -F: '{print $1}') # Level 3 -# gpio, memfd and others use pkg-config to find mount and fuse libs +# memfd and others use pkg-config to find mount and fuse libs # respectively and save it in VAR_LDLIBS. If pkg-config doesn't find # any, VAR_LDLIBS set to default. # Use the default value and filter out pkg-config for dependency check. # e.g: -# gpio/Makefile -# VAR_LDLIBS := $(shell pkg-config --libs mount) 2>/dev/null) # memfd/Makefile # VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null) |