diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-22 12:47:37 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-22 12:47:37 +0100 |
| commit | eecbee709cc4cb2edb6ca5e04cec7b80097d10a3 (patch) | |
| tree | 1b7961ab59487696f3eccb7c0551f9c8e5e9bfcd | |
| parent | 381a433896a1c9af12dd0a8f7976e31f779cae6c (diff) | |
| parent | b57f502a681296be357e5c9dc72b53b083b26472 (diff) | |
| download | linux-next-eecbee709cc4cb2edb6ca5e04cec7b80097d10a3.tar.gz linux-next-eecbee709cc4cb2edb6ca5e04cec7b80097d10a3.zip | |
Merge branch 'gpio/for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
# Conflicts:
# MAINTAINERS
# drivers/gpio/gpiolib-kunit.c
47 files changed, 1739 insertions, 254 deletions
diff --git a/Documentation/devicetree/bindings/gpio/axiado,ax3005-sgpio.yaml b/Documentation/devicetree/bindings/gpio/axiado,ax3005-sgpio.yaml new file mode 100644 index 000000000000..47ae1285d482 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/axiado,ax3005-sgpio.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/axiado,ax3005-sgpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Axiado AX3005 SGPIO Controller + +maintainers: + - Petar Stepanovic <pstepanovic@axiado.com> + - SriNavmani A <srinavmani@axiado.com> + - Prasad Bolisetty <pbolisetty@axiado.com> + +description: | + The Axiado SGPIO controller provides a serial GPIO expansion interface + that multiplexes multiple GPIO signals over a small set of physical lines. + + The controller serializes output values on a data output line (DOUT) and + samples input values from a data input line (DIN) in synchronization with + a shift clock (SCLK). A latch signal (LATCH) updates the output states + after each transfer cycle. + + SGPIO positions are organized in banks of 32, with registers controlling + output values, input sampling, and interrupt status. The number of SGPIO + positions supported depends on the controller configuration. + + The controller detects changes on input GPIOs. Interrupt events are + aggregated and signaled through a single interrupt line to the parent + interrupt controller. + + Register access is performed over the APB bus. The SGPIO shift clock is + derived from the APB clock using a programmable divider. + + Each SGPIO position provides one input and one output, and both are + exposed as GPIO lines. Position N is described by two consecutive lines: + line 2 * N is the input and line 2 * N + 1 is the output. Directions are + fixed by the hardware, so even lines are always inputs and odd lines are + always outputs. + + Only input lines can generate interrupts. Rising, falling, and both-edge + interrupts are supported. + +properties: + compatible: + const: axiado,ax3005-sgpio + + reg: + maxItems: 1 + + clocks: + description: APB interface clock + maxItems: 1 + + bus-frequency: + description: Desired SGPIO shift clock frequency in Hz + minimum: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + interrupts: + maxItems: 1 + + ngpios: + description: | + Total number of GPIO lines provided by the controller. Each SGPIO + position wired up on the board contributes one input and one output + line, so this is twice the number of positions and must be even. It + may be lower than the maximum supported by the hardware. + minimum: 2 + maximum: 1024 + multipleOf: 2 + +required: + - compatible + - reg + - clocks + - bus-frequency + - gpio-controller + - '#gpio-cells' + - interrupts + - interrupt-controller + - '#interrupt-cells' + - ngpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + gpio@33201c00 { + compatible = "axiado,ax3005-sgpio"; + reg = <0x33201c00 0x400>; + clocks = <&apb_clk>; + bus-frequency = <1000000>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic500>; + interrupts = <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>; + ngpios = <256>; + }; diff --git a/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml index 4cb2a6b9fabf..1e61e17b0ee3 100644 --- a/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml @@ -63,6 +63,13 @@ properties: GPIO registers are used as little endian. If not present registers are used as big endian by default. +patternProperties: + "^.+-hog(-[0-9]+)?$": + type: object + + required: + - gpio-hog + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index 4631388a7d91..d671aaf83bb7 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -26,6 +26,9 @@ properties: - const: kinetic,kts1622 - const: ti,tcal6416 - items: + - const: onnn,pca9655 + - const: nxp,pca9555 + - items: - enum: - exar,xra1202 - maxim,max7310 diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml index de24bb361e9f..fffeb5ec0645 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml @@ -45,6 +45,12 @@ properties: power-domains: maxItems: 1 +patternProperties: + "^.+-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + allOf: - if: properties: diff --git a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml index adeb3b3a2902..fac53e65909b 100644 --- a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml @@ -91,6 +91,9 @@ properties: - nvidia,tegra264-gpio - nvidia,tegra264-gpio-uphy - nvidia,tegra264-gpio-aon + - nvidia,tegra268-gpio + - nvidia,tegra268-gpio-uphy + - nvidia,tegra268-gpio-aon reg-names: items: @@ -169,6 +172,8 @@ allOf: - nvidia,tegra256-gpio - nvidia,tegra264-gpio - nvidia,tegra264-gpio-uphy + - nvidia,tegra268-gpio + - nvidia,tegra268-gpio-uphy then: properties: interrupts: @@ -185,6 +190,7 @@ allOf: - nvidia,tegra234-gpio-aon - nvidia,tegra238-gpio-aon - nvidia,tegra264-gpio-aon + - nvidia,tegra268-gpio-aon then: properties: interrupts: @@ -201,6 +207,9 @@ allOf: - nvidia,tegra264-gpio - nvidia,tegra264-gpio-uphy - nvidia,tegra264-gpio-aon + - nvidia,tegra268-gpio + - nvidia,tegra268-gpio-uphy + - nvidia,tegra268-gpio-aon then: required: - wakeup-parent diff --git a/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml b/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml index b18f8f0ca0ae..33c25df7b815 100644 --- a/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml @@ -40,6 +40,8 @@ properties: gpio-controller: true + gpio-ranges: true + ngpios: minimum: 1 maximum: 32 diff --git a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml index d32e103a64aa..95f5175c7a9d 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml @@ -56,6 +56,11 @@ properties: - renesas,gpio-r8a779h0 # R-Car V4M - const: renesas,rcar-gen4-gpio # R-Car Gen4 + - items: + - enum: + - renesas,gpio-r8a78000 # R-Car X5H + - const: renesas,rcar-gen5-gpio # R-Car Gen5 + reg: maxItems: 1 diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index 4ac1e12cf872..83b74843ecf2 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -55,7 +55,7 @@ looked up by the gpiod functions internally) used in the device tree. With above Internally, the GPIO subsystem prefixes the GPIO suffix ("gpios" or "gpio") with the string passed in con_id to get the resulting string -(``snprintf(... "%s-%s", con_id, gpio_suffixes[]``). +(``snprintf(... "%s-%s", con_id, gpio_suffixes[])``). ACPI ---- diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst index a4f160b95089..3e53374c7e9f 100644 --- a/Documentation/driver-api/gpio/driver.rst +++ b/Documentation/driver-api/gpio/driver.rst @@ -134,6 +134,13 @@ ending up in the pin control back-end "behind" the GPIO controller, usually closer to the actual pins. This way the pin controller can manage the below listed GPIO configurations. +The optional .get_config() callback reads a configuration back: the packed +parameter to query goes in, its bare argument comes out, the way +pinctrl_gpio_get_config() answers. gpiochip_generic_get_config() is its pin +control backed counterpart. Nothing in gpiolib calls it; it is for the GPIO +driver's own use, for example to learn the direction of a line when the pin +controller owns it. + If a pin controller back-end is used, the GPIO controller or hardware description needs to provide "GPIO ranges" mapping the GPIO line offsets to pin numbers on the pin controller so they can properly cross-reference each other. diff --git a/MAINTAINERS b/MAINTAINERS index 77f90424d8f2..ea94cbe016f5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4554,6 +4554,15 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml F: drivers/iio/adc/axiado_saradc.c +AXIADO SGPIO DRIVER +M: Petar Stepanovic <pstepanovic@axiado.com> +M: SriNavmani A <srinavmani@axiado.com> +M: Prasad Bolisetty <pbolisetty@axiado.com> +L: linux-gpio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/gpio/axiado,ax3005-sgpio.yaml +F: drivers/gpio/gpio-axiado-sgpio.c + AXIS ARTPEC ARM64 SoC SUPPORT M: Jesper Nilsson <jesper.nilsson@axis.com> M: Lars Persson <lars.persson@axis.com> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 2b93254a7890..55e4280029d8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -204,6 +204,27 @@ config GPIO_ATH79 Select this option to enable GPIO driver for Atheros AR71XX/AR724X/AR913X SoC devices. +config GPIO_AXIADO_SGPIO + tristate "Axiado AX3005 SGPIO support" + depends on ARCH_AXIADO || COMPILE_TEST + select GPIOLIB_IRQCHIP + select GPIO_REGMAP + select REGMAP_MMIO + help + Enable support for the Axiado AX3005 Serial GPIO (SGPIO) + controller. + + The SGPIO controller provides a serialized interface for + controlling multiple GPIO signals over a limited number of + physical lines. Each signal is exposed as one input and one + output line, and input lines can raise interrupts. + + This driver integrates with the Linux GPIO subsystem and + exposes the controller as a standard GPIO provider. + + To compile this driver as a module, choose M here: the module + will be called gpio-axiado-sgpio. + config GPIO_RASPBERRYPI_EXP tristate "Raspberry Pi 3 GPIO Expander" default RASPBERRYPI_FIRMWARE @@ -1830,6 +1851,18 @@ endmenu menu "Auxiliary Bus GPIO drivers" depends on AUXILIARY_BUS +config GPIO_AD7768 + tristate "Analog Devices AD7768 GPIO support" + depends on AD7768 + depends on GPIOLIB + select GPIO_REGMAP + help + Say yes here to expose the AD7768 utility pins as GPIOs when the + device tree node is marked as a GPIO controller. + + To compile this driver as a module, choose M here: the module will be + called gpio-ad7768. + config GPIO_LTC4283 tristate "Analog Devices LTC4283 GPIO support" depends on SENSORS_LTC4283 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 729f2035d2bc..88f748846e60 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o +obj-$(CONFIG_GPIO_AD7768) += gpio-ad7768.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o @@ -40,6 +41,7 @@ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o +obj-$(CONFIG_GPIO_AXIADO_SGPIO) += gpio-axiado-sgpio.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o diff --git a/drivers/gpio/gpio-ad7768.c b/drivers/gpio/gpio-ad7768.c new file mode 100644 index 000000000000..c37e1ac30612 --- /dev/null +++ b/drivers/gpio/gpio-ad7768.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7768 GPIO auxiliary driver + * + * Copyright 2026 Analog Devices Inc. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/regmap.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#define AD7768_REG_GPIO_CONTROL 0x0E +#define AD7768_GPIO_UGPIO_ENABLE BIT(7) + +#define AD7768_REG_GPIO_WRITE 0x0F +#define AD7768_REG_GPIO_READ 0x10 + +#define AD7768_NUM_GPIOS 5 +#define AD7768_FILTER_GPIO 4 + +static int ad7768_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + if (ngpios > AD7768_FILTER_GPIO && + fwnode_property_match_string(gc->fwnode, "clock-names", "mclk") < 0) + __clear_bit(AD7768_FILTER_GPIO, valid_mask); + + return 0; +} + +static int ad7768_gpio_reg_mask_xlate(struct gpio_regmap *gpio, + enum gpio_regmap_operation op, + unsigned int base, unsigned int offset, + unsigned int *reg, unsigned int *mask) +{ + struct regmap *regmap = gpio_regmap_get_drvdata(gpio); + int ret; + + *reg = base; + *mask = BIT(offset); + + if (op != GPIO_REGMAP_GET_OP) + return 0; + + /* + * AD7768 has separate input-state and output-latch registers. For an + * output line, report the programmed value from the output latch; + * input lines continue to use the input-state register. + */ + ret = regmap_test_bits(regmap, AD7768_REG_GPIO_CONTROL, *mask); + if (ret < 0) + return ret; + if (ret) + *reg = AD7768_REG_GPIO_WRITE; + + return 0; +} + +static int ad7768_gpio_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct gpio_regmap_config config; + struct device *dev = &adev->dev; + struct device *parent = dev->parent; + struct regmap *map; + int ret; + + map = dev_get_regmap(parent, NULL); + if (!map) + return -ENODEV; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(parent, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = regmap_set_bits(map, AD7768_REG_GPIO_CONTROL, AD7768_GPIO_UGPIO_ENABLE); + if (ret) + return ret; + + config = (struct gpio_regmap_config) { + .parent = dev, + .regmap = map, + .label = dev_name(parent), + .ngpio = AD7768_NUM_GPIOS, + .reg_dat_base = AD7768_REG_GPIO_READ, + .reg_set_base = AD7768_REG_GPIO_WRITE, + .reg_dir_out_base = AD7768_REG_GPIO_CONTROL, + .pm_dev = parent, + .fwnode = dev_fwnode(parent), + .reg_mask_xlate = ad7768_gpio_reg_mask_xlate, + .init_valid_mask = ad7768_gpio_init_valid_mask, + .drvdata = map, + }; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config)); +} + +static const struct auxiliary_device_id ad7768_gpio_ids[] = { + { .name = "ad7768.gpio" }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, ad7768_gpio_ids); + +static struct auxiliary_driver ad7768_gpio_driver = { + .probe = ad7768_gpio_probe, + .id_table = ad7768_gpio_ids, +}; +module_auxiliary_driver(ad7768_gpio_driver); + +MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7768 GPIO auxiliary driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index 6f10fc646008..7c04a7e86c8f 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -390,16 +390,6 @@ static const struct irq_chip adp5585_irq_chip = { GPIOCHIP_IRQ_RESOURCE_HELPERS, }; -static void adp5585_gpio_unreg_notifier(void *data) -{ - struct adp5585_gpio_dev *adp5585_gpio = data; - struct device *dev = adp5585_gpio->gpio_chip.parent; - struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); - - blocking_notifier_chain_unregister(&adp5585->event_notifier, - &adp5585_gpio->nb); -} - static int adp5585_gpio_probe(struct platform_device *pdev) { struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent); @@ -450,13 +440,9 @@ static int adp5585_gpio_probe(struct platform_device *pdev) girq->threaded = true; adp5585_gpio->nb.notifier_call = adp5585_gpio_key_event; - ret = blocking_notifier_chain_register(&adp5585->event_notifier, - &adp5585_gpio->nb); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, adp5585_gpio_unreg_notifier, - adp5585_gpio); + ret = devm_blocking_notifier_chain_register(dev, + &adp5585->event_notifier, + &adp5585_gpio->nb); if (ret) return ret; } diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 532e3360b70e..a6ac9356229c 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -220,8 +220,11 @@ static int altera_gpio_probe(struct platform_device *pdev) return -ENOMEM; mapped_irq = platform_get_irq_optional(pdev, 0); - if (mapped_irq < 0) + if (mapped_irq < 0) { + if (mapped_irq != -ENXIO) + return mapped_irq; goto skip_irq; + } if (device_property_read_u32(dev, "altr,interrupt-type", ®)) { dev_err(&pdev->dev, diff --git a/drivers/gpio/gpio-axiado-sgpio.c b/drivers/gpio/gpio-axiado-sgpio.c new file mode 100644 index 000000000000..b57a5074ccef --- /dev/null +++ b/drivers/gpio/gpio-axiado-sgpio.c @@ -0,0 +1,736 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022-2026 Axiado Corporation + */ + +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/regmap.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdesc.h> +#include <linux/irqdomain.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/types.h> + +struct axiado_sgpio_reg_offsets { + u32 mux_0; + u32 preset_0; + u32 count_0; + u32 pos_0; + + u32 mux_1; + u32 ld; + u32 ld_ss; + + u32 preset_1; + u32 count_1; + u32 pos_1; + + u32 mux_2; + u32 dout; + u32 dout_ss; + + u32 preset_2; + u32 count_2; + u32 pos_2; + + u32 mux_3; + u32 preset_3; + u32 count_3; + u32 pos_3; + + u32 mux_4; + u32 oe; + u32 oe_ss; + + u32 preset_4; + u32 count_4; + u32 pos_4; + + u32 mask; + u32 ctrl_en; + u32 ctrl_en_pos; + + u32 din_ss; + u32 status; +}; + +static const struct axiado_sgpio_reg_offsets axiado_sgpio_offsets = { + .mux_0 = 0x000, + .preset_0 = 0x1dc, + .count_0 = 0x1f0, + .pos_0 = 0x204, + + .mux_1 = 0x004, + .ld = 0x014, + .ld_ss = 0x0d8, + + .preset_1 = 0x1e0, + .count_1 = 0x1f4, + .pos_1 = 0x208, + + .mux_2 = 0x008, + .dout = 0x054, + .dout_ss = 0x158, + + .preset_2 = 0x1e4, + .count_2 = 0x1f8, + .pos_2 = 0x20c, + + .mux_3 = 0x00c, + .preset_3 = 0x1e8, + .count_3 = 0x1fc, + .pos_3 = 0x210, + + .mux_4 = 0x010, + .oe = 0x0d4, + .oe_ss = 0x1d8, + + .preset_4 = 0x1ec, + .count_4 = 0x200, + .pos_4 = 0x214, + + .mask = 0x224, + .ctrl_en = 0x218, + .ctrl_en_pos = 0x21c, + + .din_ss = 0x198, + .status = 0x228, +}; + +/* Number of SGPIO positions held by one bank register. */ +#define SGPIO_BANK_SIZE 32 +#define SGPIO_MAX_SIGNALS 512 +#define SGPIO_NUM_BANKS 16 + +/* + * Slice mux encodings taken from the AX3005 SGPIO programming sequence. + */ +#define SGPIO_MUX_CLK 0x306 +#define SGPIO_MUX_DATA 0x30c +#define SGPIO_MUX_DIN 0x14c +#define SGPIO_MUX_OE 0x10c + +/* A POS register holds the first and the last position driven by the slice. */ +#define SGPIO_POS_FIRST GENMASK(31, 16) +#define SGPIO_POS_LAST GENMASK(15, 0) +#define SGPIO_POS(pos) (FIELD_PREP(SGPIO_POS_FIRST, (pos)) | \ + FIELD_PREP(SGPIO_POS_LAST, (pos))) + +/* The clock and output enable slices always cover a full bank. */ +#define SGPIO_POS_FULL_BANK SGPIO_POS(SGPIO_BANK_SIZE - 1) + +/* The enable and interrupt registers hold one bit per slice. */ +#define SGPIO_SLICE_MASK GENMASK(15, 0) + +/* + * Interrupt sources enabled by the programming sequence. Bit 13 is left + * masked, the remaining slice events are all reported. + */ +#define SGPIO_IRQ_ENABLE 0xdfff + +/* The upper half of the status register reports one bit per data bank. */ +#define SGPIO_STATUS_EXCHANGE GENMASK(31, 16) + +struct axiado_sgpio { + u32 preset_value; + u32 count_value; + u32 pos_reg; + /* Last sampled input word of each bank, for edge detection. */ + u32 *din_shadow; + unsigned long *irq_unmasked; + unsigned long *irq_rising; + unsigned long *irq_falling; + u32 nsignals; + u32 din_bank_shift; + unsigned long *dir_out; + struct gpio_regmap *gpio; + struct irq_domain *domain; + struct regmap *regmap; + const struct axiado_sgpio_reg_offsets *regs; +}; + +/* + * Tell lockdep that these interrupts are in a different category than the + * parent they are handled from, so that the nested handling is not reported + * as recursion. + */ +static struct lock_class_key axiado_sgpio_irq_lock_class; +static struct lock_class_key axiado_sgpio_irq_request_class; + +/* + * Each SGPIO signal is exposed as two GPIO lines: an even line for the serial + * input and the following odd line for the serial output. Signal n therefore + * lives in bit (n % 32) of bank (n / 32) of either the input or the output + * register. + */ +static int axiado_sgpio_reg_mask_xlate(struct gpio_regmap *gpio, + enum gpio_regmap_operation op, + unsigned int base, unsigned int offset, + unsigned int *reg, unsigned int *mask) +{ + struct axiado_sgpio *sgpio = gpio_regmap_get_drvdata(gpio); + unsigned int signal = offset / 2; + unsigned int bank = signal / SGPIO_BANK_SIZE; + + /* + * An output line lives in the output register for both reads and + * writes, so the register follows from the line rather than from the + * operation. The AX3005 input banks are right aligned, hence the + * shift. + */ + if (offset % 2) + base = sgpio->regs->dout_ss; + else + bank += sgpio->din_bank_shift; + + *reg = base + bank * sizeof(u32); + *mask = BIT(signal % SGPIO_BANK_SIZE); + + return 0; +} + +static irqreturn_t axiado_sgpio_irq_handler(int irq, void *arg) +{ + struct axiado_sgpio *sgpio = arg; + unsigned int nbanks = DIV_ROUND_UP(sgpio->nsignals, SGPIO_BANK_SIZE); + u32 status, new_value, changed_value; + unsigned int bit, reg_ptr, i; + int ret; + + /* Read-on-clear (ACK) parent cause */ + ret = regmap_read(sgpio->regmap, sgpio->regs->status, &status); + if (ret) + return IRQ_NONE; + + /* Nothing pending: the interrupt came from somebody else. */ + if (!status) + return IRQ_NONE; + + status = FIELD_GET(SGPIO_STATUS_EXCHANGE, status); + + reg_ptr = sgpio->din_bank_shift; + + for (i = 0; i < nbanks; i++, reg_ptr++) { + if (status & BIT(reg_ptr)) { + ret = regmap_read(sgpio->regmap, + sgpio->regs->din_ss + reg_ptr * sizeof(u32), + &new_value); + if (ret) + continue; + + /* + * Track every signal so that the shadow always holds + * the last sampled level, then report only the ones + * whose interrupt is unmasked. Transitions of a masked + * signal are dropped rather than delivered later, the + * hardware has no per signal latch to replay them + * from. Only this handler touches the shadow and it is + * not reentrant. + */ + changed_value = sgpio->din_shadow[i] ^ new_value; + sgpio->din_shadow[i] = new_value; + changed_value &= bitmap_read(sgpio->irq_unmasked, + i * SGPIO_BANK_SIZE, + SGPIO_BANK_SIZE); + + while (changed_value) { + unsigned int child_irq; + irq_hw_number_t hwirq; + unsigned int position; + bool rising; + + bit = __ffs(changed_value); + changed_value &= ~BIT(bit); + + position = i * SGPIO_BANK_SIZE + bit; + hwirq = position * 2; + + rising = !!(new_value & BIT(bit)); + + if (!test_bit(position, rising ? sgpio->irq_rising + : sgpio->irq_falling)) + continue; + + child_irq = irq_find_mapping(sgpio->domain, hwirq); + if (child_irq) + handle_nested_irq(child_irq); + } + } + } + + return IRQ_HANDLED; +} + +static int axiado_sgpio_hw_init(struct axiado_sgpio *sgpio) +{ + const struct axiado_sgpio_reg_offsets *regs = sgpio->regs; + unsigned int nbanks = DIV_ROUND_UP(sgpio->nsignals, SGPIO_BANK_SIZE); + unsigned int last_bank = (sgpio->nsignals - 1) / SGPIO_BANK_SIZE; + unsigned int last_bit = (sgpio->nsignals - 1) % SGPIO_BANK_SIZE; + unsigned int i; + int ret; + + /* Slice A0 is the shift clock, slice B1 the data load. */ + const struct reg_sequence pre_load[] = { + { regs->mask, 0 }, + { regs->mux_0, SGPIO_MUX_CLK }, + { regs->preset_0, sgpio->preset_value }, + { regs->count_0, sgpio->count_value }, + { regs->pos_0, SGPIO_POS_FULL_BANK }, + { regs->mux_1, SGPIO_MUX_DATA }, + }; + /* Slice C2 is the data output. */ + const struct reg_sequence pre_dout[] = { + { regs->preset_1, sgpio->preset_value }, + { regs->count_1, sgpio->count_value }, + { regs->pos_1, sgpio->pos_reg }, + { regs->mux_2, SGPIO_MUX_DATA }, + }; + /* Slice D3 is the data input, slice E4 the output enable. */ + const struct reg_sequence post_dout[] = { + { regs->preset_2, sgpio->preset_value }, + { regs->count_2, sgpio->count_value }, + { regs->pos_2, sgpio->pos_reg }, + { regs->mux_3, SGPIO_MUX_DIN }, + { regs->preset_3, sgpio->preset_value }, + { regs->count_3, sgpio->count_value }, + { regs->pos_3, sgpio->pos_reg }, + { regs->mux_4, SGPIO_MUX_OE }, + { regs->oe, GENMASK(31, 0) }, + { regs->oe_ss, GENMASK(31, 0) }, + { regs->preset_4, sgpio->preset_value }, + { regs->count_4, sgpio->count_value }, + { regs->pos_4, SGPIO_POS_FULL_BANK }, + { regs->ctrl_en, SGPIO_SLICE_MASK }, + { regs->ctrl_en_pos, SGPIO_SLICE_MASK }, + }; + + ret = regmap_multi_reg_write(sgpio->regmap, pre_load, + ARRAY_SIZE(pre_load)); + if (ret) + return ret; + + /* + * Latch the signals that are present. Only the last bank may be + * partially populated; a last bit of zero still describes one signal. + */ + for (i = 0; i < nbanks; i++) { + u32 val = i < last_bank ? GENMASK(SGPIO_BANK_SIZE - 1, 0) : + GENMASK(last_bit, 0); + + ret = regmap_write(sgpio->regmap, regs->ld + i * 4, val); + if (ret) + return ret; + + ret = regmap_write(sgpio->regmap, regs->ld_ss + i * 4, val); + if (ret) + return ret; + } + + ret = regmap_multi_reg_write(sgpio->regmap, pre_dout, + ARRAY_SIZE(pre_dout)); + if (ret) + return ret; + + /* All outputs start out driving zero. */ + for (i = 0; i < nbanks; i++) { + ret = regmap_write(sgpio->regmap, regs->dout + i * 4, 0); + if (ret) + return ret; + + ret = regmap_write(sgpio->regmap, regs->dout_ss + i * 4, 0); + if (ret) + return ret; + } + + return regmap_multi_reg_write(sgpio->regmap, post_dout, + ARRAY_SIZE(post_dout)); +} + +static int axiado_sgpio_irq_enable(struct axiado_sgpio *sgpio) +{ + return regmap_write(sgpio->regmap, sgpio->regs->mask, + SGPIO_IRQ_ENABLE); +} + +static void axiado_sgpio_disable(void *data) +{ + struct axiado_sgpio *sgpio = data; + + /* Mask the interrupts and stop the shift engine. */ + regmap_write(sgpio->regmap, sgpio->regs->mask, 0); + regmap_write(sgpio->regmap, sgpio->regs->ctrl_en, 0); +} + +static int axiado_sgpio_set_irq_type(struct irq_data *d, unsigned int type) +{ + struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned int position; + + /* Only even GPIO offsets represent SGPIO inputs. */ + if (hwirq & 1) + return -EINVAL; + + position = hwirq / 2; + if (position >= sgpio->nsignals) + return -EINVAL; + + type &= IRQ_TYPE_SENSE_MASK; + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + break; + default: + return -EINVAL; + } + + assign_bit(position, sgpio->irq_rising, type & IRQ_TYPE_EDGE_RISING); + assign_bit(position, sgpio->irq_falling, type & IRQ_TYPE_EDGE_FALLING); + + return 0; +} + +static void axiado_sgpio_mask_irq(struct irq_data *d) +{ + struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + clear_bit(hwirq / 2, sgpio->irq_unmasked); + gpio_regmap_disable_irq(sgpio->gpio, hwirq); +} + +static void axiado_sgpio_unmask_irq(struct irq_data *d) +{ + struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpio_regmap_enable_irq(sgpio->gpio, hwirq); + set_bit(hwirq / 2, sgpio->irq_unmasked); +} + +static int axiado_sgpio_irq_request_resources(struct irq_data *d) +{ + struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d); + + return gpio_regmap_reqres_irq(sgpio->gpio, d->hwirq); +} + +static void axiado_sgpio_irq_release_resources(struct irq_data *d) +{ + struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d); + + gpio_regmap_relres_irq(sgpio->gpio, d->hwirq); +} + +static const struct irq_chip axiado_sgpio_irqchip = { + .name = "axiado-sgpio", + .irq_mask = axiado_sgpio_mask_irq, + .irq_unmask = axiado_sgpio_unmask_irq, + .irq_set_type = axiado_sgpio_set_irq_type, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, + .irq_request_resources = axiado_sgpio_irq_request_resources, + .irq_release_resources = axiado_sgpio_irq_release_resources, +}; + +static int axiado_sgpio_irq_map(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq) +{ + struct axiado_sgpio *sgpio = domain->host_data; + + /* Only even offsets represent input GPIOs. */ + if (hwirq % 2) + return -EINVAL; + + irq_set_chip_data(virq, sgpio); + irq_set_lockdep_class(virq, &axiado_sgpio_irq_lock_class, + &axiado_sgpio_irq_request_class); + irq_set_chip_and_handler(virq, &axiado_sgpio_irqchip, handle_simple_irq); + irq_set_nested_thread(virq, true); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops axiado_sgpio_domain_ops = { + .map = axiado_sgpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int axiado_sgpio_init_input_cache(struct axiado_sgpio *sgpio) +{ + unsigned int num_banks = DIV_ROUND_UP(sgpio->nsignals, + SGPIO_BANK_SIZE); + unsigned int reg_ptr = sgpio->din_bank_shift; + unsigned int i; + int ret; + + for (i = 0; i < num_banks; i++, reg_ptr++) { + ret = regmap_read(sgpio->regmap, + sgpio->regs->din_ss + reg_ptr * sizeof(u32), + &sgpio->din_shadow[i]); + if (ret) + return ret; + } + + return 0; +} + +static bool axiado_sgpio_dout_reg(const struct axiado_sgpio *sgpio, + unsigned int reg) +{ + unsigned int span = SGPIO_NUM_BANKS * sizeof(u32); + + return reg >= sgpio->regs->dout_ss && + reg < sgpio->regs->dout_ss + span; +} + +static bool axiado_sgpio_readable_reg(struct device *dev, unsigned int reg) +{ + const struct axiado_sgpio *sgpio = dev_get_drvdata(dev); + + /* The status register is the last one of the block. */ + if (reg > sgpio->regs->status) + return false; + + /* + * The output shadow feeds the shift register and cannot be read back, + * the driven value is kept in the register cache instead. + */ + return !axiado_sgpio_dout_reg(sgpio, reg); +} + +static bool axiado_sgpio_volatile_reg(struct device *dev, unsigned int reg) +{ + const struct axiado_sgpio *sgpio = dev_get_drvdata(dev); + + /* + * Everything but the output shadow is live hardware state: shifted in + * data, running counters and the interrupt status. Caching any of it + * would hand out stale values, so the cache holds the output banks + * alone. + */ + return !axiado_sgpio_dout_reg(sgpio, reg); +} + +static bool axiado_sgpio_precious_reg(struct device *dev, unsigned int reg) +{ + const struct axiado_sgpio *sgpio = dev_get_drvdata(dev); + + /* Reading the status register acknowledges the interrupts. */ + return reg == sgpio->regs->status; +} + +static const struct regmap_config axiado_sgpio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .readable_reg = axiado_sgpio_readable_reg, + .volatile_reg = axiado_sgpio_volatile_reg, + .precious_reg = axiado_sgpio_precious_reg, + .cache_type = REGCACHE_MAPLE, +}; + +static int axiado_sgpio_probe(struct platform_device *pdev) +{ + struct gpio_regmap_config config = { }; + struct irq_domain_info d_info = { }; + struct axiado_sgpio *sgpio; + unsigned int ngpio, i; + unsigned long apb_freq; + struct clk *apb_clk; + void __iomem *base; + u32 sgpio_freq; + int irq, rc; + + sgpio = devm_kzalloc(&pdev->dev, sizeof(*sgpio), GFP_KERNEL); + if (!sgpio) + return -ENOMEM; + + /* The regmap callbacks below need both of these. */ + platform_set_drvdata(pdev, sgpio); + + sgpio->regs = &axiado_sgpio_offsets; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + sgpio->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &axiado_sgpio_regmap_config); + if (IS_ERR(sgpio->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(sgpio->regmap), + "Failed to init regmap\n"); + + rc = device_property_read_u32(&pdev->dev, "ngpios", &ngpio); + if (rc) + return dev_err_probe(&pdev->dev, rc, + "Failed to read ngpios property\n"); + + /* + * Each SGPIO signal is exposed as one input and one output line, so + * the number of lines is twice the number of signals. + */ + if (!ngpio || ngpio % 2 || ngpio > SGPIO_MAX_SIGNALS * 2) + return dev_err_probe(&pdev->dev, -EINVAL, + "Invalid ngpios value: %u (even, max %u)\n", + ngpio, SGPIO_MAX_SIGNALS * 2); + + sgpio->nsignals = ngpio / 2; + + sgpio->din_shadow = devm_kcalloc(&pdev->dev, + DIV_ROUND_UP(sgpio->nsignals, + SGPIO_BANK_SIZE), + sizeof(*sgpio->din_shadow), GFP_KERNEL); + sgpio->irq_unmasked = devm_bitmap_zalloc(&pdev->dev, sgpio->nsignals, + GFP_KERNEL); + sgpio->irq_rising = devm_bitmap_zalloc(&pdev->dev, sgpio->nsignals, + GFP_KERNEL); + sgpio->irq_falling = devm_bitmap_zalloc(&pdev->dev, sgpio->nsignals, + GFP_KERNEL); + if (!sgpio->din_shadow || !sgpio->irq_unmasked || + !sgpio->irq_rising || !sgpio->irq_falling) + return -ENOMEM; + + apb_clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(apb_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(apb_clk), + "Failed to get and enable APB clock\n"); + + rc = device_property_read_u32(&pdev->dev, "bus-frequency", + &sgpio_freq); + if (rc) + return dev_err_probe(&pdev->dev, rc, + "Failed to read bus-frequency\n"); + + apb_freq = clk_get_rate(apb_clk); + + if (!apb_freq || !sgpio_freq || sgpio_freq > apb_freq) + return dev_err_probe(&pdev->dev, -EINVAL, + "Invalid SGPIO bus frequency\n"); + + sgpio->preset_value = (apb_freq / sgpio_freq) - 1; + sgpio->count_value = sgpio->preset_value; + + sgpio->pos_reg = SGPIO_POS(sgpio->nsignals - 1); + + sgpio->din_bank_shift = SGPIO_NUM_BANKS - + DIV_ROUND_UP(sgpio->nsignals, SGPIO_BANK_SIZE); + + /* + * From here on the hardware may be running, so the shutdown action has + * to be in place before the first register is programmed. It only ever + * writes zeroes, which is harmless if initialisation never got that + * far, and it runs before the APB clock is disabled because that was + * requested earlier. + */ + rc = devm_add_action_or_reset(&pdev->dev, axiado_sgpio_disable, sgpio); + if (rc) + return rc; + + rc = axiado_sgpio_hw_init(sgpio); + if (rc) + return dev_err_probe(&pdev->dev, rc, + "Failed to initialize hardware\n"); + + rc = axiado_sgpio_init_input_cache(sgpio); + if (rc) + return dev_err_probe(&pdev->dev, rc, + "Failed to initialize input cache\n"); + + sgpio->dir_out = devm_bitmap_zalloc(&pdev->dev, ngpio, GFP_KERNEL); + if (!sgpio->dir_out) + return -ENOMEM; + + /* Even lines are the serial inputs, odd lines the serial outputs. */ + for (i = 1; i < ngpio; i += 2) + __set_bit(i, sgpio->dir_out); + + d_info.fwnode = dev_fwnode(&pdev->dev); + d_info.size = ngpio; + d_info.hwirq_max = ngpio; + d_info.ops = &axiado_sgpio_domain_ops; + d_info.host_data = sgpio; + + sgpio->domain = devm_irq_domain_instantiate(&pdev->dev, &d_info); + if (IS_ERR(sgpio->domain)) + return PTR_ERR(sgpio->domain); + + config.parent = &pdev->dev; + config.regmap = sgpio->regmap; + config.ngpio = ngpio; + config.reg_dat_base = GPIO_REGMAP_ADDR(sgpio->regs->din_ss); + config.reg_set_base = GPIO_REGMAP_ADDR(sgpio->regs->dout_ss); + config.reg_mask_xlate = axiado_sgpio_reg_mask_xlate; + config.fixed_direction_output = sgpio->dir_out; + config.irq_domain = sgpio->domain; + config.drvdata = sgpio; + + sgpio->gpio = devm_gpio_regmap_register(&pdev->dev, &config); + if (IS_ERR(sgpio->gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(sgpio->gpio), + "Could not register gpiochip\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + rc = devm_request_threaded_irq(&pdev->dev, irq, NULL, axiado_sgpio_irq_handler, + IRQF_ONESHOT, dev_name(&pdev->dev), sgpio); + if (rc) + return dev_err_probe(&pdev->dev, rc, "Failed to request IRQ\n"); + + rc = axiado_sgpio_irq_enable(sgpio); + if (rc) + return dev_err_probe(&pdev->dev, rc, + "Failed to enable interrupts\n"); + + return 0; +} + +static void axiado_sgpio_remove(struct platform_device *pdev) +{ + struct axiado_sgpio *sgpio = platform_get_drvdata(pdev); + + /* + * Runs before the devres unwind, so the interrupts are masked while the + * parent interrupt is still requested. The shift engine is stopped by + * axiado_sgpio_disable(), registered before the hardware was programmed + * so that it also covers failures during probe. + */ + regmap_write(sgpio->regmap, sgpio->regs->mask, 0); +} + +static const struct of_device_id axiado_sgpio_of_match[] = { + { .compatible = "axiado,ax3005-sgpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, axiado_sgpio_of_match); + +static struct platform_driver axiado_sgpio_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = axiado_sgpio_of_match, + }, + .probe = axiado_sgpio_probe, + .remove = axiado_sgpio_remove, +}; +module_platform_driver(axiado_sgpio_driver); + +MODULE_DESCRIPTION("Axiado AX3005 Serial GPIO Driver"); +MODULE_AUTHOR("Axiado Corporation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-cgbc.c b/drivers/gpio/gpio-cgbc.c index 84b5ed3c6e22..1b603d8eccda 100644 --- a/drivers/gpio/gpio-cgbc.c +++ b/drivers/gpio/gpio-cgbc.c @@ -18,6 +18,7 @@ #define CGBC_GPIO_CMD_SET 0x65 #define CGBC_GPIO_CMD_DIR_GET 0x66 #define CGBC_GPIO_CMD_DIR_SET 0x67 +#define CGBC_GPIO_CMD_ENA_RD 0x84 struct cgbc_gpio_data { struct gpio_chip chip; @@ -146,6 +147,30 @@ static int cgbc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) return GPIO_LINE_DIRECTION_IN; } +static int cgbc_gpio_init_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + int ret; + u8 val; + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_ENA_RD, 0, 0, &val); + if (ret) + return ret; + + *valid_mask = val; + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_ENA_RD, 1, 0, &val); + if (ret) + return ret; + + *valid_mask |= (val << 8); + + return 0; +} + static int cgbc_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -172,6 +197,7 @@ static int cgbc_gpio_probe(struct platform_device *pdev) chip->get_direction = cgbc_gpio_get_direction; chip->get = cgbc_gpio_get; chip->set = cgbc_gpio_set; + chip->init_valid_mask = cgbc_gpio_init_valid_mask; chip->ngpio = CGBC_GPIO_NGPIO; ret = devm_mutex_init(dev, &gpio->lock); diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 4670ffd7ea7f..aef4345d265e 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -243,10 +243,8 @@ static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset, if (ret < 0) return ret; - if (dir == DLN2_GPIO_DIRECTION_OUT) - set_bit(offset, dln2->output_enabled); - else - clear_bit(offset, dln2->output_enabled); + assign_bit(offset, dln2->output_enabled, + dir == DLN2_GPIO_DIRECTION_OUT); return ret; } diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 3b7ebcf12fe7..86eb9624d38a 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -601,13 +601,6 @@ static const struct irq_chip sprd_eic_irq = { GPIOCHIP_IRQ_RESOURCE_HELPERS, }; -static void sprd_eic_unregister_notifier(void *data) -{ - struct notifier_block *nb = data; - - atomic_notifier_chain_unregister(&sprd_eic_irq_notifier, nb); -} - static int sprd_eic_probe(struct platform_device *pdev) { const struct sprd_eic_variant_data *pdata; @@ -690,14 +683,8 @@ static int sprd_eic_probe(struct platform_device *pdev) } sprd_eic->irq_nb.notifier_call = sprd_eic_irq_notify; - ret = atomic_notifier_chain_register(&sprd_eic_irq_notifier, - &sprd_eic->irq_nb); - if (ret) - return dev_err_probe(dev, ret, - "Failed to register with the interrupt notifier"); - - return devm_add_action_or_reset(dev, sprd_eic_unregister_notifier, - &sprd_eic->irq_nb); + return devm_atomic_notifier_chain_register(dev, &sprd_eic_irq_notifier, + &sprd_eic->irq_nb); } static const struct of_device_id sprd_eic_of_match[] = { diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index 2ad3c239367b..699325129a7d 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -362,7 +362,7 @@ static int __init it87_gpio_init(void) */ labels = kcalloc(it87_gpio->chip.ngpio, sizeof("it87_gpXY"), GFP_KERNEL); - labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(const char *), + labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(*labels_table), GFP_KERNEL); if (!labels || !labels_table) { diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 4e2f3381d82b..4e5c5d196a50 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -394,7 +394,9 @@ mlxbf2_gpio_probe(struct platform_device *pdev) gc->owner = THIS_MODULE; irq = platform_get_irq_optional(pdev, 0); - if (irq >= 0) { + if (irq < 0 && irq != -ENXIO) + return irq; + if (irq > 0) { girq = &gs->chip.gc.irq; gpio_irq_chip_set_chip(girq, &mlxbf2_gpio_irq_chip); girq->handler = handle_simple_irq; diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 566326644a2c..929f64c5140a 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -231,7 +231,9 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) gc->add_pin_ranges = mlxbf3_gpio_add_pin_ranges; irq = platform_get_irq_optional(pdev, 0); - if (irq >= 0) { + if (irq < 0 && irq != -ENXIO) + return irq; + if (irq > 0) { girq = &gs->chip.gc.irq; gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip); girq->default_type = IRQ_TYPE_NONE; diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index e9c531eef452..0709ffaab4de 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -49,6 +49,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` #include <linux/log2.h> #include <linux/module.h> #include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/pinconf-generic.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/spinlock.h> @@ -354,6 +355,11 @@ static int gpio_mmio_dir_return(struct gpio_chip *gc, unsigned int gpio, if (!chip->pinctrl) return 0; +#ifdef CONFIG_PINCTRL + if (list_empty(&gc->gpiodev->pin_ranges)) + return 0; +#endif + if (dir_out) return pinctrl_gpio_direction_output(gc, gpio); else @@ -367,7 +373,17 @@ static int gpio_mmio_dir_in_err(struct gpio_chip *gc, unsigned int gpio) static int gpio_mmio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) { - return gpio_mmio_dir_return(gc, gpio, false); + struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); + int ret; + + ret = gpio_mmio_dir_return(gc, gpio, false); + if (ret) + return ret; + + guard(raw_spinlock_irqsave)(&chip->lock); + chip->sdir &= ~gpio_mmio_line2mask(gc, gpio); + + return 0; } static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, @@ -379,9 +395,19 @@ static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, static int gpio_mmio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { + struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); + int ret; + gc->set(gc, gpio, val); - return gpio_mmio_dir_return(gc, gpio, true); + ret = gpio_mmio_dir_return(gc, gpio, true); + if (ret) + return ret; + + guard(raw_spinlock_irqsave)(&chip->lock); + chip->sdir |= gpio_mmio_line2mask(gc, gpio); + + return 0; } static int gpio_mmio_dir_in(struct gpio_chip *gc, unsigned int gpio) @@ -596,20 +622,51 @@ static int gpio_mmio_setup_direction(struct gpio_generic_chip *chip, gc->direction_input = gpio_mmio_dir_in_err; else gc->direction_input = gpio_mmio_simple_dir_in; + + if (cfg->flags & GPIO_GENERIC_PINCTRL_BACKEND) { + chip->dir_unreadable = true; + gc->get_direction = gpio_mmio_get_dir; + gc->get_config = gpiochip_generic_get_config; + } } return 0; } +static void gpio_mmio_seed_dir_from_pinctrl(struct gpio_chip *gc, + unsigned int gpio) +{ + struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); + unsigned long config; + + if (!gc->get_config || chip->reg_dir_out || chip->reg_dir_in) + return; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT_ENABLE, 0); + if (gc->get_config(gc, gpio, &config)) + return; + + guard(raw_spinlock_irqsave)(&chip->lock); + if (config) + chip->sdir |= gpio_mmio_line2mask(gc, gpio); + else + chip->sdir &= ~gpio_mmio_line2mask(gc, gpio); +} + static int gpio_mmio_request(struct gpio_chip *gc, unsigned int gpio_pin) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); + int ret; if (gpio_pin >= gc->ngpio) return -EINVAL; - if (chip->pinctrl) - return gpiochip_generic_request(gc, gpio_pin); + if (chip->pinctrl) { + ret = gpiochip_generic_request(gc, gpio_pin); + if (ret) + return ret; + gpio_mmio_seed_dir_from_pinctrl(gc, gpio_pin); + } return 0; } diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index a2796240fe14..267e6d5bacd0 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -599,6 +599,21 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) } static const struct regmap_config mvebu_gpio_regmap_config = { + .name = "gpio", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static const struct regmap_config mvebu_gpio_percpu_regmap_config = { + .name = "percpu", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static const struct regmap_config mvebu_pwm_regmap_config = { + .name = "pwm", .reg_bits = 32, .reg_stride = 4, .val_bits = 32, @@ -868,7 +883,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev, return PTR_ERR(base); mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base, - &mvebu_gpio_regmap_config); + &mvebu_pwm_regmap_config); if (IS_ERR(mvpwm->regs)) return PTR_ERR(mvpwm->regs); @@ -1135,7 +1150,7 @@ static int mvebu_gpio_probe_raw(struct platform_device *pdev, mvchip->percpu_regs = devm_regmap_init_mmio(&pdev->dev, base, - &mvebu_gpio_regmap_config); + &mvebu_gpio_percpu_regmap_config); if (IS_ERR(mvchip->percpu_regs)) return PTR_ERR(mvchip->percpu_regs); } diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 005420baecf4..a9b9a3d9631e 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1032,8 +1032,6 @@ static void omap_gpio_mod_init(struct gpio_bank *bank) static int omap_gpio_chip_init(struct gpio_bank *bank, struct device *pm_dev) { struct gpio_irq_chip *irq; - static int gpio; - const char *label; int ret; /* @@ -1055,11 +1053,7 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct device *pm_dev) if (bank->regs->wkup_en) bank->chip.parent = &omap_mpuio_device.dev; } else { - label = devm_kasprintf(bank->chip.parent, GFP_KERNEL, "gpio-%d-%d", - gpio, gpio + bank->width - 1); - if (!label) - return -ENOMEM; - bank->chip.label = label; + bank->chip.label = dev_name(bank->chip.parent); } bank->chip.base = -1; bank->chip.ngpio = bank->width; @@ -1086,9 +1080,6 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct device *pm_dev) if (ret) gpiochip_remove(&bank->chip); - if (!bank->is_mpuio) - gpio += bank->width; - return ret; } diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index c1f5e10a3c20..1ba6a09542f3 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -71,8 +71,9 @@ MODULE_DEVICE_TABLE(of, pcf857x_of_table); struct pcf857x { struct gpio_chip chip; struct i2c_client *client; - struct mutex lock; /* protect 'out' */ + struct mutex lock; /* protect 'out' and 'dir' */ unsigned int out; /* software latch */ + unsigned int dir; /* direction latch (1 = input) */ unsigned int status; /* current status */ unsigned int irq_enabled; /* enabled irqs */ @@ -126,12 +127,21 @@ static int pcf857x_input(struct gpio_chip *chip, unsigned int offset) mutex_lock(&gpio->lock); gpio->out |= (1 << offset); + gpio->dir |= (1 << offset); status = gpio->write(gpio->client, gpio->out); mutex_unlock(&gpio->lock); return status; } +static int pcf857x_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct pcf857x *gpio = gpiochip_get_data(chip); + + return (gpio->dir & (1 << offset)) ? GPIO_LINE_DIRECTION_IN + : GPIO_LINE_DIRECTION_OUT; +} + static int pcf857x_get(struct gpio_chip *chip, unsigned int offset) { struct pcf857x *gpio = gpiochip_get_data(chip); @@ -167,6 +177,7 @@ static int pcf857x_output(struct gpio_chip *chip, unsigned int offset, int value gpio->out |= bit; else gpio->out &= ~bit; + gpio->dir &= ~bit; status = gpio->write(gpio->client, gpio->out); mutex_unlock(&gpio->lock); @@ -187,6 +198,7 @@ static int pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask, mutex_lock(&gpio->lock); gpio->out &= ~*mask; gpio->out |= *bits & *mask; + gpio->dir &= ~*mask; status = gpio->write(gpio->client, gpio->out); mutex_unlock(&gpio->lock); @@ -301,6 +313,7 @@ static int pcf857x_probe(struct i2c_client *client) gpio->chip.set_multiple = pcf857x_set_multiple; gpio->chip.direction_input = pcf857x_input; gpio->chip.direction_output = pcf857x_output; + gpio->chip.get_direction = pcf857x_get_direction; gpio->chip.ngpio = (uintptr_t)i2c_get_match_data(client); reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); @@ -396,6 +409,7 @@ static int pcf857x_probe(struct i2c_client *client) * reset state. Otherwise it flags pins to be driven low. */ gpio->out = ~n_latch; + gpio->dir = ~n_latch; gpio->status = gpio->read(gpio->client); /* Enable irqchip if we have an interrupt */ diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 2b5da973f51f..3ff21e899278 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -268,7 +268,7 @@ static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) spin_lock_irqsave(&gpio_lock, flags); value = readl_relaxed(base + GPDR_OFFSET); - if (__gpio_is_inverted(chip->base + offset)) + if (__gpio_is_inverted(offset)) value |= mask; else value &= ~mask; @@ -297,7 +297,7 @@ static int pxa_gpio_direction_output(struct gpio_chip *chip, spin_lock_irqsave(&gpio_lock, flags); tmp = readl_relaxed(base + GPDR_OFFSET); - if (__gpio_is_inverted(chip->base + offset)) + if (__gpio_is_inverted(offset)) tmp &= ~mask; else tmp |= mask; diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 09bebde5c426..54dc47d45de7 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -36,6 +36,7 @@ struct gpio_rcar_info { bool has_both_edge_trigger; bool has_always_in; bool has_inen; + const u8 *offsetmap; }; struct gpio_rcar_priv { @@ -49,34 +50,68 @@ struct gpio_rcar_priv { struct gpio_rcar_bank_info bank_info; }; -#define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */ -#define INOUTSEL 0x04 /* General Input/Output Switching Register */ -#define OUTDT 0x08 /* General Output Register */ -#define INDT 0x0c /* General Input Register */ -#define INTDT 0x10 /* Interrupt Display Register */ -#define INTCLR 0x14 /* Interrupt Clear Register */ -#define INTMSK 0x18 /* Interrupt Mask Register */ -#define MSKCLR 0x1c /* Interrupt Mask Clear Register */ -#define POSNEG 0x20 /* Positive/Negative Logic Select Register */ -#define EDGLEVEL 0x24 /* Edge/level Select Register */ -#define OUTDTSEL 0x40 /* Output Data Select Register */ -#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ -#define INEN 0x50 /* General Input Enable Register */ +enum gpio_rcar_reg { + IOINTSEL, /* General IO/Interrupt Switching Register */ + INOUTSEL, /* General Input/Output Switching Register */ + OUTDT, /* General Output Register */ + INDT, /* General Input Register */ + INTDT, /* Interrupt Display Register */ + INTCLR, /* Interrupt Clear Register */ + INTMSK, /* Interrupt Mask Register */ + MSKCLR, /* Interrupt Mask Clear Register */ + POSNEG, /* Positive/Negative Logic Select Register */ + EDGLEVEL, /* Edge/level Select Register */ + OUTDTSEL, /* Output Data Select Register */ + BOTHEDGE, /* One Edge/Both Edge Select Register */ + INEN, /* General Input Enable Register */ +}; + +static const u8 gpio_rcar_offsetmap_gen1[] = { + [IOINTSEL] = 0x00, + [INOUTSEL] = 0x04, + [OUTDT] = 0x08, + [INDT] = 0x0c, + [INTDT] = 0x10, + [INTCLR] = 0x14, + [INTMSK] = 0x18, + [MSKCLR] = 0x1c, + [POSNEG] = 0x20, + [EDGLEVEL] = 0x24, + [OUTDTSEL] = 0x40, + [BOTHEDGE] = 0x4c, + [INEN] = 0x50, +}; + +static const u8 gpio_rcar_offsetmap_gen5[] = { + [IOINTSEL] = 0x00, + [INOUTSEL] = 0x04, + [OUTDT] = 0x08, + [INDT] = 0x1c, + [INTDT] = 0x80, + [INTCLR] = 0x84, + [INTMSK] = 0x88, + [MSKCLR] = 0x8c, + [POSNEG] = 0x90, + [EDGLEVEL] = 0x94, + [OUTDTSEL] = 0x0c, + [BOTHEDGE] = 0xbc, + [INEN] = 0x18, +}; #define RCAR_MAX_GPIO_PER_BANK 32 -static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs) +static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, enum gpio_rcar_reg offs) { - return ioread32(p->base + offs); + return ioread32(p->base + p->info.offsetmap[offs]); } -static inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs, +static inline void gpio_rcar_write(struct gpio_rcar_priv *p, enum gpio_rcar_reg offs, u32 value) { - iowrite32(value, p->base + offs); + iowrite32(value, p->base + p->info.offsetmap[offs]); } -static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs, +static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, enum gpio_rcar_reg offs, int bit, bool value) { u32 tmp = gpio_rcar_read(p, offs); @@ -399,6 +434,7 @@ static const struct gpio_rcar_info gpio_rcar_info_gen1 = { .has_both_edge_trigger = false, .has_always_in = false, .has_inen = false, + .offsetmap = gpio_rcar_offsetmap_gen1, }; static const struct gpio_rcar_info gpio_rcar_info_gen2 = { @@ -406,6 +442,7 @@ static const struct gpio_rcar_info gpio_rcar_info_gen2 = { .has_both_edge_trigger = true, .has_always_in = false, .has_inen = false, + .offsetmap = gpio_rcar_offsetmap_gen1, }; static const struct gpio_rcar_info gpio_rcar_info_gen3 = { @@ -413,6 +450,7 @@ static const struct gpio_rcar_info gpio_rcar_info_gen3 = { .has_both_edge_trigger = true, .has_always_in = true, .has_inen = false, + .offsetmap = gpio_rcar_offsetmap_gen1, }; static const struct gpio_rcar_info gpio_rcar_info_gen4 = { @@ -420,6 +458,15 @@ static const struct gpio_rcar_info gpio_rcar_info_gen4 = { .has_both_edge_trigger = true, .has_always_in = true, .has_inen = true, + .offsetmap = gpio_rcar_offsetmap_gen1, +}; + +static const struct gpio_rcar_info gpio_rcar_info_gen5 = { + .has_outdtsel = true, + .has_both_edge_trigger = true, + .has_always_in = true, + .has_inen = true, + .offsetmap = gpio_rcar_offsetmap_gen5, }; static const struct of_device_id gpio_rcar_of_table[] = { @@ -439,6 +486,9 @@ static const struct of_device_id gpio_rcar_of_table[] = { .compatible = "renesas,rcar-gen4-gpio", .data = &gpio_rcar_info_gen4, }, { + .compatible = "renesas,rcar-gen5-gpio", + .data = &gpio_rcar_info_gen5, + }, { .compatible = "renesas,gpio-rcar", .data = &gpio_rcar_info_gen1, }, { diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index 4a606bad5848..5d98ad597d9e 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -176,10 +176,10 @@ static void realtek_gpio_update_line_imr(struct realtek_gpio_ctrl *ctrl, unsigne u32 reg_val; reg += 4 * (line_shift / 32); - reg_val = ioread32(reg); + reg_val = __raw_readl(reg); reg_val &= ~(REALTEK_GPIO_IMR_LINE_MASK << shift); reg_val |= (irq_type & irq_mask & REALTEK_GPIO_IMR_LINE_MASK) << shift; - iowrite32(reg_val, reg); + __raw_writel(reg_val, reg); } static void realtek_gpio_irq_ack(struct irq_data *data) @@ -393,18 +393,29 @@ static int realtek_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&ctrl->lock); + gen_gc_flags = GPIO_GENERIC_PINCTRL_BACKEND; + if (dev_flags & GPIO_PORTS_REVERSED) { - gen_gc_flags = 0; ctrl->bank_read = realtek_gpio_bank_read; ctrl->bank_write = realtek_gpio_bank_write; ctrl->line_imr_pos = realtek_gpio_line_imr_pos; } else { - gen_gc_flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER; + gen_gc_flags |= GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER; ctrl->bank_read = realtek_gpio_bank_read_swapped; ctrl->bank_write = realtek_gpio_bank_write_swapped; ctrl->line_imr_pos = realtek_gpio_line_imr_pos_swapped; } + if (device_property_read_bool(dev, "little-endian")) { + gen_gc_flags = 0; + ctrl->bank_read = realtek_gpio_bank_read; + ctrl->bank_write = realtek_gpio_bank_write; + } else if (device_is_big_endian(dev)) { + gen_gc_flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER; + ctrl->bank_read = realtek_gpio_bank_read_swapped; + ctrl->bank_write = realtek_gpio_bank_write_swapped; + } + config = (struct gpio_generic_chip_config) { .dev = dev, .sz = 4, @@ -423,6 +434,8 @@ static int realtek_gpio_probe(struct platform_device *pdev) ctrl->chip.gc.owner = THIS_MODULE; irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 && irq != -ENXIO) + return irq; if (!(dev_flags & GPIO_INTERRUPTS_DISABLED) && irq > 0) { girq = &ctrl->chip.gc.irq; gpio_irq_chip_set_chip(girq, &realtek_gpio_irq_chip); diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index fb21396e1d02..2f5f45b84cc9 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -6,11 +6,13 @@ */ #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/compiler_attributes.h> #include <linux/device.h> #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/types.h> @@ -23,6 +25,8 @@ struct gpio_regmap { struct device *parent; struct regmap *regmap; + struct device *pm_dev; + struct gpio_chip gpio_chip; int reg_stride; @@ -79,6 +83,33 @@ static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio, return 0; } +static int gpio_regmap_runtime_get(struct gpio_regmap *gpio) +{ + if (!gpio->pm_dev) + return 0; + + return pm_runtime_get_active(gpio->pm_dev, RPM_TRANSPARENT); +} + +static void gpio_regmap_runtime_put(struct gpio_regmap *gpio) +{ + if (!gpio->pm_dev) + return; + + pm_runtime_put_autosuspend(gpio->pm_dev); +} + +DEFINE_GUARD(gpio_regmap_runtime, struct gpio_regmap *, + gpio_regmap_runtime_get(_T), gpio_regmap_runtime_put(_T)) +DEFINE_GUARD_COND(gpio_regmap_runtime, _try, + gpio_regmap_runtime_get(_T), _RET == 0) + +#define GPIO_REGMAP_RUNTIME_ACQUIRE(_gpio, _var) \ + ACQUIRE(gpio_regmap_runtime_try, _var)(_gpio) + +#define GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(_var_ptr) \ + ACQUIRE_ERR(gpio_regmap_runtime, _var_ptr) + static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset) { struct gpio_regmap *gpio = gpiochip_get_data(chip); @@ -91,15 +122,21 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset) else base = gpio_regmap_addr(gpio->reg_set_base); - ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_OP, base, offset, ®, &mask); + GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm); + ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_OP, base, offset, + ®, &mask); if (ret) return ret; + if (gpio->reg_dat_base != gpio->reg_set_base) + return regmap_test_bits(gpio->regmap, reg, mask); + /* ensure we don't spoil any register cache with pin input values */ - if (gpio->reg_dat_base == gpio->reg_set_base) - ret = regmap_read_bypassed(gpio->regmap, reg, &val); - else - ret = regmap_read(gpio->regmap, reg, &val); + ret = regmap_read_bypassed(gpio->regmap, reg, &val); if (ret) return ret; @@ -114,7 +151,13 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset, unsigned int reg, mask, mask_val; int ret; - ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset, ®, &mask); + GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm); + ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset, + ®, &mask); if (ret) return ret; @@ -146,6 +189,11 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip, unsigned int base, reg, mask, value = 0; int ret; + GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm); + ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + if (val) base = gpio_regmap_addr(gpio->reg_set_base); else @@ -183,7 +231,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int offset) { struct gpio_regmap *gpio = gpiochip_get_data(chip); - unsigned int base, val, reg, mask; + unsigned int base, reg, mask; int invert, ret; if (gpio_regmap_fixed_direction(gpio, offset)) { @@ -208,18 +256,24 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, return -ENOTSUPP; } - ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_DIR_OP, base, offset, ®, &mask); + GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm); + ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; - ret = regmap_read(gpio->regmap, reg, &val); + ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_DIR_OP, base, offset, + ®, &mask); if (ret) return ret; - if (!!(val & mask) ^ invert) + ret = regmap_test_bits(gpio->regmap, reg, mask); + if (ret < 0) + return ret; + + if (ret ^ invert) return GPIO_LINE_DIRECTION_OUT; - else - return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_IN; } static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio, @@ -262,7 +316,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip, return -ENOTSUPP; } - ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset, ®, &mask); + GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm); + ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset, + ®, &mask); if (ret) return ret; @@ -293,6 +353,11 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip, struct gpio_regmap *gpio = gpiochip_get_data(chip); int ret; + GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm); + ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + /* * First check if this is gonna work on a fixed direction line, * if it doesn't (i.e. this is a fixed input line), then do not @@ -304,7 +369,9 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip, return ret; } - gpio_regmap_set(chip, offset, value); + ret = gpio_regmap_set(chip, offset, value); + if (ret) + return ret; return gpio_regmap_set_direction(chip, offset, true); } @@ -399,6 +466,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config gpio->reg_clr_base = config->reg_clr_base; gpio->reg_dir_in_base = config->reg_dir_in_base; gpio->reg_dir_out_base = config->reg_dir_out_base; + gpio->pm_dev = config->pm_dev; chip = &gpio->gpio_chip; chip->parent = config->parent; @@ -406,7 +474,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->base = -1; chip->names = config->names; chip->label = config->label ?: dev_name(config->parent); - chip->can_sleep = regmap_might_sleep(config->regmap); + chip->can_sleep = config->pm_dev || regmap_might_sleep(config->regmap); chip->init_valid_mask = config->init_valid_mask; chip->request = gpiochip_generic_request; diff --git a/drivers/gpio/gpio-rtd1625.c b/drivers/gpio/gpio-rtd1625.c index 483e44cf5abc..bebd1b8ede48 100644 --- a/drivers/gpio/gpio-rtd1625.c +++ b/drivers/gpio/gpio-rtd1625.c @@ -107,10 +107,11 @@ static int rtd1625_reg_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_ope unsigned int base, unsigned int offset, unsigned int *reg, unsigned int *mask) { - /* Each GPIO has its own dedicated 32-bit register */ struct rtd1625_gpio *data = gpio_regmap_get_drvdata(gpio); - int val = 0, ret = 0; + /* Each GPIO has its own dedicated 32-bit register */ *reg = base + offset * 4; + unsigned int val; + int ret; switch (op) { case GPIO_REGMAP_SET_OP: @@ -220,11 +221,11 @@ static void rtd1625_gpio_irq_handle(struct irq_desc *desc) { unsigned int (*get_reg_offset)(struct rtd1625_gpio *gpio, unsigned int offset); struct rtd1625_gpio *data = irq_desc_get_handler_data(desc); + struct device *dev = regmap_get_device(data->regmap); struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int irq = irq_desc_get_irq(desc); struct irq_domain *domain = data->domain; - unsigned int reg_offset, i, j, val; - irq_hw_number_t hwirq; + unsigned int reg_offset, j, val; unsigned long status; u32 irq_type; int ret; @@ -240,11 +241,12 @@ static void rtd1625_gpio_irq_handle(struct irq_desc *desc) chained_irq_enter(chip, desc); - for (i = 0; i < data->info->num_gpios; i += 32) { + for (unsigned int i = 0; i < data->info->num_gpios; i += 32) { reg_offset = get_reg_offset(data, i); ret = regmap_read(data->regmap, reg_offset, &val); if (ret) { - pr_err_ratelimited("Failed to read IRQ status for GPIO %u: %d\n", i, ret); + dev_err_ratelimited(dev, "Failed to read IRQ status for GPIO %u: %d\n", + i, ret); continue; } @@ -261,12 +263,14 @@ static void rtd1625_gpio_irq_handle(struct irq_desc *desc) if (irq != data->irqs[RTD1625_IRQ_LEVEL]) { ret = regmap_write(data->regmap, reg_offset, status); if (ret) - pr_err_ratelimited("Failed to clear edge IRQ for GPIO %u: %d\n", - i, ret); + dev_err_ratelimited(dev, + "Failed to clear edge IRQ for GPIO %u: %d\n", + i, ret); } for_each_set_bit(j, &status, 32) { - hwirq = i + j; + irq_hw_number_t hwirq = i + j; + irq_type = irq_get_trigger_type(irq_find_mapping(domain, hwirq)); /* @@ -486,7 +490,6 @@ static int rtd1625_gpio_setup_irq(struct platform_device *pdev, struct rtd1625_g return irq; num_irqs = (data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK) ? 3 : 2; - for (unsigned int i = 0; i < num_irqs; i++) { irq = platform_get_irq(pdev, i); if (irq < 0) @@ -612,8 +615,7 @@ static const struct rtd1625_gpio_info rtd1625_iso_gpio_info = { static const struct rtd1625_gpio_info rtd1625_isom_gpio_info = { .num_gpios = 4, - .irq_type_support = IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_LOW | - IRQ_TYPE_LEVEL_HIGH, + .irq_type_support = IRQ_TYPE_DEFAULT, .base_offset = 0x20, .gpa_offset = 0x00, .gpda_offset = 0x04, diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 0c73bec04267..898638e2794c 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -59,10 +59,10 @@ struct gpio_sim_attribute { unsigned int offset; }; -static struct gpio_sim_attribute * -to_gpio_sim_attr(struct device_attribute *dev_attr) +static const struct gpio_sim_attribute * +to_gpio_sim_attr(const struct device_attribute *dev_attr) { - return container_of(dev_attr, struct gpio_sim_attribute, dev_attr); + return container_of_const(dev_attr, struct gpio_sim_attribute, dev_attr); } static int gpio_sim_apply_pull(struct gpio_sim_chip *chip, @@ -270,9 +270,9 @@ static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc) } static ssize_t gpio_sim_sysfs_val_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { - struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); + const struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); struct gpio_sim_chip *chip = dev_get_drvdata(dev); int val; @@ -283,7 +283,7 @@ static ssize_t gpio_sim_sysfs_val_show(struct device *dev, } static ssize_t gpio_sim_sysfs_val_store(struct device *dev, - struct device_attribute *attr, + const struct device_attribute *attr, const char *buf, size_t count) { /* @@ -299,10 +299,10 @@ static const char *const gpio_sim_sysfs_pull_strings[] = { }; static ssize_t gpio_sim_sysfs_pull_show(struct device *dev, - struct device_attribute *attr, + const struct device_attribute *attr, char *buf) { - struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); + const struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); struct gpio_sim_chip *chip = dev_get_drvdata(dev); int pull; @@ -313,10 +313,10 @@ static ssize_t gpio_sim_sysfs_pull_show(struct device *dev, } static ssize_t gpio_sim_sysfs_pull_store(struct device *dev, - struct device_attribute *attr, + const struct device_attribute *attr, const char *buf, size_t len) { - struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); + const struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); struct gpio_sim_chip *chip = dev_get_drvdata(dev); int ret, pull; @@ -361,7 +361,7 @@ static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) unsigned int num_lines = chip->gc.ngpio; struct device *dev = chip->gc.parent; struct attribute_group *attr_group; - struct attribute **attrs; + const struct attribute **attrs; int i, ret; chip->attr_groups = devm_kcalloc(dev, sizeof(*chip->attr_groups), @@ -396,15 +396,15 @@ static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) val_dev_attr->attr.mode = pull_dev_attr->attr.mode = 0644; - val_dev_attr->show = gpio_sim_sysfs_val_show; - val_dev_attr->store = gpio_sim_sysfs_val_store; - pull_dev_attr->show = gpio_sim_sysfs_pull_show; - pull_dev_attr->store = gpio_sim_sysfs_pull_store; + val_dev_attr->show_const = gpio_sim_sysfs_val_show; + val_dev_attr->store_const = gpio_sim_sysfs_val_store; + pull_dev_attr->show_const = gpio_sim_sysfs_pull_show; + pull_dev_attr->store_const = gpio_sim_sysfs_pull_store; attrs[0] = &val_dev_attr->attr; attrs[1] = &pull_dev_attr->attr; - attr_group->attrs = attrs; + attr_group->attrs_const = attrs; chip->attr_groups[i] = attr_group; } @@ -1037,7 +1037,7 @@ gpio_sim_device_config_live_store(struct config_item *item, CONFIGFS_ATTR(gpio_sim_device_config_, live); -static struct configfs_attribute *gpio_sim_device_config_attrs[] = { +static const struct configfs_attribute *const gpio_sim_device_config_attrs[] = { &gpio_sim_device_config_attr_dev_name, &gpio_sim_device_config_attr_live, NULL @@ -1154,7 +1154,7 @@ gpio_sim_bank_config_num_lines_store(struct config_item *item, CONFIGFS_ATTR(gpio_sim_bank_config_, num_lines); -static struct configfs_attribute *gpio_sim_bank_config_attrs[] = { +static const struct configfs_attribute *const gpio_sim_bank_config_attrs[] = { &gpio_sim_bank_config_attr_chip_name, &gpio_sim_bank_config_attr_label, &gpio_sim_bank_config_attr_num_lines, @@ -1228,7 +1228,7 @@ static ssize_t gpio_sim_line_config_valid_store(struct config_item *item, CONFIGFS_ATTR(gpio_sim_line_config_, valid); -static struct configfs_attribute *gpio_sim_line_config_attrs[] = { +static const struct configfs_attribute *const gpio_sim_line_config_attrs[] = { &gpio_sim_line_config_attr_name, &gpio_sim_line_config_attr_valid, NULL @@ -1364,7 +1364,7 @@ gpio_sim_hog_config_active_low_store(struct config_item *item, CONFIGFS_ATTR(gpio_sim_hog_config_, active_low); -static struct configfs_attribute *gpio_sim_hog_config_attrs[] = { +static const struct configfs_attribute *const gpio_sim_hog_config_attrs[] = { &gpio_sim_hog_config_attr_name, &gpio_sim_hog_config_attr_direction, &gpio_sim_hog_config_attr_active_low, @@ -1390,7 +1390,7 @@ static const struct configfs_item_operations gpio_sim_hog_config_item_ops = { static const struct config_item_type gpio_sim_hog_config_type = { .ct_item_ops = &gpio_sim_hog_config_item_ops, - .ct_attrs = gpio_sim_hog_config_attrs, + .ct_attrs_const = gpio_sim_hog_config_attrs, .ct_owner = THIS_MODULE, }; @@ -1444,7 +1444,7 @@ static const struct configfs_group_operations gpio_sim_line_config_group_ops = { static const struct config_item_type gpio_sim_line_config_type = { .ct_item_ops = &gpio_sim_line_config_item_ops, .ct_group_ops = &gpio_sim_line_config_group_ops, - .ct_attrs = gpio_sim_line_config_attrs, + .ct_attrs_const = gpio_sim_line_config_attrs, .ct_owner = THIS_MODULE, }; @@ -1505,7 +1505,7 @@ static const struct configfs_group_operations gpio_sim_bank_config_group_ops = { static const struct config_item_type gpio_sim_bank_config_group_type = { .ct_item_ops = &gpio_sim_bank_config_item_ops, .ct_group_ops = &gpio_sim_bank_config_group_ops, - .ct_attrs = gpio_sim_bank_config_attrs, + .ct_attrs_const = gpio_sim_bank_config_attrs, .ct_owner = THIS_MODULE, }; @@ -1560,7 +1560,7 @@ static const struct configfs_group_operations gpio_sim_device_config_group_ops = static const struct config_item_type gpio_sim_device_config_group_type = { .ct_item_ops = &gpio_sim_device_config_item_ops, .ct_group_ops = &gpio_sim_device_config_group_ops, - .ct_attrs = gpio_sim_device_config_attrs, + .ct_attrs_const = gpio_sim_device_config_attrs, .ct_owner = THIS_MODULE, }; diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c index 958034b9f3f3..d60680ace144 100644 --- a/drivers/gpio/gpio-siox.c +++ b/drivers/gpio/gpio-siox.c @@ -148,11 +148,11 @@ static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset) if (offset >= 12) { unsigned int bitpos = 19 - offset; - ret = ddata->setdata[0] & (1 << bitpos); + ret = !!(ddata->setdata[0] & (1 << bitpos)); } else { unsigned int bitpos = 11 - offset; - ret = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8)); + ret = !!(ddata->getdata[bitpos / 8] & (1 << (bitpos % 8))); } mutex_unlock(&ddata->lock); diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 044d81e7cafb..0717649a6820 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -299,8 +299,8 @@ static int gpio_la_poll_probe(struct platform_device *pdev) debugfs_create_blob("meta_data", 0400, priv->debug_dir, &priv->meta); debugfs_create_ulong("delay_ns", 0600, priv->debug_dir, &priv->delay_ns); debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->acq_delay); - debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size); - debugfs_create_file_unsafe("capture", 0200, priv->debug_dir, priv, &fops_capture); + debugfs_create_file("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size); + debugfs_create_file("capture", 0200, priv->debug_dir, priv, &fops_capture); debugfs_create_file("trigger", 0200, priv->debug_dir, priv, &fops_trigger); return 0; diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index d9a2dedf50ea..34e084c95d7a 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -1419,6 +1419,71 @@ static const struct tegra_gpio_soc tegra264_uphy_soc = { .has_vm_support = true, }; +static const struct tegra_gpio_port tegra268_main_ports[] = { + TEGRA264_MAIN_GPIO_PORT(F, 3, 0, 8), + TEGRA264_MAIN_GPIO_PORT(G, 3, 1, 5), + TEGRA264_MAIN_GPIO_PORT(H, 1, 0, 8), + TEGRA264_MAIN_GPIO_PORT(J, 1, 1, 8), + TEGRA264_MAIN_GPIO_PORT(K, 1, 2, 8), + TEGRA264_MAIN_GPIO_PORT(L, 1, 3, 8), + TEGRA264_MAIN_GPIO_PORT(M, 1, 4, 2), + TEGRA264_MAIN_GPIO_PORT(P, 2, 0, 8), + TEGRA264_MAIN_GPIO_PORT(Q, 2, 1, 8), + TEGRA264_MAIN_GPIO_PORT(R, 2, 2, 8), + TEGRA264_MAIN_GPIO_PORT(S, 2, 3, 2), + TEGRA264_MAIN_GPIO_PORT(T, 0, 0, 7), + TEGRA264_MAIN_GPIO_PORT(U, 0, 1, 8), + TEGRA264_MAIN_GPIO_PORT(V, 0, 2, 8), + TEGRA264_MAIN_GPIO_PORT(W, 0, 3, 8), + TEGRA264_MAIN_GPIO_PORT(Y, 0, 5, 8), + TEGRA264_MAIN_GPIO_PORT(Z, 0, 6, 8), + TEGRA264_MAIN_GPIO_PORT(AL, 0, 4, 3), +}; + +static const struct tegra_gpio_soc tegra268_main_soc = { + .num_ports = ARRAY_SIZE(tegra268_main_ports), + .ports = tegra268_main_ports, + .name = "tegra268-gpio", + .instance = 0, + .num_irqs_per_bank = 8, + .has_vm_support = true, +}; + +static const struct tegra_gpio_port tegra268_aon_ports[] = { + TEGRA264_AON_GPIO_PORT(AA, 0, 0, 8), + TEGRA264_AON_GPIO_PORT(BB, 0, 1, 2), + TEGRA264_AON_GPIO_PORT(CC, 0, 2, 8), + TEGRA264_AON_GPIO_PORT(DD, 0, 3, 8), + TEGRA264_AON_GPIO_PORT(EE, 0, 4, 2) +}; + +static const struct tegra_gpio_soc tegra268_aon_soc = { + .num_ports = ARRAY_SIZE(tegra268_aon_ports), + .ports = tegra268_aon_ports, + .name = "tegra268-gpio-aon", + .instance = 1, + .num_irqs_per_bank = 8, + .has_gte = true, + .has_vm_support = true, +}; + +static const struct tegra_gpio_port tegra268_uphy_ports[] = { + TEGRA264_UPHY_GPIO_PORT(A, 0, 0, 6), + TEGRA264_UPHY_GPIO_PORT(B, 0, 1, 8), + TEGRA264_UPHY_GPIO_PORT(C, 0, 2, 3), + TEGRA264_UPHY_GPIO_PORT(D, 1, 0, 6), + TEGRA264_UPHY_GPIO_PORT(E, 1, 1, 4), +}; + +static const struct tegra_gpio_soc tegra268_uphy_soc = { + .num_ports = ARRAY_SIZE(tegra268_uphy_ports), + .ports = tegra268_uphy_ports, + .name = "tegra268-gpio-uphy", + .instance = 2, + .num_irqs_per_bank = 8, + .has_vm_support = true, +}; + #define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ TEGRA_GPIO_PORT(TEGRA256_MAIN, _name, _bank, _port, _pins) @@ -1529,6 +1594,15 @@ static const struct of_device_id tegra186_gpio_of_match[] = { .compatible = "nvidia,tegra264-gpio-uphy", .data = &tegra264_uphy_soc }, { + .compatible = "nvidia,tegra268-gpio", + .data = &tegra268_main_soc + }, { + .compatible = "nvidia,tegra268-gpio-aon", + .data = &tegra268_aon_soc + }, { + .compatible = "nvidia,tegra268-gpio-uphy", + .data = &tegra268_uphy_soc + }, { /* sentinel */ } }; diff --git a/drivers/gpio/gpiolib-acpi-quirks.c b/drivers/gpio/gpiolib-acpi-quirks.c index 600c4b0e64e4..3ab02c5f95df 100644 --- a/drivers/gpio/gpiolib-acpi-quirks.c +++ b/drivers/gpio/gpiolib-acpi-quirks.c @@ -441,6 +441,19 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { }, { /* + * Spurious wakeups from GPP3 PCIe bridge interrupt + * Found in BIOS UX425UA.301 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA_UM425UA"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "AMDI0030:00@24", + }, + }, + { + /* * Lenovo IdeaPad Slim 3 15ABR8: the touchscreen wake IRQ fires as soon * as the system enters suspend, causing an immediate spurious resume. */ diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 4b7dd37dedae..f68e33cbd7d4 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -234,13 +234,6 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd, return 0; case GPIOHANDLE_SET_LINE_VALUES_IOCTL: - /* - * All line descriptors were created at once with the same - * flags so just check if the first one is really output. - */ - if (!test_bit(GPIOD_FLAG_IS_OUT, &lh->descs[0]->flags)) - return -EPERM; - if (copy_from_user(&ghd, ip, sizeof(ghd))) return -EFAULT; @@ -715,6 +708,8 @@ static int hte_edge_setup(struct line *line, u64 eflags) line->desc); ret = hte_ts_get(NULL, hdesc, 0); + if (ret == -ENODEV) + return -EOPNOTSUPP; if (ret) return ret; diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c index 6a2c8b34f34b..11849aa565b3 100644 --- a/drivers/gpio/gpiolib-kunit.c +++ b/drivers/gpio/gpiolib-kunit.c @@ -4,6 +4,7 @@ */ #include <linux/cleanup.h> +#include <linux/device.h> #include <linux/err.h> #include <linux/fwnode.h> #include <linux/gpio/consumer.h> @@ -17,19 +18,34 @@ #include <kunit/fwnode.h> #include <kunit/platform_device.h> +#include <kunit/resource.h> #include <kunit/test.h> #define GPIO_TEST_PROVIDER "gpio-test-provider" -#define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer" +#define GPIO_BASE_TEST_CONSUMER "gpio-base-test-consumer" #define GPIO_PROBE_ORDER_TEST_CONSUMER "gpio-probe-order-test-consumer" #define GPIO_PROBE_DEFER_TEST_CONSUMER "gpio-probe-defer-test-consumer" #define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer" -#define GPIO_CONSUMER_NAME "gpio-swnode-consumer-test-device" #define GPIO_TEST_PROVIDER_NGPIO 4 KUNIT_DEFINE_ACTION_WRAPPER(gpio_device_put_wrapper, gpio_device_put, struct gpio_device *); +static void gpio_test_device_check_bound(struct kunit *test, + struct device *dev, bool expected) +{ + bool bound = false; + + wait_for_device_probe(); + scoped_guard(device, dev) + bound = device_is_bound(dev); + + if (expected) + KUNIT_ASSERT_TRUE(test, bound); + else + KUNIT_ASSERT_FALSE(test, bound); +} + /* * The test provider tracks per-line direction and value so that lines can be * driven as both inputs and outputs - this is needed to exercise input as well @@ -103,7 +119,7 @@ static int gpio_test_provider_probe(struct platform_device *pdev) gc->base = -1; gc->ngpio = GPIO_TEST_PROVIDER_NGPIO; - gc->label = GPIO_CONSUMER_NAME; + gc->label = GPIO_TEST_PROVIDER; gc->parent = dev; gc->owner = THIS_MODULE; @@ -127,20 +143,20 @@ static const struct software_node gpio_test_provider_swnode = { .name = "gpio-test-provider-primary", }; -struct gpio_swnode_consumer_pdata { +struct gpio_test_consumer_pdata { bool gpio_ok; int errno; }; -static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = { +static const struct gpio_test_consumer_pdata gpio_test_pdata_template = { .gpio_ok = false, .errno = 0, }; -static int gpio_swnode_consumer_probe(struct platform_device *pdev) +static int gpio_test_consumer_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct gpio_swnode_consumer_pdata *pdata = dev_get_platdata(dev); + struct gpio_test_consumer_pdata *pdata = dev_get_platdata(dev); struct gpio_desc *desc; desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); @@ -154,21 +170,21 @@ static int gpio_swnode_consumer_probe(struct platform_device *pdev) return 0; } -static struct platform_driver gpio_swnode_consumer_driver = { - .probe = gpio_swnode_consumer_probe, +static struct platform_driver gpio_test_consumer_driver = { + .probe = gpio_test_consumer_probe, .driver = { - .name = GPIO_SWNODE_TEST_CONSUMER, + .name = GPIO_BASE_TEST_CONSUMER, }, }; -static int gpio_swnode_register_drivers(struct kunit *test) +static int gpio_test_register_drivers(struct kunit *test) { int ret; ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); KUNIT_ASSERT_EQ(test, ret, 0); - ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); + ret = kunit_platform_driver_register(test, &gpio_test_consumer_driver); KUNIT_ASSERT_EQ(test, ret, 0); return 0; @@ -176,11 +192,10 @@ static int gpio_swnode_register_drivers(struct kunit *test) static void gpio_swnode_lookup_by_primary(struct kunit *test) { - struct gpio_swnode_consumer_pdata *pdata; + struct gpio_test_consumer_pdata *pdata; struct platform_device_info pdevinfo; struct property_entry properties[2]; struct platform_device *pdev; - bool bound = false; pdevinfo = (struct platform_device_info){ .name = GPIO_TEST_PROVIDER, @@ -197,21 +212,17 @@ static void gpio_swnode_lookup_by_primary(struct kunit *test) properties[1] = (struct property_entry){ }; pdevinfo = (struct platform_device_info){ - .name = GPIO_SWNODE_TEST_CONSUMER, + .name = GPIO_BASE_TEST_CONSUMER, .id = PLATFORM_DEVID_NONE, - .data = &gpio_swnode_pdata_template, - .size_data = sizeof(gpio_swnode_pdata_template), + .data = &gpio_test_pdata_template, + .size_data = sizeof(gpio_test_pdata_template), .properties = properties, }; pdev = kunit_platform_device_register_full(test, &pdevinfo); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); - wait_for_device_probe(); - scoped_guard(device, &pdev->dev) - bound = device_is_bound(&pdev->dev); - - KUNIT_ASSERT_TRUE(test, bound); + gpio_test_device_check_bound(test, &pdev->dev, true); pdata = dev_get_platdata(&pdev->dev); KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); @@ -219,12 +230,11 @@ static void gpio_swnode_lookup_by_primary(struct kunit *test) static void gpio_swnode_lookup_by_secondary(struct kunit *test) { - struct gpio_swnode_consumer_pdata *pdata; + struct gpio_test_consumer_pdata *pdata; struct platform_device_info pdevinfo; struct property_entry properties[2]; struct fwnode_handle *primary; struct platform_device *pdev; - bool bound = false; /* * Can't live on the stack as it will still get referenced in cleanup @@ -251,21 +261,17 @@ static void gpio_swnode_lookup_by_secondary(struct kunit *test) properties[1] = (struct property_entry){ }; pdevinfo = (struct platform_device_info){ - .name = GPIO_SWNODE_TEST_CONSUMER, + .name = GPIO_BASE_TEST_CONSUMER, .id = PLATFORM_DEVID_NONE, - .data = &gpio_swnode_pdata_template, - .size_data = sizeof(gpio_swnode_pdata_template), + .data = &gpio_test_pdata_template, + .size_data = sizeof(gpio_test_pdata_template), .properties = properties, }; pdev = kunit_platform_device_register_full(test, &pdevinfo); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); - wait_for_device_probe(); - scoped_guard(device, &pdev->dev) - bound = device_is_bound(&pdev->dev); - - KUNIT_ASSERT_TRUE(test, bound); + gpio_test_device_check_bound(test, &pdev->dev, true); pdata = dev_get_platdata(&pdev->dev); KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); @@ -280,7 +286,7 @@ static struct kunit_case gpio_swnode_lookup_tests[] = { static struct kunit_suite gpio_swnode_lookup_test_suite = { .name = "gpio-swnode-lookup", .test_cases = gpio_swnode_lookup_tests, - .init = gpio_swnode_register_drivers, + .init = gpio_test_register_drivers, }; static void gpio_swnode_unregister_swnode(void *data) @@ -335,7 +341,6 @@ static void gpio_swnode_probe_order(struct kunit *test) struct gpio_probe_order_pdata *pdata; struct platform_device_info pdevinfo; struct platform_device *prvd, *cons; - bool bound = false; int ret; ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); @@ -366,11 +371,7 @@ static void gpio_swnode_probe_order(struct kunit *test) cons = kunit_platform_device_register_full(test, &pdevinfo); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); - wait_for_device_probe(); - scoped_guard(device, &cons->dev) - bound = device_is_bound(&cons->dev); - - KUNIT_ASSERT_FALSE(test, bound); + gpio_test_device_check_bound(test, &cons->dev, false); pdata = dev_get_platdata(&cons->dev); KUNIT_ASSERT_EQ(test, pdata->probe_count, 0); @@ -385,15 +386,8 @@ static void gpio_swnode_probe_order(struct kunit *test) prvd = kunit_platform_device_register_full(test, &pdevinfo); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); - wait_for_device_probe(); - - scoped_guard(device, &prvd->dev) - bound = device_is_bound(&prvd->dev); - KUNIT_ASSERT_TRUE(test, bound); - - scoped_guard(device, &cons->dev) - bound = device_is_bound(&cons->dev); - KUNIT_ASSERT_TRUE(test, bound); + gpio_test_device_check_bound(test, &prvd->dev, true); + gpio_test_device_check_bound(test, &cons->dev, true); pdata = dev_get_platdata(&cons->dev); KUNIT_ASSERT_EQ(test, pdata->probe_count, 1); @@ -455,7 +449,6 @@ static void gpio_swnode_probe_defer_on_unregistered(struct kunit *test) struct platform_device_info pdevinfo; struct platform_device *prvd, *cons; struct fwnode_handle *fwnode; - bool bound = false; int ret; ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); @@ -479,11 +472,7 @@ static void gpio_swnode_probe_defer_on_unregistered(struct kunit *test) cons = kunit_platform_device_register_full(test, &pdevinfo); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); - wait_for_device_probe(); - scoped_guard(device, &cons->dev) - bound = device_is_bound(&cons->dev); - - KUNIT_ASSERT_FALSE(test, bound); + gpio_test_device_check_bound(test, &cons->dev, false); pdata = dev_get_platdata(&cons->dev); KUNIT_ASSERT_GT(test, pdata->probe_count, 0); @@ -501,15 +490,8 @@ static void gpio_swnode_probe_defer_on_unregistered(struct kunit *test) prvd = kunit_platform_device_register_full(test, &pdevinfo); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); - wait_for_device_probe(); - - scoped_guard(device, &prvd->dev) - bound = device_is_bound(&prvd->dev); - KUNIT_ASSERT_TRUE(test, bound); - - scoped_guard(device, &cons->dev) - bound = device_is_bound(&cons->dev); - KUNIT_ASSERT_TRUE(test, bound); + gpio_test_device_check_bound(test, &cons->dev, true); + gpio_test_device_check_bound(test, &prvd->dev, true); pdata = dev_get_platdata(&cons->dev); KUNIT_ASSERT_EQ(test, pdata->gpio_err, 0); @@ -566,13 +548,6 @@ static int gpio_unbind_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } -static void gpio_unbind_unregister_notifier(void *data) -{ - struct notifier_block *nb = data; - - blocking_notifier_chain_unregister(&gpio_unbind_notifier, nb); -} - static int gpio_unbind_consumer_probe(struct platform_device *pdev) { struct gpio_unbind_consumer_drvdata *data; @@ -590,11 +565,8 @@ static int gpio_unbind_consumer_probe(struct platform_device *pdev) return PTR_ERR(data->desc); data->nb.notifier_call = gpio_unbind_notify; - ret = blocking_notifier_chain_register(&gpio_unbind_notifier, &data->nb); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, gpio_unbind_unregister_notifier, &data->nb); + ret = devm_blocking_notifier_chain_register(dev, &gpio_unbind_notifier, + &data->nb); if (ret) return ret; @@ -616,7 +588,6 @@ static void gpio_unbind_with_consumers(struct kunit *test) struct platform_device_info pdevinfo; struct property_entry properties[2]; struct platform_device *prvd, *cons; - bool bound = false; int ret; ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); @@ -657,11 +628,7 @@ static void gpio_unbind_with_consumers(struct kunit *test) ret = kunit_platform_device_add(test, cons); KUNIT_ASSERT_EQ(test, ret, 0); - wait_for_device_probe(); - scoped_guard(device, &cons->dev) - bound = device_is_bound(&cons->dev); - - KUNIT_ASSERT_TRUE(test, bound); + gpio_test_device_check_bound(test, &cons->dev, true); kunit_platform_device_unregister(test, prvd); @@ -767,14 +734,13 @@ static const struct software_node *const gpio_hog_swnodes[] = { static void gpio_hog_assert(struct kunit *test, unsigned int offset, int expected_direction) { - struct gpio_swnode_consumer_pdata *pdata; + struct gpio_test_consumer_pdata *pdata; struct platform_device_info pdevinfo; struct property_entry properties[2]; struct platform_device *pdev; struct fwnode_handle *fwnode; struct gpio_device *gdev; struct gpio_desc *desc; - bool bound = true; int ret; fwnode = software_node_fwnode(&gpio_test_provider_swnode); @@ -792,7 +758,7 @@ static void gpio_hog_assert(struct kunit *test, unsigned int offset, wait_for_device_probe(); /* The hog must have configured the line with the expected direction. */ - gdev = gpio_device_find_by_label(GPIO_CONSUMER_NAME); + gdev = gpio_device_find_by_label(GPIO_TEST_PROVIDER); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gdev); ret = kunit_add_action_or_reset(test, gpio_device_put_wrapper, gdev); @@ -811,21 +777,17 @@ static void gpio_hog_assert(struct kunit *test, unsigned int offset, properties[1] = (struct property_entry){ }; pdevinfo = (struct platform_device_info){ - .name = GPIO_SWNODE_TEST_CONSUMER, + .name = GPIO_BASE_TEST_CONSUMER, .id = PLATFORM_DEVID_NONE, - .data = &gpio_swnode_pdata_template, - .size_data = sizeof(gpio_swnode_pdata_template), + .data = &gpio_test_pdata_template, + .size_data = sizeof(gpio_test_pdata_template), .properties = properties, }; pdev = kunit_platform_device_register_full(test, &pdevinfo); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); - wait_for_device_probe(); - scoped_guard(device, &pdev->dev) - bound = device_is_bound(&pdev->dev); - - KUNIT_ASSERT_FALSE(test, bound); + gpio_test_device_check_bound(test, &pdev->dev, false); pdata = dev_get_platdata(&pdev->dev); KUNIT_ASSERT_FALSE(test, pdata->gpio_ok); @@ -869,7 +831,107 @@ static struct kunit_suite gpio_swnode_hog_test_suite = { .test_cases = gpio_swnode_hog_tests, .suite_init = gpio_hog_suite_init, .suite_exit = gpio_hog_suite_exit, - .init = gpio_swnode_register_drivers, + .init = gpio_test_register_drivers, +}; + +KUNIT_DEFINE_ACTION_WRAPPER(gpiod_remove_lookup_table_wrapper, + gpiod_remove_lookup_table, + struct gpiod_lookup_table *); + +static int kunit_gpiod_add_lookup_table(struct kunit *test, + struct gpiod_lookup_table *table) +{ + gpiod_add_lookup_table(table); + + return kunit_add_action_or_reset(test, gpiod_remove_lookup_table_wrapper, table); +} + +static struct gpiod_lookup_table gpio_test_lookup_table_by_device = { + .dev_id = GPIO_BASE_TEST_CONSUMER, + .table = { + GPIO_LOOKUP(GPIO_TEST_PROVIDER, 0, "foo", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static void gpio_lookup_table_by_device(struct kunit *test) +{ + struct gpio_test_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct platform_device *pdev; + int ret; + + ret = kunit_gpiod_add_lookup_table(test, &gpio_test_lookup_table_by_device); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_BASE_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_test_pdata_template, + .size_data = sizeof(gpio_test_pdata_template), + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + gpio_test_device_check_bound(test, &pdev->dev, true); + + pdata = dev_get_platdata(&pdev->dev); + KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); +} + +static struct gpiod_lookup_table gpio_test_lookup_table_global = { + .dev_id = NULL, + .table = { + GPIO_LOOKUP(GPIO_TEST_PROVIDER, 0, "foo", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static void gpio_lookup_table_global(struct kunit *test) +{ + struct platform_device_info pdevinfo; + struct platform_device *pdev; + struct gpio_desc *desc; + int ret; + + ret = kunit_gpiod_add_lookup_table(test, &gpio_test_lookup_table_global); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + gpio_test_device_check_bound(test, &pdev->dev, true); + + desc = gpiod_get_index(NULL, "foo", 0, GPIOD_OUT_HIGH); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + gpiod_put(desc); +} + +static struct kunit_case gpio_lookup_table_tests[] = { + KUNIT_CASE(gpio_lookup_table_by_device), + KUNIT_CASE(gpio_lookup_table_global), + { } +}; + +static struct kunit_suite gpio_lookup_tables_suite = { + .name = "gpio-lookup-tables", + .test_cases = gpio_lookup_table_tests, + .init = gpio_test_register_drivers, }; kunit_test_suites( @@ -877,6 +939,7 @@ kunit_test_suites( &gpio_swnode_probe_order_test_suite, &gpio_unbind_with_consumers_test_suite, &gpio_swnode_hog_test_suite, + &gpio_lookup_tables_suite, ); MODULE_DESCRIPTION("Test module for the GPIO subsystem"); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9decee5a95c7..2987051d9904 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2447,6 +2447,27 @@ int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset, } EXPORT_SYMBOL_GPL(gpiochip_generic_config); +/** + * gpiochip_generic_get_config() - read back the configuration of a pin + * @gc: the gpiochip owning the GPIO + * @offset: the offset of the GPIO to query + * @config: the packed parameter to query, replaced by its bare argument + * + * Returns: + * 0 on success, or negative errno on failure. + */ +int gpiochip_generic_get_config(struct gpio_chip *gc, unsigned int offset, + unsigned long *config) +{ +#ifdef CONFIG_PINCTRL + if (list_empty(&gc->gpiodev->pin_ranges)) + return -ENOTSUPP; +#endif + + return pinctrl_gpio_get_config(gc, offset, config); +} +EXPORT_SYMBOL_GPL(gpiochip_generic_get_config); + #ifdef CONFIG_PINCTRL /** diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index 9a45b376d36f..f08ef52bf4f8 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -21,6 +21,7 @@ #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> @@ -291,8 +292,8 @@ struct pinmux_ops imx_pmx_ops = { .set_mux = imx_pmx_set, }; -static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id, - unsigned long *config) +static int imx_pinconf_get_raw_mmio(struct pinctrl_dev *pctldev, + unsigned int pin_id, unsigned long *config) { struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); const struct imx_pinctrl_soc_info *info = ipctl->info; @@ -312,6 +313,39 @@ static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id, return 0; } +static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, + unsigned int pin_id, unsigned long *config) +{ + struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx_pinctrl_soc_info *info = ipctl->info; + const struct imx_pin_reg *pin_reg = &ipctl->pin_regs[pin_id]; + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned int mask; + u32 raw; + + switch (param) { + case PIN_CONFIG_OUTPUT_ENABLE: + mask = info->obe_mask; + break; + case PIN_CONFIG_INPUT_ENABLE: + mask = info->ibe_mask; + break; + default: + mask = 0; + break; + } + + if (!mask) + return -ENOTSUPP; + if (pin_reg->conf_reg == -1) + return -EINVAL; + + raw = readl(ipctl->base + pin_reg->conf_reg); + *config = pinconf_to_config_packed(param, !!(raw & mask)); + + return 0; +} + static int imx_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin_id, unsigned long *config) { @@ -319,9 +353,21 @@ static int imx_pinconf_get(struct pinctrl_dev *pctldev, const struct imx_pinctrl_soc_info *info = ipctl->info; if (info->flags & IMX_USE_SCU) + return -ENOTSUPP; + + return imx_pinconf_get_mmio(pctldev, pin_id, config); +} + +static int imx_pinconf_get_raw(struct pinctrl_dev *pctldev, + unsigned int pin_id, unsigned long *config) +{ + struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx_pinctrl_soc_info *info = ipctl->info; + + if (info->flags & IMX_USE_SCU) return info->imx_pinconf_get(pctldev, pin_id, config); else - return imx_pinconf_get_mmio(pctldev, pin_id, config); + return imx_pinconf_get_raw_mmio(pctldev, pin_id, config); } static int imx_pinconf_set_mmio(struct pinctrl_dev *pctldev, @@ -426,7 +472,7 @@ static void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, struct imx_pin *pin = &((struct imx_pin *)(grp->data))[i]; name = pin_get_name(pctldev, pin->pin); - ret = imx_pinconf_get(pctldev, pin->pin, &config); + ret = imx_pinconf_get_raw(pctldev, pin->pin, &config); if (ret) return; seq_printf(s, " %s: 0x%lx\n", name, config); diff --git a/drivers/pinctrl/freescale/pinctrl-imx.h b/drivers/pinctrl/freescale/pinctrl-imx.h index f65ff45b4003..8fa7e1e2521d 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.h +++ b/drivers/pinctrl/freescale/pinctrl-imx.h @@ -91,6 +91,10 @@ struct imx_pinctrl_soc_info { unsigned int mux_mask; u8 mux_shift; + /* OBE/IBE bits in the conf register, 0 if the pad does not have them */ + unsigned int obe_mask; + unsigned int ibe_mask; + int (*gpio_set_direction)(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, diff --git a/drivers/pinctrl/freescale/pinctrl-vf610.c b/drivers/pinctrl/freescale/pinctrl-vf610.c index 76a4bc0181a0..77d077618782 100644 --- a/drivers/pinctrl/freescale/pinctrl-vf610.c +++ b/drivers/pinctrl/freescale/pinctrl-vf610.c @@ -319,6 +319,8 @@ static const struct imx_pinctrl_soc_info vf610_pinctrl_info = { .gpio_set_direction = vf610_pmx_gpio_set_direction, .mux_mask = 0x700000, .mux_shift = 20, + .obe_mask = 0x2, + .ibe_mask = 0x1, }; static const struct of_device_id vf610_pinctrl_of_match[] = { diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 17511434ed07..4077dc678cae 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -359,6 +359,10 @@ struct gpio_irq_chip { * @set_config: optional hook for all kinds of settings. Uses the same * packed config format as generic pinconf. Must return 0 on success and * a negative error number on failure. + * @get_config: optional hook to read back a setting. Takes the packed + * generic pinconf parameter to query and returns its bare argument in + * the same variable, like pinctrl_gpio_get_config(). Must return 0 on + * success and a negative error number on failure. * @to_irq: optional hook supporting non-static gpiod_to_irq() mappings; * implementation may not sleep * @dbg_show: optional routine to show contents in debugfs; default code @@ -434,6 +438,9 @@ struct gpio_chip { int (*set_config)(struct gpio_chip *gc, unsigned int offset, unsigned long config); + int (*get_config)(struct gpio_chip *gc, + unsigned int offset, + unsigned long *config); int (*to_irq)(struct gpio_chip *gc, unsigned int offset); @@ -708,6 +715,8 @@ int gpiochip_generic_request(struct gpio_chip *gc, unsigned int offset); void gpiochip_generic_free(struct gpio_chip *gc, unsigned int offset); int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset, unsigned long config); +int gpiochip_generic_get_config(struct gpio_chip *gc, unsigned int offset, + unsigned long *config); /** * struct gpio_pin_range - pin range controlled by a gpio chip diff --git a/include/linux/gpio/legacy.h b/include/linux/gpio/legacy.h index 557ef635935e..4001208d5d63 100644 --- a/include/linux/gpio/legacy.h +++ b/include/linux/gpio/legacy.h @@ -63,10 +63,6 @@ static inline int gpio_get_value_cansleep(unsigned gpio) { return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio)); } -static inline void gpio_set_value_cansleep(unsigned gpio, int value) -{ - gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value); -} static inline int gpio_get_value(unsigned gpio) { @@ -148,12 +144,6 @@ static inline int gpio_get_value_cansleep(unsigned gpio) return 0; } -static inline void gpio_set_value_cansleep(unsigned gpio, int value) -{ - /* GPIO can never have been requested or set as output */ - WARN_ON(1); -} - static inline int gpio_to_irq(unsigned gpio) { /* GPIO can never have been requested or set as input */ diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h index 6f52c140e50d..6fe0877717cb 100644 --- a/include/linux/gpio/regmap.h +++ b/include/linux/gpio/regmap.h @@ -46,6 +46,7 @@ enum gpio_regmap_operation { * @parent: The parent device * @regmap: The regmap used to access the registers * given, the name of the device is used + * @pm_dev: (Optional) Device to use for runtime power management. * @fwnode: (Optional) The firmware node. * If not given, the fwnode of the parent is used. * @label: (Optional) Descriptive name for GPIO controller. @@ -117,6 +118,7 @@ enum gpio_regmap_operation { struct gpio_regmap_config { struct device *parent; struct regmap *regmap; + struct device *pm_dev; struct fwnode_handle *fwnode; const char *label; diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 11b8f0b8da0c..7be49d447426 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -107,14 +107,14 @@ static inline int pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset, unsigned long *config) { - return 0; + return -ENOTSUPP; } static inline int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) { - return 0; + return -ENOTSUPP; } static inline struct pinctrl * __must_check pinctrl_get(struct device *dev) diff --git a/tools/gpio/gpio-hammer.c b/tools/gpio/gpio-hammer.c index ba0866eb3581..1a21e5460242 100644 --- a/tools/gpio/gpio-hammer.c +++ b/tools/gpio/gpio-hammer.c @@ -145,7 +145,7 @@ int main(int argc, char **argv) case 'o': /* * Avoid overflow. Do not immediately error, we want to - * be able to accurately report on the amount of times + * be able to accurately report on the number of times * '-o' was given to give an accurate error message */ if (i < GPIOHANDLES_MAX) |
