diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-29 09:43:39 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-29 09:43:39 -0800 |
commit | fa889d85551e0bd962fdefe1cc113f9ba1d04a36 (patch) | |
tree | 4f74147dc17e9fa39d06053a949c5491ccf2bace | |
parent | b3a6082223369203d7e7db7e81253ac761377644 (diff) | |
parent | 0282c72d30d32913d641dc81f3f38607ace98802 (diff) | |
download | linux-fa889d85551e0bd962fdefe1cc113f9ba1d04a36.tar.bz2 |
Merge tag 'gpio-v5.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij:
"This is the bulk of GPIO changes for the v5.6 kernel cycle.
This is a pretty calm cycle so far, nothing special going on really.
Some more changes will come in from the irqchip and pin control trees.
I also deleted an orphan include file for FMC that was dangling since
subsystem was removed.
Core changes:
- Document the usecases for the kernelspace vs userspace handling of
GPIOs.
- Handle MSI (message signalled interrupts) properly in the core
hierarchical irqdomain code.
- Fix a rare race condition while initializing the descriptor array.
New drivers:
- Xylon LogiCVC GPIO driver.
- WDC934x GPIO controller driver.
Driver improvements:
- Implemented suspend/resume in the Tegra driver.
- MPC8xx edge detection fixup.
- Properly convert ThunderX to use hierarchical irqdomain with
GPIOLIB_IRQCHIP on top of the revert of the previous buggy
switchover. This time it works (hopefully).
Misc:
- Drop a FMC remnant file <linux/ipmi-fru.h>
- A slew of fixes"
* tag 'gpio-v5.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (48 commits)
MAINTAINERS: Replace Tien Hock Loh as Altera PIO maintainer
gpiolib: hold gpio devices lock until ->descs array is initialised
gpio: aspeed-sgpio: fixed typos
gpio: mvebu: clear irq in edge cause register before unmask edge irq
gpiolib: Lower verbosity when allocating hierarchy irq
gpiolib: Remove duplicated function gpio_do_set_config()
gpio: Fix the no return statement warning
gpio: wcd934x: Add support to wcd934x gpio controller
gpiolib: remove set but not used variable 'config'
gpio: vx855: fixed a typo
gpio: mockup: sort headers alphabetically
gpio: mockup: update the license tag
gpio: Remove the unused flags
gpiolib: Set lockdep class for hierarchical irq domains
gpio: thunderx: Switch to GPIOLIB_IRQCHIP
gpiolib: Add the support for the msi parent domain
gpiolib: Add support for the irqdomain which doesn't use irq_fwspec as arg
gpio: Add use guidance documentation
dt-bindings: gpio: wcd934x: Add bindings for gpio
gpio: altera: change to platform_get_irq_optional to avoid false-positive error
...
38 files changed, 814 insertions, 413 deletions
diff --git a/Documentation/devicetree/bindings/gpio/qcom,wcd934x-gpio.yaml b/Documentation/devicetree/bindings/gpio/qcom,wcd934x-gpio.yaml new file mode 100644 index 000000000000..32a566ec3558 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/qcom,wcd934x-gpio.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/qcom,wcd934x-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: WCD9340/WCD9341 GPIO controller + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: | + Qualcomm Technologies Inc WCD9340/WCD9341 Audio Codec has integrated + gpio controller to control 5 gpios on the chip. + +properties: + compatible: + enum: + - qcom,wcd9340-gpio + - qcom,wcd9341-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + +additionalProperties: false + +examples: + - | + wcdgpio: gpio@42 { + compatible = "qcom,wcd9340-gpio"; + reg = <0x042 0x2>; + gpio-controller; + #gpio-cells = <2>; + }; + +... diff --git a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt index 41e5fed0f842..10dce84b1545 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt +++ b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt @@ -18,7 +18,8 @@ Required Properties: - "renesas,gpio-r8a7793": for R8A7793 (R-Car M2-N) compatible GPIO controller. - "renesas,gpio-r8a7794": for R8A7794 (R-Car E2) compatible GPIO controller. - "renesas,gpio-r8a7795": for R8A7795 (R-Car H3) compatible GPIO controller. - - "renesas,gpio-r8a7796": for R8A7796 (R-Car M3-W) compatible GPIO controller. + - "renesas,gpio-r8a7796": for R8A77960 (R-Car M3-W) compatible GPIO controller. + - "renesas,gpio-r8a77961": for R8A77961 (R-Car M3-W+) compatible GPIO controller. - "renesas,gpio-r8a77965": for R8A77965 (R-Car M3-N) compatible GPIO controller. - "renesas,gpio-r8a77970": for R8A77970 (R-Car V3M) compatible GPIO controller. - "renesas,gpio-r8a77980": for R8A77980 (R-Car V3H) compatible GPIO controller. diff --git a/Documentation/devicetree/bindings/gpio/xylon,logicvc-gpio.yaml b/Documentation/devicetree/bindings/gpio/xylon,logicvc-gpio.yaml new file mode 100644 index 000000000000..d102888c1be7 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/xylon,logicvc-gpio.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Bootlin +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/gpio/xylon,logicvc-gpio.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Xylon LogiCVC GPIO controller + +maintainers: + - Paul Kocialkowski <paul.kocialkowski@bootlin.com> + +description: | + The LogiCVC GPIO describes the GPIO block included in the LogiCVC display + controller. These are meant to be used for controlling display-related + signals. + + The controller exposes GPIOs from the display and power control registers, + which are mapped by the driver as follows: + - GPIO[4:0] (display control) mapped to index 0-4 + - EN_BLIGHT (power control) mapped to index 5 + - EN_VDD (power control) mapped to index 6 + - EN_VEE (power control) mapped to index 7 + - V_EN (power control) mapped to index 8 + +properties: + $nodename: + pattern: "^gpio@[0-9a-f]+$" + + compatible: + enum: + - xylon,logicvc-3.02.a-gpio + + reg: + maxItems: 1 + + "#gpio-cells": + const: 2 + + gpio-controller: true + + gpio-line-names: + minItems: 1 + maxItems: 9 + +required: + - compatible + - reg + - "#gpio-cells" + - gpio-controller + +examples: + - | + logicvc: logicvc@43c00000 { + compatible = "xylon,logicvc-3.02.a", "syscon", "simple-mfd"; + reg = <0x43c00000 0x6000>; + + #address-cells = <1>; + #size-cells = <1>; + + logicvc_gpio: gpio@40 { + compatible = "xylon,logicvc-3.02.a-gpio"; + reg = <0x40 0x40>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", + "EN_BLIGHT", "EN_VDD", "EN_VEE", "V_EN"; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/xylon,logicvc.yaml b/Documentation/devicetree/bindings/mfd/xylon,logicvc.yaml new file mode 100644 index 000000000000..abc9937506e0 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/xylon,logicvc.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Bootlin +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/mfd/xylon,logicvc.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Xylon LogiCVC multi-function device + +maintainers: + - Paul Kocialkowski <paul.kocialkowski@bootlin.com> + +description: | + The LogiCVC is a display controller that also contains a GPIO controller. + As a result, a multi-function device is exposed as parent of the display + and GPIO blocks. + +properties: + compatible: + items: + - enum: + - xylon,logicvc-3.02.a + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + +select: + properties: + compatible: + contains: + enum: + - xylon,logicvc-3.02.a + + required: + - compatible + +required: + - compatible + - reg + +examples: + - | + logicvc: logicvc@43c00000 { + compatible = "xylon,logicvc-3.02.a", "syscon", "simple-mfd"; + reg = <0x43c00000 0x6000>; + #address-cells = <1>; + #size-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 6046f4555852..9cb3bc683db7 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1060,6 +1060,8 @@ patternProperties: description: Xilinx "^xunlong,.*": description: Shenzhen Xunlong Software CO.,Limited + "^xylon,.*": + description: Xylon "^yones-toptech,.*": description: Yones Toptech Co., Ltd. "^ysoft,.*": diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 20e07e40be02..46c13780994c 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -267,6 +267,8 @@ DRM GPIO devm_gpiod_get() + devm_gpiod_get_array() + devm_gpiod_get_array_optional() devm_gpiod_get_index() devm_gpiod_get_index_optional() devm_gpiod_get_optional() diff --git a/Documentation/driver-api/gpio/drivers-on-gpio.rst b/Documentation/driver-api/gpio/drivers-on-gpio.rst index f3a189320e11..820b403d50f6 100644 --- a/Documentation/driver-api/gpio/drivers-on-gpio.rst +++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst @@ -95,7 +95,7 @@ to emulate MCTRL (modem control) signals CTS/RTS by using two GPIO lines. The MTD NOR flash has add-ons for extra GPIO lines too, though the address bus is usually connected directly to the flash. -Use those instead of talking directly to the GPIOs using sysfs; they integrate -with kernel frameworks better than your userspace code could. Needless to say, -just using the appropriate kernel drivers will simplify and speed up your -embedded hacking in particular by providing ready-made components. +Use those instead of talking directly to the GPIOs from userspace; they +integrate with kernel frameworks better than your userspace code could. +Needless to say, just using the appropriate kernel drivers will simplify and +speed up your embedded hacking in particular by providing ready-made components. diff --git a/Documentation/driver-api/gpio/index.rst b/Documentation/driver-api/gpio/index.rst index 5b61032aa4ea..1d48fe248f05 100644 --- a/Documentation/driver-api/gpio/index.rst +++ b/Documentation/driver-api/gpio/index.rst @@ -8,6 +8,7 @@ Contents: :maxdepth: 2 intro + using-gpio driver consumer board diff --git a/Documentation/driver-api/gpio/using-gpio.rst b/Documentation/driver-api/gpio/using-gpio.rst new file mode 100644 index 000000000000..dda069444032 --- /dev/null +++ b/Documentation/driver-api/gpio/using-gpio.rst @@ -0,0 +1,50 @@ +========================= +Using GPIO Lines in Linux +========================= + +The Linux kernel exists to abstract and present hardware to users. GPIO lines +as such are normally not user facing abstractions. The most obvious, natural +and preferred way to use GPIO lines is to let kernel hardware drivers deal +with them. + +For examples of already existing generic drivers that will also be good +examples for any other kernel drivers you want to author, refer to +:doc:`drivers-on-gpio` + +For any kind of mass produced system you want to support, such as servers, +laptops, phones, tablets, routers, and any consumer or office or business goods +using appropriate kernel drivers is paramount. Submit your code for inclusion +in the upstream Linux kernel when you feel it is mature enough and you will get +help to refine it, see :doc:`../../process/submitting-patches`. + +In Linux GPIO lines also have a userspace ABI. + +The userspace ABI is intended for one-off deployments. Examples are prototypes, +factory lines, maker community projects, workshop specimen, production tools, +industrial automation, PLC-type use cases, door controllers, in short a piece +of specialized equipment that is not produced by the numbers, requiring +operators to have a deep knowledge of the equipment and knows about the +software-hardware interface to be set up. They should not have a natural fit +to any existing kernel subsystem and not be a good fit for an operating system, +because of not being reusable or abstract enough, or involving a lot of non +computer hardware related policy. + +Applications that have a good reason to use the industrial I/O (IIO) subsystem +from userspace will likely be a good fit for using GPIO lines from userspace as +well. + +Do not under any circumstances abuse the GPIO userspace ABI to cut corners in +any product development projects. If you use it for prototyping, then do not +productify the prototype: rewrite it using proper kernel drivers. Do not under +any circumstances deploy any uniform products using GPIO from userspace. + +The userspace ABI is a character device for each GPIO hardware unit (GPIO chip). +These devices will appear on the system as ``/dev/gpiochip0`` thru +``/dev/gpiochipN``. Examples of how to directly use the userspace ABI can be +found in the kernel tree ``tools/gpio`` subdirectory. + +For structured and managed applications, we recommend that you make use of the +libgpiod_ library. This provides helper abstractions, command line utlities +and arbitration for multiple simultaneous consumers on the same GPIO chip. + +.. _libgpiod: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/ diff --git a/MAINTAINERS b/MAINTAINERS index d51ce1a0a817..e12989b9dfd3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -734,7 +734,7 @@ S: Maintained F: drivers/mailbox/mailbox-altera.c ALTERA PIO DRIVER -M: Tien Hock Loh <thloh@altera.com> +M: Joyce Ooi <joyce.ooi@intel.com> L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-altera.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f57d95a3db02..081776b91e5a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -312,6 +312,12 @@ config GPIO_IXP4XX IXP4xx series of chips. If unsure, say N. +config GPIO_LOGICVC + tristate "Xylon LogiCVC GPIO support" + depends on MFD_SYSCON && OF + help + Say yes here to support GPIO functionality of the Xylon LogiCVC + programmable logic block. config GPIO_LOONGSON bool "Loongson-2/3 GPIO support" @@ -582,6 +588,7 @@ config GPIO_THUNDERX tristate "Cavium ThunderX/OCTEON-TX GPIO" depends on ARCH_THUNDER || (64BIT && COMPILE_TEST) depends on PCI_MSI + select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY select IRQ_FASTEOI_HIERARCHY_HANDLERS help @@ -621,6 +628,13 @@ config GPIO_VX855 additional drivers must be enabled in order to use the functionality of the device. +config GPIO_WCD934X + tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver" + depends on MFD_WCD934X && OF_GPIO + help + This driver is to supprot GPIO block found on the Qualcomm Technologies + Inc WCD9340/WCD9341 Audio Codec. + config GPIO_XGENE bool "APM X-Gene GPIO controller support" depends on ARM64 && OF_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 11eeeebbde0d..af158b7e1a43 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_IT87) += gpio-it87.o obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o +obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o @@ -158,6 +159,7 @@ obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o +obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 76f8c7ff18ff..3a44e6ae52bd 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -10,6 +10,28 @@ approach. This means that GPIO consumers, drivers and machine descriptions ideally have no use or idea of the global GPIO numberspace that has/was used in the inception of the GPIO subsystem. +The numberspace issue is the same as to why irq is moving away from irq +numbers to IRQ descriptors. + +The underlying motivation for this is that the GPIO numberspace has become +unmanageable: machine board files tend to become full of macros trying to +establish the numberspace at compile-time, making it hard to add any numbers +in the middle (such as if you missed a pin on a chip) without the numberspace +breaking. + +Machine descriptions such as device tree or ACPI does not have a concept of the +Linux GPIO number as those descriptions are external to the Linux kernel +and treat GPIO lines as abstract entities. + +The runtime-assigned GPIO numberspace (what you get if you assign the GPIO +base as -1 in struct gpio_chip) has also became unpredictable due to factors +such as probe ordering and the introduction of -EPROBE_DEFER making probe +ordering of independent GPIO chips essentially unpredictable, as their base +number will be assigned on a first come first serve basis. + +The best way to get out of the problem is to make the global GPIO numbers +unimportant by simply not using them. GPIO descriptors deal with this. + Work items: - Convert all GPIO device drivers to only #include <linux/gpio/driver.h> @@ -33,7 +55,7 @@ This header and helpers appeared at one point when there was no proper driver infrastructure for doing simpler MMIO GPIO devices and there was no core support for parsing device tree GPIOs from the core library with the [devm_]gpiod_get() calls we have today that will implicitly go into -the device tree back-end. +the device tree back-end. It is legacy and should not be used in new code. Work items: @@ -59,6 +81,15 @@ Work items: uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead. +Get rid of <linux/gpio.h> + +This legacy header is a one stop shop for anything GPIO is closely tied +to the global GPIO numberspace. The endgame of the above refactorings will +be the removal of <linux/gpio.h> and from that point only the specialized +headers under <linux/gpio/*.h> will be used. This requires all the above to +be completed and is expected to take a long time. + + Collect drivers Collect GPIO drivers from arch/* and other places that should be placed @@ -109,7 +140,7 @@ try to cover any generic kind of irqchip cascaded from a GPIO. int irq; /* from platform etc */ struct my_gpio *g; - struct gpio_irq_chip *girq + struct gpio_irq_chip *girq; /* Set up the irqchip dynamically */ g->irq.name = "my_gpio_irq"; @@ -137,9 +168,14 @@ try to cover any generic kind of irqchip cascaded from a GPIO. - Look over and identify any remaining easily converted drivers and dry-code conversions to gpiolib irqchip for maintainers to test -- Support generic hierarchical GPIO interrupts: these are for the - non-cascading case where there is one IRQ per GPIO line, there is - currently no common infrastructure for this. +- Drop gpiochip_set_chained_irqchip() when all the chained irqchips + have been converted to the above infrastructure. + +- Add more infrastructure to make it possible to also pass a threaded + irqchip in struct gpio_irq_chip. + +- Drop gpiochip_irqchip_add_nested() when all the chained irqchips + have been converted to the above infrastructure. Increase integration with pin control diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 9f2e6b04c361..cc4ba71e4fe3 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -266,7 +266,7 @@ static int altera_gpio_probe(struct platform_device *pdev) altera_gc->mmchip.gc.owner = THIS_MODULE; altera_gc->mmchip.gc.parent = &pdev->dev; - altera_gc->mapped_irq = platform_get_irq(pdev, 0); + altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); if (altera_gc->mapped_irq < 0) goto skip_irq; diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 8319812593e3..d16645c1d8d9 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -391,7 +391,7 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, gpio->irq = rc; - /* Disable IRQ and clear Interrupt status registers for all SPGIO Pins. */ + /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { bank = &aspeed_sgpio_banks[i]; /* disable irq enable bits */ diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index f1037b61f763..879db23d8454 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -978,7 +978,7 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, } /** - * aspeed_gpio_copro_set_ops - Sets the callbacks used for handhsaking with + * aspeed_gpio_copro_set_ops - Sets the callbacks used for handshaking with * the coprocessor for shared GPIO banks * @ops: The callbacks * @data: Pointer passed back to the callbacks diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 4122683eb1f9..baee8c3f06ad 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -19,7 +19,6 @@ #include <linux/io.h> #include <linux/gpio/driver.h> #include <linux/of_device.h> -#include <linux/of_irq.h> #include <linux/init.h> #include <linux/irqdomain.h> #include <linux/irqchip/chained_irq.h> @@ -586,11 +585,18 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) kona_gpio->gpio_chip = template_chip; chip = &kona_gpio->gpio_chip; - kona_gpio->num_bank = of_irq_count(dev->of_node); - if (kona_gpio->num_bank == 0) { + ret = platform_irq_count(pdev); + if (!ret) { dev_err(dev, "Couldn't determine # GPIO banks\n"); return -ENOENT; + } else if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Couldn't determine GPIO banks: (%pe)\n", + ERR_PTR(ret)); + return ret; } + kona_gpio->num_bank = ret; + if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) { dev_err(dev, "Too many GPIO banks configured (max=%d)\n", GPIO_MAX_BANK_NUM); diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index ff19a8ad5663..1d0827e79703 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -64,11 +64,11 @@ static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg, if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8) return -EINVAL; - /* Check that on valiue fits it's placeholder */ + /* Check that on value fits its placeholder */ if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i]) return -EINVAL; - /* Check that off valiue fits it's placeholder */ + /* Check that off value fits its placeholder */ if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i]) return -EINVAL; diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 08234e64993a..f954359c9544 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -253,17 +253,16 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, lirq->irq = irq; uirq = &priv->uirqs[lirq->index]; if (uirq->refcnt == 0) { + spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, dev_name(priv->dev), priv); if (ret) { dev_err(priv->dev, "Could not request underlying irq %d\n", uirq->uirq); - - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); - return ret; } + spin_lock_irqsave(&priv->gc.bgpio_lock, flags); } uirq->refcnt++; @@ -309,8 +308,11 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) if (index >= 0) { uirq = &priv->uirqs[lirq->index]; uirq->refcnt--; - if (uirq->refcnt == 0) + if (uirq->refcnt == 0) { + spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); free_irq(uirq->uirq, priv); + return; + } } spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); @@ -433,12 +435,9 @@ static int grgpio_probe(struct platform_device *ofdev) static int grgpio_remove(struct platform_device *ofdev) { struct grgpio_priv *priv = platform_get_drvdata(ofdev); - unsigned long flags; int i; int ret = 0; - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); - if (priv->domain) { for (i = 0; i < GRGPIO_MAX_NGPIO; i++) { if (priv->uirqs[i].refcnt != 0) { @@ -454,8 +453,6 @@ static int grgpio_remove(struct platform_device *ofdev) irq_domain_remove(priv->domain); out: - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); - return ret; } diff --git a/drivers/gpio/gpio-logicvc.c b/drivers/gpio/gpio-logicvc.c new file mode 100644 index 000000000000..015632cf159f --- /dev/null +++ b/drivers/gpio/gpio-logicvc.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#define LOGICVC_CTRL_REG 0x40 +#define LOGICVC_CTRL_GPIO_SHIFT 11 +#define LOGICVC_CTRL_GPIO_BITS 5 + +#define LOGICVC_POWER_CTRL_REG 0x78 +#define LOGICVC_POWER_CTRL_GPIO_SHIFT 0 +#define LOGICVC_POWER_CTRL_GPIO_BITS 4 + +struct logicvc_gpio { + struct gpio_chip chip; + struct regmap *regmap; +}; + +static void logicvc_gpio_offset(struct logicvc_gpio *logicvc, unsigned offset, + unsigned int *reg, unsigned int *bit) +{ + if (offset >= LOGICVC_CTRL_GPIO_BITS) { + *reg = LOGICVC_POWER_CTRL_REG; + + /* To the (virtual) power ctrl offset. */ + offset -= LOGICVC_CTRL_GPIO_BITS; + /* To the actual bit offset in reg. */ + offset += LOGICVC_POWER_CTRL_GPIO_SHIFT; + } else { + *reg = LOGICVC_CTRL_REG; + + /* To the actual bit offset in reg. */ + offset += LOGICVC_CTRL_GPIO_SHIFT; + } + + *bit = BIT(offset); +} + +static int logicvc_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct logicvc_gpio *logicvc = gpiochip_get_data(chip); + unsigned int reg, bit, value; + int ret; + + logicvc_gpio_offset(logicvc, offset, ®, &bit); + + ret = regmap_read(logicvc->regmap, reg, &value); + if (ret) + return ret; + + return !!(value & bit); +} + +static void logicvc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct logicvc_gpio *logicvc = gpiochip_get_data(chip); + unsigned int reg, bit; + + logicvc_gpio_offset(logicvc, offset, ®, &bit); + + regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0); +} + +static int logicvc_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + /* Pins are always configured as output, so just set the value. */ + logicvc_gpio_set(chip, offset, value); + + return 0; +} + +static struct regmap_config logicvc_gpio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .name = "logicvc-gpio", +}; + +static int logicvc_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *of_node = dev->of_node; + struct logicvc_gpio *logicvc; + int ret; + + logicvc = devm_kzalloc(dev, sizeof(*logicvc), GFP_KERNEL); + if (!logicvc) + return -ENOMEM; + + /* Try to get regmap from parent first. */ + logicvc->regmap = syscon_node_to_regmap(of_node->parent); + + /* Grab our own regmap if that fails. */ + if (IS_ERR(logicvc->regmap)) { + struct resource res; + void __iomem *base; + + ret = of_address_to_resource(of_node, 0, &res); + if (ret) { + dev_err(dev, "Failed to get resource from address\n"); + return ret; + } + + base = devm_ioremap_resource(dev, &res); + if (IS_ERR(base)) { + dev_err(dev, "Failed to map I/O base\n"); + return PTR_ERR(base); + } + + logicvc_gpio_regmap_config.max_register = resource_size(&res) - + logicvc_gpio_regmap_config.reg_stride; + + logicvc->regmap = + devm_regmap_init_mmio(dev, base, + &logicvc_gpio_regmap_config); + if (IS_ERR(logicvc->regmap)) { + dev_err(dev, "Failed to create regmap for I/O\n"); + return PTR_ERR(logicvc->regmap); + } + } + + logicvc->chip.parent = dev; + logicvc->chip.owner = THIS_MODULE; + logicvc->chip.label = dev_name(dev); + logicvc->chip.base = -1; + logicvc->chip.ngpio = LOGICVC_CTRL_GPIO_BITS + + LOGICVC_POWER_CTRL_GPIO_BITS; + logicvc->chip.get = logicvc_gpio_get; + logicvc->chip.set = logicvc_gpio_set; + logicvc->chip.direction_output = logicvc_gpio_direction_output; + + platform_set_drvdata(pdev, logicvc); + + return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc); +} + +static const struct of_device_id logicivc_gpio_of_table[] = { + { + .compatible = "xylon,logicvc-3.02.a-gpio", + }, + { } +}; + +MODULE_DEVICE_TABLE(of, logicivc_gpio_of_table); + +static struct platform_driver logicvc_gpio_driver = { + .driver = { + .name = "gpio-logicvc", + .of_match_table = logicivc_gpio_of_table, + }, + .probe = logicvc_gpio_probe, +}; + +module_platform_driver(logicvc_gpio_driver); + +MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); +MODULE_DESCRIPTION("Xylon LogiCVC GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 94b8d3ae27bc..7d343bea784a 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * GPIO Testing Device Driver * @@ -7,18 +7,18 @@ * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl> */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/gpio/driver.h> +#include <linux/debugfs.h> #include <linux/gpio/consumer.h> -#include <linux/platform_device.h> -#include <linux/slab.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irq_sim.h> -#include <linux/debugfs.h> -#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include <linux/property.h> +#include <linux/slab.h> +#include <linux/uaccess.h> #include "gpiolib.h" diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 5ae30de3490a..604dfec353a1 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -296,6 +296,7 @@ static const struct mpc8xxx_gpio_devtype mpc512x_gpio_devtype = { static const struct mpc8xxx_gpio_devtype ls1028a_gpio_devtype = { .gpio_dir_in_init = ls1028a_gpio_dir_in_init, + .irq_set_type = mpc8xxx_irq_set_type, }; static const struct mpc8xxx_gpio_devtype mpc5125_gpio_devtype = { diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 993bbeb3c006..d2b999c7987f 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -46,7 +46,6 @@ #include <linux/irqdomain.h> #include <linux/mfd/syscon.h> #include <linux/of_device.h> -#include <linux/of_irq.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -432,6 +431,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) u32 mask = d->mask; irq_gc_lock(gc); + mvebu_gpio_write_edge_cause(mvchip, ~mask); ct->mask_cache_priv |= mask; mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); @@ -1102,7 +1102,11 @@ static int mvebu_gpio_probe(struct platform_device *pdev) soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION; /* Some gpio controllers do not provide irq support */ - have_irqs = of_irq_count(np) != 0; + err = platform_irq_count(pdev); + if (err < 0) + return err; + + have_irqs = err != 0; mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL); diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 9853547e7276..5638b4e5355f 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -764,8 +764,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, pca953x_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT | - IRQF_SHARED, + IRQF_ONESHOT | IRQF_SHARED, dev_name(&client->dev), chip); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", @@ -855,8 +854,6 @@ out: return ret; } -static const struct of_device_id pca953x_dt_ids[]; - static int pca953x_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index b04c561f3280..4d47b2c41186 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -244,7 +244,6 @@ static struct platform_driver sama5d2_piobu_driver = { module_platform_driver(sama5d2_piobu_driver); -MODULE_VERSION("1.0"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver"); MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>"); diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 5e375186f90e..866201cf5f65 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -243,4 +243,3 @@ static struct platform_driver tb10x_gpio_driver = { module_platform_driver(tb10x_gpio_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("tb10x gpio."); -MODULE_VERSION("0.0.1"); diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 6fdfe4c5303e..acb99eff9939 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -96,12 +96,12 @@ struct tegra_gpio_info { static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, u32 val, u32 reg) { - __raw_writel(val, tgi->regs + reg); + writel_relaxed(val, tgi->regs + reg); } static inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg) { - return __raw_readl(tgi->regs + reg); + return readl_relaxed(tgi->regs + reg); } static unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port, @@ -416,11 +416,8 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) static int tegra_gpio_resume(struct device *dev) { struct tegra_gpio_info *tgi = dev_get_drvdata(dev); - unsigned long flags; unsigned int b, p; - local_irq_save(flags); - for (b = 0; b < tgi->bank_count; b++) { struct tegra_gpio_bank *bank = &tgi->bank_info[b]; @@ -448,17 +445,14 @@ static int tegra_gpio_resume(struct device *dev) } } - local_irq_restore(flags); return 0; } static int tegra_gpio_suspend(struct device *dev) { struct tegra_gpio_info *tgi = dev_get_drvdata(dev); - unsigned long flags; unsigned int b, p; - local_irq_save(flags); for (b = 0; b < tgi->bank_count; b++) { struct tegra_gpio_bank *bank = &tgi->bank_info[b]; @@ -488,7 +482,7 @@ static int tegra_gpio_suspend(struct device *dev) GPIO_INT_ENB(tgi, gpio)); } } - local_irq_restore(flags); + return 0; } @@ -497,6 +491,11 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); unsigned int gpio = d->hwirq; u32 port, bit, mask; + int err; + + err = irq_set_irq_wake(bank->irq, enable); + if (err) + return err; port = GPIO_PORT(gpio); bit = GPIO_BIT(gpio); @@ -507,7 +506,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) else bank->wake_enb[port] &= ~mask; - return irq_set_irq_wake(bank->irq, enable); + return 0; } #endif @@ -557,7 +556,7 @@ static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) #endif static const struct dev_pm_ops tegra_gpio_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) }; static int tegra_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 55b43b7ce88d..de241263d4be 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -448,17 +448,24 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain, return 0; } -static void tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { struct tegra_gpio *gpio = gpiochip_get_data(chip); + struct irq_fwspec *fwspec; + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->param_count = 3; fwspec->param[0] = gpio->soc->instance; fwspec->param[1] = parent_hwirq; fwspec->param[2] = parent_type; + + return fwspec; } static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip, @@ -621,7 +628,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->chip = &gpio->intc; irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq; - irq->populate_parent_fwspec = tegra186_gpio_populate_parent_fwspec; + irq->populate_parent_alloc_arg = tegra186_gpio_populate_parent_fwspec; irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq; irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate; irq->handler = handle_simple_irq; diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 462770479045..9f66deab46ea 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/spinlock.h> +#include <asm-generic/msi.h> #define GPIO_RX_DAT 0x0 @@ -53,7 +54,6 @@ struct thunderx_line { struct thunderx_gpio { struct gpio_chip chip; u8 __iomem *register_base; - struct irq_domain *irqd; struct msix_entry *msix_entries; /* per line MSI-X */ struct thunderx_line *line_entries; /* per line irq info */ raw_spinlock_t lock; @@ -286,54 +286,60 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, } } -static void thunderx_gpio_irq_ack(struct irq_data *data) +static void thunderx_gpio_irq_ack(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_INTR, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_mask(struct irq_data *data) +static void thunderx_gpio_irq_mask(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1C, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_mask_ack(struct irq_data *data) +static void thunderx_gpio_irq_mask_ack(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_unmask(struct irq_data *data) +static void thunderx_gpio_irq_unmask(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1S, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static int thunderx_gpio_irq_set_type(struct irq_data *data, +static int thunderx_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); + struct thunderx_line *txline = + &txgpio->line_entries[irqd_to_hwirq(d)]; u64 bit_cfg; - irqd_set_trigger_type(data, flow_type); + irqd_set_trigger_type(d, flow_type); bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN; if (flow_type & IRQ_TYPE_EDGE_BOTH) { - irq_set_handler_locked(data, handle_fasteoi_ack_irq); + irq_set_handler_locked(d, handle_fasteoi_ack_irq); bit_cfg |= GPIO_BIT_CFG_INT_TYPE; } else { - irq_set_handler_locked(data, handle_fasteoi_mask_irq); + irq_set_handler_locked(d, handle_fasteoi_mask_irq); } raw_spin_lock(&txgpio->lock); @@ -362,33 +368,6 @@ static void thunderx_gpio_irq_disable(struct irq_data *data) irq_chip_disable_parent(data); } -static int thunderx_gpio_irq_request_resources(struct irq_data *data) -{ - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; - int r; - - r = gpiochip_lock_as_irq(&txgpio->chip, txline->line); - if (r) - return r; - - r = irq_chip_request_resources_parent(data); - if (r) - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); - - return r; -} - -static void thunderx_gpio_irq_release_resources(struct irq_data *data) -{ - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; - - irq_chip_release_resources_parent(data); - - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); -} - /* * Interrupts are chained from underlying MSI-X vectors. We have * these irq_chip functions to be able to handle level triggering @@ -405,48 +384,42 @@ static struct irq_chip thunderx_gpio_irq_chip = { .irq_unmask = thunderx_gpio_irq_unmask, .irq_eoi = irq_chip_eoi_parent, .irq_set_affinity = irq_chip_set_affinity_parent, - .irq_request_resources = thunderx_gpio_irq_request_resources, - .irq_release_resources = thunderx_gpio_irq_release_resources, .irq_set_type = thunderx_gpio_irq_set_type, .flags = IRQCHIP_SET_TYPE_MASKED }; -static int thunderx_gpio_irq_translate(struct irq_domain *d, - struct irq_fwspec *fwspec, - irq_hw_number_t *hwirq, - unsigned int *type) +static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) { - struct thunderx_gpio *txgpio = d->host_data; + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); + struct irq_data *irqd; + unsigned int irq; - if (WARN_ON(fwspec->param_count < 2)) + irq = txgpio->msix_entries[child].vector; + irqd = irq_domain_get_irq_data(gc->irq.parent_domain, irq); + if (!irqd) return -EINVAL; - if (fwspec->param[0] >= txgpio->chip.ngpio) - return -EINVAL; - *hwirq = fwspec->param[0]; - *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + *parent = irqd_to_hwirq(irqd); + *parent_type = IRQ_TYPE_LEVEL_HIGH; return 0; } -static int thunderx_gpio_irq_alloc(struct irq_domain *d, unsigned int virq, - unsigned int nr_irqs, void *arg) +static void *thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip, + unsigned int parent_hwirq, + unsigned int parent_type) { - struct thunderx_line *txline = arg; - - return irq_domain_set_hwirq_and_chip(d, virq, txline->line, - &thunderx_gpio_irq_chip, txline); -} - -static const struct irq_domain_ops thunderx_gpio_irqd_ops = { - .alloc = thunderx_gpio_irq_alloc, - .translate = thunderx_gpio_irq_translate -}; + msi_alloc_info_t *info; -static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) -{ - struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; - return irq_find_mapping(txgpio->irqd, offset); + info->hwirq = parent_hwirq; + return info; } static int thunderx_gpio_probe(struct pci_dev *pdev, @@ -456,6 +429,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, struct device *dev = &pdev->dev; struct thunderx_gpio *txgpio; struct gpio_chip *chip; + struct gpio_irq_chip *girq; int ngpio, i; int err = 0; @@ -500,8 +474,8 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, } txgpio->msix_entries = devm_kcalloc(dev, - ngpio, sizeof(struct msix_entry), - GFP_KERNEL); + ngpio, sizeof(struct msix_entry), + GFP_KERNEL); if (!txgpio->msix_entries) { err = -ENOMEM; goto out; @@ -542,27 +516,6 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, if (err < 0) goto out; - /* - * Push GPIO specific irqdomain on hierarchy created as a side - * effect of the pci_enable_msix() - */ - txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, - 0, 0, of_node_to_fwnode(dev->of_node), - &thunderx_gpio_irqd_ops, txgpio); - if (!txgpio->irqd) { - err = -ENOMEM; - goto out; - } - - /* Push on irq_data and the domain for each line. */ - for (i = 0; i < ngpio; i++) { - err = irq_domain_push_irq(txgpio->irqd, - txgpio->msix_entries[i].vector, - &txgpio->line_entries[i]); - if (err < 0) - dev_err(dev, "irq_domain_push_irq: %d\n", err); - } - chip->label = KBUILD_MODNAME; chip->parent = dev; chip->owner = THIS_MODULE; @@ -577,11 +530,35 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, chip->set = thunderx_gpio_set; chip->set_multiple = thunderx_gpio_set_multiple; chip->set_config = thunderx_gpio_set_config; - chip->to_irq = thunderx_gpio_to_irq; + girq = &chip->irq; + girq->chip = &thunderx_gpio_irq_chip; + girq->fwnode = of_node_to_fwnode(dev->of_node); + girq->parent_domain = + irq_get_irq_data(txgpio->msix_entries[0].vector)->domain; + girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq; + girq->populate_parent_alloc_arg = thunderx_gpio_populate_parent_alloc_info; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + err = devm_gpiochip_add_data(dev, chip, txgpio); if (err) goto out; + /* Push on irq_data and the domain for each line. */ + for (i = 0; i < ngpio; i++) { + struct irq_fwspec fwspec; + + fwspec.fwnode = of_node_to_fwnode(dev->of_node); + fwspec.param_count = 2; + fwspec.param[0] = i; + fwspec.param[1] = IRQ_TYPE_NONE; + err = irq_domain_push_irq(girq->domain, + txgpio->msix_entries[i].vector, + &fwspec); + if (err < 0) + dev_err(dev, "irq_domain_push_irq: %d\n", err); + } + dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n", ngpio, chip->base); return 0; @@ -596,10 +573,10 @@ static void thunderx_gpio_remove(struct pci_dev *pdev) struct thunderx_gpio *txgpio = pci_get_drvdata(pdev); for (i = 0; i < txgpio->chip.ngpio; i++) - irq_domain_pop_irq(txgpio->irqd, + irq_domain_pop_irq(txgpio->chip.irq.domain, txgpio->msix_entries[i].vector); - irq_domain_remove(txgpio->irqd); + irq_domain_remove(txgpio->chip.irq.domain); pci_set_drvdata(pdev, NULL); } diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 4ff146ca32fe..3bf397b8dfbc 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -71,7 +71,7 @@ static inline u_int32_t gpio_o_bit(int i) return 1 << (i + 13); } -/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering: +/* Mapping between numeric GPIO ID and the actual GPIO hardware numbering: * 0..13 GPI 0..13 * 14..26 GPO 0..12 * 27..41 GPIO 0..14 diff --git a/drivers/gpio/gpio-wcd934x.c b/drivers/gpio/gpio-wcd934x.c new file mode 100644 index 000000000000..74913f2e5697 --- /dev/null +++ b/drivers/gpio/gpio-wcd934x.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019, Linaro Limited + +#include <linux/module.h> +#include <linux/gpio/driver.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/of_device.h> + +#define WCD_PIN_MASK(p) BIT(p - 1) +#define WCD_REG_DIR_CTL_OFFSET 0x42 +#define WCD_REG_VAL_CTL_OFFSET 0x43 +#define WCD934X_NPINS 5 + +struct wcd_gpio_data { + struct regmap *map; + struct gpio_chip chip; +}; + +static int wcd_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_data *data = gpiochip_get_data(chip); + unsigned int value; + int ret; + + ret = regmap_read(data->map, WCD_REG_DIR_CTL_OFFSET, &value); + if (ret < 0) + return ret; + + if (value & WCD_PIN_MASK(pin)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_data *data = gpiochip_get_data(chip); + + return regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, + WCD_PIN_MASK(pin), 0); +} + +static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, + int val) +{ + struct wcd_gpio_data *data = gpiochip_get_data(chip); + + regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, + WCD_PIN_MASK(pin), WCD_PIN_MASK(pin)); + + return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, + WCD_PIN_MASK(pin), + val ? WCD_PIN_MASK(pin) : 0); +} + +static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_data *data = gpiochip_get_data(chip); + int value; + + regmap_read(data->map, WCD_REG_VAL_CTL_OFFSET, &value); + + return !!(value && WCD_PIN_MASK(pin)); +} + +static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val) +{ + wcd_gpio_direction_output(chip, pin, val); +} + +static int wcd_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd_gpio_data *data; + struct gpio_chip *chip; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->map = dev_get_regmap(dev->parent, NULL); + if (!data->map) { + dev_err(dev, "%s: failed to get regmap\n", __func__); + return -EINVAL; + } + + chip = &data->chip; + chip->direction_input = wcd_gpio_direction_input; + chip->direction_output = wcd_gpio_direction_output; + chip->get_direction = wcd_gpio_get_direction; + chip->get = wcd_gpio_get; + chip->set = wcd_gpio_set; + chip->parent = dev; + chip->base = -1; + chip->ngpio = WCD934X_NPINS; + chip->label = dev_name(dev); + chip->of_gpio_n_cells = 2; + chip->can_sleep = false; + + return devm_gpiochip_add_data(dev, chip, data); +} + +static const struct of_device_id wcd_gpio_of_match[] = { + { .compatible = "qcom,wcd9340-gpio" }, + { .compatible = "qcom,wcd9341-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd_gpio_of_match); + +static struct platform_driver wcd_gpio_driver = { + .driver = { + .name = "wcd934x-gpio", + .of_match_table = wcd_gpio_of_match, + }, + .probe = wcd_gpio_probe, +}; + +module_platform_driver(wcd_gpio_driver); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index fbf6b1a0a4fa..23e3d335cd54 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -762,10 +762,9 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) parent = &gdev->dev; /* use chip->base for the ID; it's already known to be unique */ - dev = device_create_with_groups(&gpio_class, parent, - MKDEV(0, 0), - chip, gpiochip_groups, - "gpiochip%d", chip->base); + dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip, + gpiochip_groups, GPIOCHIP_NAME "%d", + chip->base); if (IS_ERR(dev)) return PTR_ERR(dev); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bcfbfded9ba3..9d9ba56cb73d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -140,7 +140,7 @@ EXPORT_SYMBOL_GPL(gpio_to_desc); * in the given chip for the specified hardware number. */ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, - u16 hwnum) + unsigned int hwnum) { struct gpio_device *gdev = chip->gpiodev; @@ -232,15 +232,15 @@ int gpiod_get_direction(struct gpio_desc *desc) return -ENOTSUPP; ret = chip->get_direction(chip, offset); - if (ret > 0) { - /* GPIOF_DIR_IN, or other positive */ + if (ret < 0) + return ret; + + /* GPIOF_DIR_IN or other positive, otherwise GPIOF_DIR_OUT */ + if (ret > 0) ret = 1; - clear_bit(FLAG_IS_OUT, &desc->flags); - } - if (ret == 0) { - /* GPIOF_DIR_OUT */ - set_bit(FLAG_IS_OUT, &desc->flags); - } + + assign_bit(FLAG_IS_OUT, &desc->flags, !ret); + return ret; } EXPORT_SYMBOL_GPL(gpiod_get_direction); @@ -492,15 +492,6 @@ static int linehandle_validate_flags(u32 flags) return 0; } -static void linehandle_configure_flag(unsigned long *flagsp, - u32 bit, bool active) -{ - if (active) - set_bit(bit, flagsp); - else - clear_bit(bit, flagsp); -} - static long linehandle_set_config(struct linehandle_state *lh, void __user *ip) { @@ -522,22 +513,22 @@ static long linehandle_set_config(struct linehandle_state *lh, desc = lh->descs[i]; flagsp = &desc->flags; - linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW, + assign_bit(FLAG_ACTIVE_LOW, flagsp, lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); - linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN, + assign_bit(FLAG_OPEN_DRAIN, flagsp, lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); - linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE, + assign_bit(FLAG_OPEN_SOURCE, flagsp, lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); - linehandle_configure_flag(flagsp, FLAG_PULL_UP, + assign_bit(FLAG_PULL_UP, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); - linehandle_configure_flag(flagsp, FLAG_PULL_DOWN, + assign_bit(FLAG_PULL_DOWN, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); - linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE, + assign_bit(FLAG_BIAS_DISABLE, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); /* @@ -686,14 +677,13 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < handlereq.lines; i++) { u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc; + struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); - if (offset >= gdev->ngpio) { - ret = -EINVAL; + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); goto out_free_descs; } - desc = &gdev->descs[offset]; ret = gpiod_request(desc, lh->label); if (ret) goto out_free_descs; @@ -1018,8 +1008,9 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) lflags = eventreq.handleflags; eflags = eventreq.eventflags; - if (offset >= gdev->ngpio) - return -EINVAL; + desc = gpiochip_get_desc(gdev->chip, offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); /* Return an error if a unknown flag is set */ if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || @@ -1057,7 +1048,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) } } - desc = &gdev->descs[offset]; ret = gpiod_request(desc, le->label); if (ret) goto out_free_label; @@ -1184,10 +1174,11 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; - if (lineinfo.line_offset >= gdev->ngpio) - return -EINVAL; - desc = &gdev->descs[lineinfo.line_offset]; + desc = gpiochip_get_desc(chip, lineinfo.line_offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + if (desc->name) { strncpy(lineinfo.name, desc->name, sizeof(lineinfo.name)); @@ -1427,7 +1418,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, ret = gdev->id; goto err_free_gdev; } - dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); + dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); device_initialize(&gdev->dev); dev_set_drvdata(&gdev->dev, gdev); if (chip->parent && chip->parent->driver) @@ -1452,7 +1443,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, if (chip->ngpio > FASTPATH_NGPIO) chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n", - chip->ngpio, FASTPATH_NGPIO); + chip->ngpio, FASTPATH_NGPIO); gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); if (!gdev->label) { @@ -1495,11 +1486,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, goto err_free_label; } - spin_unlock_irqrestore(&gpio_lock, flags); - for (i = 0; i < chip->ngpio; i++) gdev->descs[i].gdev = gdev; + spin_unlock_irqrestore(&gpio_lock, flags); + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -1524,15 +1515,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, struct gpio_desc *desc = &gdev->descs[i]; if (chip->get_direction && gpiochip_line_is_valid(chip, i)) { - if (!chip->get_direction(chip, i)) - set_bit(FLAG_IS_OUT, &desc->flags); - else - clear_bit(FLAG_IS_OUT, &desc->flags); + assign_bit(FLAG_IS_OUT, + &desc->flags, !chip->get_direction(chip, i)); } else { - if (!chip->direction_input) - set_bit(FLAG_IS_OUT, &desc->flags); - else - clear_bit(FLAG_IS_OUT, &desc->flags); + assign_bit(FLAG_IS_OUT, + &desc->flags, !chip->direction_input); } } @@ -2003,7 +1990,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; struct irq_fwspec *fwspec = data; - struct irq_fwspec parent_fwspec; + void *parent_arg; unsigned int parent_hwirq; unsigned int parent_type; struct gpio_irq_chip *girq = &gc->irq; @@ -2019,7 +2006,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, if (ret) return ret; - chip_info(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); + chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); ret = girq->child_to_parent_hwirq(gc, hwirq, type, &parent_hwirq, &parent_type); @@ -2027,7 +2014,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, chip_err(gc, "can't look up hwirq %lu\n", hwirq); return ret; } - chip_info(gc, "found parent hwirq %u\n", parent_hwirq); + chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq); /* * We set handle_bad_irq because the .set_type() should @@ -2042,23 +2029,27 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, NULL, NULL); irq_set_probe(irq); - /* - * Create a IRQ fwspec to send up to the parent irqdomain: - * specify the hwirq we address on the parent and tie it - * all together up the chain. - */ - parent_fwspec.fwnode = d->parent->fwnode; /* This parent only handles asserted level IRQs */ - girq->populate_parent_fwspec(gc, &parent_fwspec, parent_hwirq, - parent_type); - chip_info(gc, "alloc_irqs_parent for %d parent hwirq %d\n", + parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq, parent_type); + if (!parent_arg) + return -ENOMEM; + + chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", irq, parent_hwirq); - ret = irq_domain_alloc_irqs_parent(d, irq, 1, &parent_fwspec); + irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key); + ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg); + /* + * If the parent irqdomain is msi, the interrupts have already + * been allocated, so the EEXIST is good. + */ + if (irq_domain_is_msi(d->parent) && (ret == -EEXIST)) + ret = 0; if (ret) chip_err(gc, "failed to allocate parent hwirq %d for hwirq %lu\n", parent_hwirq, hwirq); + kfree(parent_arg); return ret; } @@ -2095,8 +2086,8 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) if (!gc->irq.child_offset_to_irq) gc->irq.child_offset_to_irq = gpiochip_child_offset_to_irq_noop; - if (!gc->irq.populate_parent_fwspec) - gc->irq.populate_parent_fwspec = + if (!gc->irq.populate_parent_alloc_arg) + gc->irq.populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops); @@ -2122,27 +2113,43 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) return !!gc->irq.parent_domain; } -void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->param_count = 2; fwspec->param[0] = parent_hwirq; fwspec->param[1] = parent_type; + + return fwspec; } EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell); -void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->param_count = 4; fwspec->param[0] = 0; fwspec->param[1] = parent_hwirq; fwspec->param[2] = 0; fwspec->param[3] = parent_type; + + return fwspec; } EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell); @@ -2998,7 +3005,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); * A pointer to the GPIO descriptor, or an ERR_PTR()-encoded negative error * code on failure. */ -struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, +struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, + unsigned int hwnum, const char *label, enum gpio_lookup_flags lflags, enum gpiod_flags dflags) @@ -3050,25 +3058,13 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); * rely on gpio_request() having been called beforehand. */ -static int gpio_set_config(struct gpio_chip *gc, unsigned offset, +static int gpio_set_config(struct gpio_chip *gc, unsigned int offset, enum pin_config_param mode) { - unsigned long config; - unsigned arg; - - switch (mode) { - case PIN_CONFIG_BIAS_DISABLE: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_UP: - arg = 1; - break; - - default: - arg = 0; - } + if (!gc->set_config) + return -ENOTSUPP; - config = PIN_CONF_PACKED(mode, arg); - return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; + return gc->set_config(gc, offset, mode); } static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc) @@ -3302,15 +3298,9 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) VALIDATE_DESC(desc); chip = desc->gdev->chip; - if (!chip->set || !chip->set_config) { - gpiod_dbg(desc, - "%s: missing set() or set_config() operations\n", - __func__); - return -ENOTSUPP; - } config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); - return chip->set_config(chip, gpio_chip_hwgpio(desc), config); + return gpio_set_config(chip, gpio_chip_hwgpio(desc), config); } EXPORT_SYMBOL_GPL(gpiod_set_debounce); @@ -3334,10 +3324,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for * persistence state. */ - if (transitory) - set_bit(FLAG_TRANSITORY, &desc->flags); - else - clear_bit(FLAG_TRANSITORY, &desc->flags); + assign_bit(FLAG_TRANSITORY, &desc->flags, transitory); /* If the driver supports it, set the persistence state now */ chip = desc->gdev->chip; @@ -3347,7 +3334,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, !transitory); gpio = gpio_chip_hwgpio(desc); - rc = chip->set_config(chip, gpio, packed); + rc = gpio_set_config(chip, gpio, packed); if (rc == -ENOTSUPP) { dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", gpio); @@ -3804,10 +3791,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, gpio_set_open_source_value_commit(desc, value); } else { __set_bit(hwgpio, mask); - if (value) - __set_bit(hwgpio, bits); - else - __clear_bit(hwgpio, bits); + __assign_bit(hwgpio, bits, value); count++; } i++; @@ -5124,7 +5108,7 @@ static int __init gpiolib_dev_init(void) return ret; } - ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip"); + ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME); if (ret < 0) { pr_err("gpiolib: failed to allocate char dev region\n"); bus_unregister(&gpio_bus_type); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index ca9bc1e4803c..3e0aab2945d8 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -16,6 +16,8 @@ #include <linux/module.h> #include <linux/cdev.h> +#define GPIOCHIP_NAME "gpiochip" + /** * struct gpio_device - internal state container for GPIO devices * @id: numerical ID number for the GPIO chip @@ -78,7 +80,8 @@ struct gpio_array { unsigned long invert_mask[]; }; -struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); +struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, + unsigned int hwnum); int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index 653d1095bfea..fe0be8a6ebb7 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -1060,7 +1060,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) girq->fwnode = of_node_to_fwnode(state->dev->of_node); girq->parent_domain = parent_domain; girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq; - girq->populate_parent_fwspec = gpiochip_populate_parent_fwspec_fourcell; + girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_fourcell; girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq; girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate; diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index dca86886b1f9..73d986a903f1 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -794,7 +794,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) girq->fwnode = of_node_to_fwnode(pctrl->dev->of_node); girq->parent_domain = parent_domain; girq->child_to_parent_hwirq = pm8xxx_child_to_parent_hwirq; - girq->populate_parent_fwspec = gpiochip_populate_parent_fwspec_fourcell; + girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_fourcell; girq->child_offset_to_irq = pm8xxx_child_offset_to_irq; girq->child_irq_domain_ops.translate = pm8xxx_domain_translate; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index e2480ef94c55..18350f274905 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -94,16 +94,15 @@ struct gpio_irq_chip { unsigned int *parent_type); /** - * @populate_parent_fwspec: + * @populate_parent_alloc_arg : * - * This optional callback populates the &struct irq_fwspec for the - * parent's IRQ domain. If this is not specified, then + * This optional callback allocates and populates the specific struct + * for the parent's IRQ domain. If this is not specified, then * &gpiochip_populate_parent_fwspec_twocell will be used. A four-cell * variant named &gpiochip_populate_parent_fwspec_fourcell is also * available. */ - void (*populate_parent_fwspec)(struct gpio_chip *chip, - struct irq_fwspec *fwspec, + void *(*populate_parent_alloc_arg)(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type); @@ -537,29 +536,27 @@ struct bgpio_pdata { #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type); -void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type); #else -static inline void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +static inline void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { + return NULL; } -static inline void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +static inline void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { + return NULL; } #endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ @@ -715,7 +712,8 @@ gpiochip_remove_pin_ranges(struct gpio_chip *chip) #endif /* CONFIG_PINCTRL */ -struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, +struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, + unsigned int hwnum, const char *label, enum gpio_lookup_flags lflags, enum gpiod_flags dflags); diff --git a/include/linux/ipmi-fru.h b/include/linux/ipmi-fru.h deleted file mode 100644 index 05c9422624c6..000000000000 --- a/include/linux/ipmi-fru.h +++ /dev/null @@ -1,134 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2012 CERN (www.cern.ch) - * Author: Alessandro Rubini <rubini@gnudd.com> - * - * This work is part of the White Rabbit project, a research effort led - * by CERN, the European Institute for Nuclear Research. - */ -#ifndef __LINUX_IPMI_FRU_H__ -#define __LINUX_IPMI_FRU_H__ -#ifdef __KERNEL__ -# include <linux/types.h> -# include <linux/string.h> -#else -# include <stdint.h> -# include <string.h> -#endif - -/* - * These structures match the unaligned crap we have in FRU1011.pdf - * (http://download.intel.com/design/servers/ipmi/FRU1011.pdf) - */ - -/* chapter 8, page 5 */ -struct fru_common_header { - uint8_t format; /* 0x01 */ - uint8_t internal_use_off; /* multiple of 8 bytes */ - uint8_t chassis_info_off; /* multiple of 8 bytes */ - uint8_t board_area_off; /* multiple of 8 bytes */ - uint8_t product_area_off; /* multiple of 8 bytes */ - uint8_t multirecord_off; /* multiple of 8 bytes */ - uint8_t pad; /* must be 0 */ - uint8_t checksum; /* sum modulo 256 must be 0 */ -}; - -/* chapter 9, page 5 -- internal_use: not used by us */ - -/* chapter 10, page 6 -- chassis info: not used by us */ - -/* chapter 13, page 9 -- used by board_info_area below */ -struct fru_type_length { - uint8_t type_length; - uint8_t data[0]; -}; - -/* chapter 11, page 7 */ -struct fru_board_info_area { - uint8_t format; /* 0x01 */ - uint8_t area_len; /* multiple of 8 bytes */ - uint8_t language; /* I hope it's 0 */ - uint8_t mfg_date[3]; /* LSB, minutes since 1996-01-01 */ - struct fru_type_length tl[0]; /* type-length stuff follows */ - - /* - * the TL there are in order: - * Board Manufacturer - * Board Product Name - * Board Serial Number - * Board Part Number - * FRU File ID (may be null) - * more manufacturer-specific stuff - * 0xc1 as a terminator - * 0x00 pad to a multiple of 8 bytes - 1 - * checksum (sum of all stuff module 256 must be zero) - */ -}; - -enum fru_type { - FRU_TYPE_BINARY = 0x00, - FRU_TYPE_BCDPLUS = 0x40, - FRU_TYPE_ASCII6 = 0x80, - FRU_TYPE_ASCII = 0xc0, /* not ascii: depends on language */ -}; - -/* - * some helpers - */ -static inline struct fru_board_info_area *fru_get_board_area( - const struct fru_common_header *header) -{ - /* we know for sure that the header is 8 bytes in size */ - return (struct fru_board_info_area *)(header + header->board_area_off); -} - -static inline int fru_type(struct fru_type_length *tl) -{ - return tl->type_length & 0xc0; -} - -static inline int fru_length(struct fru_type_length *tl) -{ - return (tl->type_length & 0x3f) + 1; /* len of whole record */ -} - -/* assume ascii-latin1 encoding */ -static inline int fru_strlen(struct fru_type_length *tl) -{ - return fru_length(tl) - 1; -} - -static inline char *fru_strcpy(char *dest, struct fru_type_length *tl) -{ - int len = fru_strlen(tl); - memcpy(dest, tl->data, len); - dest[len] = '\0'; - return dest; -} - -static inline struct fru_type_length *fru_next_tl(struct fru_type_length *tl) -{ - return tl + fru_length(tl); -} - -static inline int fru_is_eof(struct fru_type_length *tl) -{ - return tl->type_length == 0xc1; -} - -/* - * External functions defined in fru-parse.c. - */ -extern int fru_header_cksum_ok(struct fru_common_header *header); -extern int fru_bia_cksum_ok(struct fru_board_info_area *bia); - -/* All these 4 return allocated strings by calling fru_alloc() */ -extern char *fru_get_board_manufacturer(struct fru_common_header *header); -extern char *fru_get_product_name(struct fru_common_header *header); -extern char *fru_get_serial_number(struct fru_common_header *header); -extern char *fru_get_part_number(struct fru_common_header *header); - -/* This must be defined by the caller of the above functions */ -extern void *fru_alloc(size_t size); - -#endif /* __LINUX_IMPI_FRU_H__ */ |