mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
This is the bulk of GPIO changes for the v5.6 kernel cycle
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. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEElDRnuGcz/wPCXQWMQRCzN7AZXXMFAl4xO9gACgkQQRCzN7AZ XXO+lBAAv+viQVCj1IG6ajCWpsAECHY+U3xRl4ETy86Jx2uNJS48xmnYrjbqUH+h r9HDi1Z5pyc14PtOEi01qVt87z612VmZbYNZ7tVBMXsGhN18wHRtC1y8GDtRSOxj Zqeyu6zFn2WxGTbwGdjxeliVcuCUOLu+zsE/xnCmUWT0gkeMi62MpSR4/chtbh3g Qu6lxtUcF2MN5IuGb6oCTnWQF+Bk9Pdib2HcKDqIGjQKbato7GLAEQdHY1K9vb7l Wwovasg62CDMtXohBL6SZJJPWPhoK0MUNrKdPJPb3W2yJKgoiVyoNz8FRGmX2OUx 3v0elGP83v4jdmA6aHRfTmmcYKmEevxSFAxjCXw6pYEsPwf3VIr6TMkqANogA16S Ag2eda/6gTiVKlFCVi9uxkLfVvYcdUTYWCjG0xOseVJRnWpXJbNwjCd493Qwhbim zfziqoCYPZ6rLWcoDFkWZ27edfHCdPBlamnRyfHy5+1Y9s4jdcuMtp5B8tlvGuOp 55j/FSNvpPdmXIS0g8/C90nZ2WiAM9N5C1CyrLwgJvixHcMFhmKkJVnJ0zHHCOdC Mu1CBdaGlH7o4+M+CIMU63q5YnHrmoZvZ3t5PPlCl5iUETuKGZmWBKzRv5qx3xld iwSf8vfy+4bJGOF9xSgSvTOpoVEfsJAagKoBiT3WJuK9zi65vmI= =3Glg -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
fa889d8555
@ -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>;
|
||||
};
|
||||
|
||||
...
|
@ -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.
|
||||
|
@ -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";
|
||||
};
|
||||
};
|
50
Documentation/devicetree/bindings/mfd/xylon,logicvc.yaml
Normal file
50
Documentation/devicetree/bindings/mfd/xylon,logicvc.yaml
Normal file
@ -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>;
|
||||
};
|
@ -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,.*":
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -8,6 +8,7 @@ Contents:
|
||||
:maxdepth: 2
|
||||
|
||||
intro
|
||||
using-gpio
|
||||
driver
|
||||
consumer
|
||||
board
|
||||
|
50
Documentation/driver-api/gpio/using-gpio.rst
Normal file
50
Documentation/driver-api/gpio/using-gpio.rst
Normal file
@ -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/
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
170
drivers/gpio/gpio-logicvc.c
Normal file
170
drivers/gpio/gpio-logicvc.c
Normal file
@ -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");
|
@ -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"
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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>");
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
msi_alloc_info_t *info;
|
||||
|
||||
return irq_domain_set_hwirq_and_chip(d, virq, txline->line,
|
||||
&thunderx_gpio_irq_chip, txline);
|
||||
}
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
static const struct irq_domain_ops thunderx_gpio_irqd_ops = {
|
||||
.alloc = thunderx_gpio_irq_alloc,
|
||||
.translate = thunderx_gpio_irq_translate
|
||||
};
|
||||
|
||||
static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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
|
||||
|
121
drivers/gpio/gpio-wcd934x.c
Normal file
121
drivers/gpio/gpio-wcd934x.c
Normal file
@ -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");
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
if (!gc->set_config)
|
||||
return -ENOTSUPP;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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__ */
|
Loading…
Reference in New Issue
Block a user