This is the bulk of GPIO changes for the v5.8 kernel cycle.
Core changes: - A new GPIO aggregator driver has been merged: this can join a few select GPIO lines into a new aggregated GPIO chip. This can be used for security: a process can be granted access to only these lines, for example for industrial control. Another way to use this is to reexpose certain select lines to a virtual machine or container. - Warn if the gpio-line-names is too long in he DT parser core. - GPIO lines can now be looked up by line name in addition to being looked up by offset. New drivers: - A new generic regmap GPIO driver has been merged. Too many regmap drivers are starting to look like each other so we need to create some common ground and try to move drivers over to using that. - The F7188X driver now supports F81865. Driver improvements: - Large improvements to the PCA953x expander, get multiple lines and several cleanups. - Large improvements to the DesignWare DWAPB driver, and Sergey Semin has volunteered to maintain it. - PL061 can now be built as a module, this is part of a bigger effort to make the ARM platforms more modular. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEElDRnuGcz/wPCXQWMQRCzN7AZXXMFAl7ZarsACgkQQRCzN7AZ XXNRERAAvb/8YZX8yzfWqqKZ+aXxBOzn5LsbS15QjJz9W22wAZNhjNEHAtzd+xNJ BiyDt+BQLGqIr2aCKglVFOOYfa8PhTjrVA40ujRFUs3d8q8gr/RFpfGN3/w36h0W e3+QCHqe27Sk07wCB3RH7hzGcKhEUUt+A5sI0uX/nt9obx8IxdVPkafBYqCfdfWw qv9T/MU75ZYeLURg24AkgZR069gQeiDAEKVa8lYh9qKBhircFzBxM6Bg9Efn8tst E+6XIXNREslXBq5wtAosXI/t25ZpYtzDvkM5+/lm1jQyjMEh9wJyjmgu7tcRRZRK xxxOcUHybDJaCFogkA5iOLzyfcUbGZM/i5wQkUE3SPACypGfncjkYZPdY6Smljd1 SfBfl48xch2WW12f1/P04VwSVDNxNe9/RUqapZ5ZQfd2DJwyiZM6p+S85rrFKJse BvixFpGMY3J5h/lZUoaF93JaaQiWh2RK6WnsBvMVn2P9+WyPnwdUL2EX2RrYvdMR iH3jmRzlSeVQnest5c1k+i05O3D0fq3gJ+qvzT5gqxbFdRm6HZEhJNiVT56Y1V4n 30FJMbXg8mddxLvl7b1Hfi5E7PLFOP9Ygz7bHxAfogROGpWMMGgAlynxcSjqUesg YWoRPl0XmtMcsvVNJvLM/B3+Gxh78CsMzo4Nwh9FtgUMOG6B0Xw= =B5OK -----END PGP SIGNATURE----- Merge tag 'gpio-v5.8-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.8 kernel cycle. Core changes: - A new GPIO aggregator driver has been merged: this can join a few select GPIO lines into a new aggregated GPIO chip. This can be used for security: a process can be granted access to only these lines, for example for industrial control. Another way to use this is to reexpose certain select lines to a virtual machine or container. - Warn if the gpio-line-names is too long in he DT parser core. - GPIO lines can now be looked up by line name in addition to being looked up by offset. New drivers: - A new generic regmap GPIO driver has been merged. Too many regmap drivers are starting to look like each other so we need to create some common ground and try to move drivers over to using that. - The F7188X driver now supports F81865. Driver improvements: - Large improvements to the PCA953x expander, get multiple lines and several cleanups. - Large improvements to the DesignWare DWAPB driver, and Sergey Semin has volunteered to maintain it. - PL061 can now be built as a module, this is part of a bigger effort to make the ARM platforms more modular" * tag 'gpio-v5.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (77 commits) gpio: pca953x: Drop unneeded ACPI_PTR() MAINTAINERS: Add gpio regmap section gpio: add a reusable generic gpio_chip using regmap gpiolib: Introduce gpiochip_irqchip_add_domain() gpio: gpiolib: Allow GPIO IRQs to lazy disable gpiolib: Separate GPIO_GET_LINEINFO_WATCH_IOCTL conditional gpio: rcar: Fix runtime PM imbalance on error gpio: pca935x: Allow IRQ support for driver built as a module gpio: pxa: Add COMPILE_TEST support dt-bindings: gpio: Add renesas,em-gio bindings MAINTAINERS: Fix file name for DesignWare GPIO DT schema gpio: dwapb: Remove unneeded has_irq member in struct dwapb_port_property gpio: dwapb: Don't use IRQ 0 as valid Linux interrupt gpio: dwapb: avoid error message for optional IRQ gpio: dwapb: Call acpi_gpiochip_free_interrupts() on GPIO chip de-registration gpio: max730x: bring gpiochip_add_data after port config MAINTAINERS: Add GPIO Aggregator section docs: gpio: Add GPIO Aggregator documentation gpio: Add GPIO Aggregator gpiolib: Add support for GPIO lookup by line name ...
This commit is contained in:
commit
3f7e82379f
111
Documentation/admin-guide/gpio/gpio-aggregator.rst
Normal file
111
Documentation/admin-guide/gpio/gpio-aggregator.rst
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
GPIO Aggregator
|
||||||
|
===============
|
||||||
|
|
||||||
|
The GPIO Aggregator provides a mechanism to aggregate GPIOs, and expose them as
|
||||||
|
a new gpio_chip. This supports the following use cases.
|
||||||
|
|
||||||
|
|
||||||
|
Aggregating GPIOs using Sysfs
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
GPIO controllers are exported to userspace using /dev/gpiochip* character
|
||||||
|
devices. Access control to these devices is provided by standard UNIX file
|
||||||
|
system permissions, on an all-or-nothing basis: either a GPIO controller is
|
||||||
|
accessible for a user, or it is not.
|
||||||
|
|
||||||
|
The GPIO Aggregator provides access control for a set of one or more GPIOs, by
|
||||||
|
aggregating them into a new gpio_chip, which can be assigned to a group or user
|
||||||
|
using standard UNIX file ownership and permissions. Furthermore, this
|
||||||
|
simplifies and hardens exporting GPIOs to a virtual machine, as the VM can just
|
||||||
|
grab the full GPIO controller, and no longer needs to care about which GPIOs to
|
||||||
|
grab and which not, reducing the attack surface.
|
||||||
|
|
||||||
|
Aggregated GPIO controllers are instantiated and destroyed by writing to
|
||||||
|
write-only attribute files in sysfs.
|
||||||
|
|
||||||
|
/sys/bus/platform/drivers/gpio-aggregator/
|
||||||
|
|
||||||
|
"new_device" ...
|
||||||
|
Userspace may ask the kernel to instantiate an aggregated GPIO
|
||||||
|
controller by writing a string describing the GPIOs to
|
||||||
|
aggregate to the "new_device" file, using the format
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
[<gpioA>] [<gpiochipB> <offsets>] ...
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
"<gpioA>" ...
|
||||||
|
is a GPIO line name,
|
||||||
|
|
||||||
|
"<gpiochipB>" ...
|
||||||
|
is a GPIO chip label, and
|
||||||
|
|
||||||
|
"<offsets>" ...
|
||||||
|
is a comma-separated list of GPIO offsets and/or
|
||||||
|
GPIO offset ranges denoted by dashes.
|
||||||
|
|
||||||
|
Example: Instantiate a new GPIO aggregator by aggregating GPIO
|
||||||
|
line 19 of "e6052000.gpio" and GPIO lines 20-21 of
|
||||||
|
"e6050000.gpio" into a new gpio_chip:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ echo 'e6052000.gpio 19 e6050000.gpio 20-21' > new_device
|
||||||
|
|
||||||
|
"delete_device" ...
|
||||||
|
Userspace may ask the kernel to destroy an aggregated GPIO
|
||||||
|
controller after use by writing its device name to the
|
||||||
|
"delete_device" file.
|
||||||
|
|
||||||
|
Example: Destroy the previously-created aggregated GPIO
|
||||||
|
controller, assumed to be "gpio-aggregator.0":
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ echo gpio-aggregator.0 > delete_device
|
||||||
|
|
||||||
|
|
||||||
|
Generic GPIO Driver
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The GPIO Aggregator can also be used as a generic driver for a simple
|
||||||
|
GPIO-operated device described in DT, without a dedicated in-kernel driver.
|
||||||
|
This is useful in industrial control, and is not unlike e.g. spidev, which
|
||||||
|
allows the user to communicate with an SPI device from userspace.
|
||||||
|
|
||||||
|
Binding a device to the GPIO Aggregator is performed either by modifying the
|
||||||
|
gpio-aggregator driver, or by writing to the "driver_override" file in Sysfs.
|
||||||
|
|
||||||
|
Example: If "door" is a GPIO-operated device described in DT, using its own
|
||||||
|
compatible value::
|
||||||
|
|
||||||
|
door {
|
||||||
|
compatible = "myvendor,mydoor";
|
||||||
|
|
||||||
|
gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>,
|
||||||
|
<&gpio2 20 GPIO_ACTIVE_LOW>;
|
||||||
|
gpio-line-names = "open", "lock";
|
||||||
|
};
|
||||||
|
|
||||||
|
it can be bound to the GPIO Aggregator by either:
|
||||||
|
|
||||||
|
1. Adding its compatible value to ``gpio_aggregator_dt_ids[]``,
|
||||||
|
2. Binding manually using "driver_override":
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ echo gpio-aggregator > /sys/bus/platform/devices/door/driver_override
|
||||||
|
$ echo door > /sys/bus/platform/drivers/gpio-aggregator/bind
|
||||||
|
|
||||||
|
After that, a new gpiochip "door" has been created:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ gpioinfo door
|
||||||
|
gpiochip12 - 2 lines:
|
||||||
|
line 0: "open" unused input active-high
|
||||||
|
line 1: "lock" unused input active-high
|
|
@ -7,6 +7,7 @@ gpio
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
gpio-aggregator
|
||||||
sysfs
|
sysfs
|
||||||
|
|
||||||
.. only:: subproject and html
|
.. only:: subproject and html
|
||||||
|
|
70
Documentation/devicetree/bindings/gpio/renesas,em-gio.yaml
Normal file
70
Documentation/devicetree/bindings/gpio/renesas,em-gio.yaml
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/gpio/renesas,em-gio.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Renesas EMMA Mobile General Purpose I/O Interface
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Magnus Damm <magnus.damm@gmail.com>
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: renesas,em-gio
|
||||||
|
|
||||||
|
reg:
|
||||||
|
items:
|
||||||
|
- description: First set of contiguous registers
|
||||||
|
- description: Second set of contiguous registers
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
items:
|
||||||
|
- description: Interrupt for the first set of 16 GPIO ports
|
||||||
|
- description: Interrupt for the second set of 16 GPIO ports
|
||||||
|
|
||||||
|
gpio-controller: true
|
||||||
|
|
||||||
|
'#gpio-cells':
|
||||||
|
const: 2
|
||||||
|
|
||||||
|
gpio-ranges:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
ngpios:
|
||||||
|
minimum: 1
|
||||||
|
maximum: 32
|
||||||
|
|
||||||
|
interrupt-controller: true
|
||||||
|
|
||||||
|
'#interrupt-cells':
|
||||||
|
const: 2
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- gpio-controller
|
||||||
|
- '#gpio-cells'
|
||||||
|
- gpio-ranges
|
||||||
|
- ngpios
|
||||||
|
- interrupt-controller
|
||||||
|
- '#interrupt-cells'
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
gpio0: gpio@e0050000 {
|
||||||
|
compatible = "renesas,em-gio";
|
||||||
|
reg = <0xe0050000 0x2c>, <0xe0050040 0x20>;
|
||||||
|
interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
gpio-controller;
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
gpio-ranges = <&pfc 0 0 32>;
|
||||||
|
ngpios = <32>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
};
|
134
Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml
Normal file
134
Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/gpio/snps,dw-apb-gpio.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Synopsys DesignWare APB GPIO controller
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Synopsys DesignWare GPIO controllers have a configurable number of ports,
|
||||||
|
each of which are intended to be represented as child nodes with the generic
|
||||||
|
GPIO-controller properties as desribed in this bindings file.
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Hoan Tran <hoan@os.amperecomputing.com>
|
||||||
|
- Serge Semin <fancer.lancer@gmail.com>
|
||||||
|
|
||||||
|
properties:
|
||||||
|
$nodename:
|
||||||
|
pattern: "^gpio@[0-9a-f]+$"
|
||||||
|
|
||||||
|
compatible:
|
||||||
|
const: snps,dw-apb-gpio
|
||||||
|
|
||||||
|
"#address-cells":
|
||||||
|
const: 1
|
||||||
|
|
||||||
|
"#size-cells":
|
||||||
|
const: 0
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
minItems: 1
|
||||||
|
items:
|
||||||
|
- description: APB interface clock source
|
||||||
|
- description: DW GPIO debounce reference clock source
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
minItems: 1
|
||||||
|
items:
|
||||||
|
- const: bus
|
||||||
|
- const: db
|
||||||
|
|
||||||
|
resets:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
patternProperties:
|
||||||
|
"^gpio-(port|controller)@[0-9a-f]+$":
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: snps,dw-apb-gpio-port
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
gpio-controller: true
|
||||||
|
|
||||||
|
'#gpio-cells':
|
||||||
|
const: 2
|
||||||
|
|
||||||
|
snps,nr-gpios:
|
||||||
|
description: The number of GPIO pins exported by the port.
|
||||||
|
default: 32
|
||||||
|
allOf:
|
||||||
|
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
- minimum: 1
|
||||||
|
maximum: 32
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
description: |
|
||||||
|
The interrupts to the parent controller raised when GPIOs generate
|
||||||
|
the interrupts. If the controller provides one combined interrupt
|
||||||
|
for all GPIOs, specify a single interrupt. If the controller provides
|
||||||
|
one interrupt for each GPIO, provide a list of interrupts that
|
||||||
|
correspond to each of the GPIO pins.
|
||||||
|
minItems: 1
|
||||||
|
maxItems: 32
|
||||||
|
|
||||||
|
interrupt-controller: true
|
||||||
|
|
||||||
|
'#interrupt-cells':
|
||||||
|
const: 2
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- gpio-controller
|
||||||
|
- '#gpio-cells'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
interrupt-controller: [ interrupts ]
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- "#address-cells"
|
||||||
|
- "#size-cells"
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
gpio: gpio@20000 {
|
||||||
|
compatible = "snps,dw-apb-gpio";
|
||||||
|
reg = <0x20000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
porta: gpio-port@0 {
|
||||||
|
compatible = "snps,dw-apb-gpio-port";
|
||||||
|
reg = <0>;
|
||||||
|
gpio-controller;
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
snps,nr-gpios = <8>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
interrupt-parent = <&vic1>;
|
||||||
|
interrupts = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
portb: gpio-port@1 {
|
||||||
|
compatible = "snps,dw-apb-gpio-port";
|
||||||
|
reg = <1>;
|
||||||
|
gpio-controller;
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
snps,nr-gpios = <8>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
...
|
|
@ -1,65 +0,0 @@
|
||||||
* Synopsys DesignWare APB GPIO controller
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible : Should contain "snps,dw-apb-gpio"
|
|
||||||
- reg : Address and length of the register set for the device.
|
|
||||||
- #address-cells : should be 1 (for addressing port subnodes).
|
|
||||||
- #size-cells : should be 0 (port subnodes).
|
|
||||||
|
|
||||||
The GPIO controller has a configurable number of ports, each of which are
|
|
||||||
represented as child nodes with the following properties:
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible : "snps,dw-apb-gpio-port"
|
|
||||||
- gpio-controller : Marks the device node as a gpio controller.
|
|
||||||
- #gpio-cells : Should be two. The first cell is the pin number and
|
|
||||||
the second cell is used to specify the gpio polarity:
|
|
||||||
0 = active high
|
|
||||||
1 = active low
|
|
||||||
- reg : The integer port index of the port, a single cell.
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
- interrupt-controller : The first port may be configured to be an interrupt
|
|
||||||
controller.
|
|
||||||
- #interrupt-cells : Specifies the number of cells needed to encode an
|
|
||||||
interrupt. Shall be set to 2. The first cell defines the interrupt number,
|
|
||||||
the second encodes the triger flags encoded as described in
|
|
||||||
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
|
||||||
- interrupts : The interrupts to the parent controller raised when GPIOs
|
|
||||||
generate the interrupts. If the controller provides one combined interrupt
|
|
||||||
for all GPIOs, specify a single interrupt. If the controller provides one
|
|
||||||
interrupt for each GPIO, provide a list of interrupts that correspond to each
|
|
||||||
of the GPIO pins. When specifying multiple interrupts, if any are unconnected,
|
|
||||||
use the interrupts-extended property to specify the interrupts and set the
|
|
||||||
interrupt controller handle for unused interrupts to 0.
|
|
||||||
- snps,nr-gpios : The number of pins in the port, a single cell.
|
|
||||||
- resets : Reset line for the controller.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
gpio: gpio@20000 {
|
|
||||||
compatible = "snps,dw-apb-gpio";
|
|
||||||
reg = <0x20000 0x1000>;
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <0>;
|
|
||||||
|
|
||||||
porta: gpio@0 {
|
|
||||||
compatible = "snps,dw-apb-gpio-port";
|
|
||||||
gpio-controller;
|
|
||||||
#gpio-cells = <2>;
|
|
||||||
snps,nr-gpios = <8>;
|
|
||||||
reg = <0>;
|
|
||||||
interrupt-controller;
|
|
||||||
#interrupt-cells = <2>;
|
|
||||||
interrupt-parent = <&vic1>;
|
|
||||||
interrupts = <0>;
|
|
||||||
};
|
|
||||||
|
|
||||||
portb: gpio@1 {
|
|
||||||
compatible = "snps,dw-apb-gpio-port";
|
|
||||||
gpio-controller;
|
|
||||||
#gpio-cells = <2>;
|
|
||||||
snps,nr-gpios = <8>;
|
|
||||||
reg = <1>;
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -113,13 +113,15 @@ files that desire to do so need to include the following header::
|
||||||
GPIOs are mapped by the means of tables of lookups, containing instances of the
|
GPIOs are mapped by the means of tables of lookups, containing instances of the
|
||||||
gpiod_lookup structure. Two macros are defined to help declaring such mappings::
|
gpiod_lookup structure. Two macros are defined to help declaring such mappings::
|
||||||
|
|
||||||
GPIO_LOOKUP(chip_label, chip_hwnum, con_id, flags)
|
GPIO_LOOKUP(key, chip_hwnum, con_id, flags)
|
||||||
GPIO_LOOKUP_IDX(chip_label, chip_hwnum, con_id, idx, flags)
|
GPIO_LOOKUP_IDX(key, chip_hwnum, con_id, idx, flags)
|
||||||
|
|
||||||
where
|
where
|
||||||
|
|
||||||
- chip_label is the label of the gpiod_chip instance providing the GPIO
|
- key is either the label of the gpiod_chip instance providing the GPIO, or
|
||||||
- chip_hwnum is the hardware number of the GPIO within the chip
|
the GPIO line name
|
||||||
|
- chip_hwnum is the hardware number of the GPIO within the chip, or U16_MAX
|
||||||
|
to indicate that key is a GPIO line name
|
||||||
- con_id is the name of the GPIO function from the device point of view. It
|
- con_id is the name of the GPIO function from the device point of view. It
|
||||||
can be NULL, in which case it will match any function.
|
can be NULL, in which case it will match any function.
|
||||||
- idx is the index of the GPIO within the function.
|
- idx is the index of the GPIO within the function.
|
||||||
|
@ -135,7 +137,10 @@ where
|
||||||
|
|
||||||
In the future, these flags might be extended to support more properties.
|
In the future, these flags might be extended to support more properties.
|
||||||
|
|
||||||
Note that GPIO_LOOKUP() is just a shortcut to GPIO_LOOKUP_IDX() where idx = 0.
|
Note that:
|
||||||
|
1. GPIO line names are not guaranteed to be globally unique, so the first
|
||||||
|
match found will be used.
|
||||||
|
2. GPIO_LOOKUP() is just a shortcut to GPIO_LOOKUP_IDX() where idx = 0.
|
||||||
|
|
||||||
A lookup table can then be defined as follows, with an empty entry defining its
|
A lookup table can then be defined as follows, with an empty entry defining its
|
||||||
end. The 'dev_id' field of the table is the identifier of the device that will
|
end. The 'dev_id' field of the table is the identifier of the device that will
|
||||||
|
|
16
MAINTAINERS
16
MAINTAINERS
|
@ -7283,6 +7283,13 @@ F: Documentation/firmware-guide/acpi/gpio-properties.rst
|
||||||
F: drivers/gpio/gpiolib-acpi.c
|
F: drivers/gpio/gpiolib-acpi.c
|
||||||
F: drivers/gpio/gpiolib-acpi.h
|
F: drivers/gpio/gpiolib-acpi.h
|
||||||
|
|
||||||
|
GPIO AGGREGATOR
|
||||||
|
M: Geert Uytterhoeven <geert+renesas@glider.be>
|
||||||
|
L: linux-gpio@vger.kernel.org
|
||||||
|
S: Supported
|
||||||
|
F: Documentation/admin-guide/gpio/gpio-aggregator.rst
|
||||||
|
F: drivers/gpio/gpio-aggregator.c
|
||||||
|
|
||||||
GPIO IR Transmitter
|
GPIO IR Transmitter
|
||||||
M: Sean Young <sean@mess.org>
|
M: Sean Young <sean@mess.org>
|
||||||
L: linux-media@vger.kernel.org
|
L: linux-media@vger.kernel.org
|
||||||
|
@ -7296,6 +7303,12 @@ S: Maintained
|
||||||
F: drivers/gpio/gpio-mockup.c
|
F: drivers/gpio/gpio-mockup.c
|
||||||
F: tools/testing/selftests/gpio/
|
F: tools/testing/selftests/gpio/
|
||||||
|
|
||||||
|
GPIO REGMAP
|
||||||
|
R: Michael Walle <michael@walle.cc>
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/gpio/gpio-regmap.c
|
||||||
|
F: include/linux/gpio/regmap.h
|
||||||
|
|
||||||
GPIO SUBSYSTEM
|
GPIO SUBSYSTEM
|
||||||
M: Linus Walleij <linus.walleij@linaro.org>
|
M: Linus Walleij <linus.walleij@linaro.org>
|
||||||
M: Bartosz Golaszewski <bgolaszewski@baylibre.com>
|
M: Bartosz Golaszewski <bgolaszewski@baylibre.com>
|
||||||
|
@ -16368,9 +16381,10 @@ F: drivers/tty/serial/8250/8250_lpss.c
|
||||||
|
|
||||||
SYNOPSYS DESIGNWARE APB GPIO DRIVER
|
SYNOPSYS DESIGNWARE APB GPIO DRIVER
|
||||||
M: Hoan Tran <hoan@os.amperecomputing.com>
|
M: Hoan Tran <hoan@os.amperecomputing.com>
|
||||||
|
M: Serge Semin <fancer.lancer@gmail.com>
|
||||||
L: linux-gpio@vger.kernel.org
|
L: linux-gpio@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt
|
F: Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml
|
||||||
F: drivers/gpio/gpio-dwapb.c
|
F: drivers/gpio/gpio-dwapb.c
|
||||||
|
|
||||||
SYNOPSYS DESIGNWARE AXI DMAC DRIVER
|
SYNOPSYS DESIGNWARE AXI DMAC DRIVER
|
||||||
|
|
|
@ -73,6 +73,10 @@ config GPIO_GENERIC
|
||||||
depends on HAS_IOMEM # Only for IOMEM drivers
|
depends on HAS_IOMEM # Only for IOMEM drivers
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
config GPIO_REGMAP
|
||||||
|
depends on REGMAP
|
||||||
|
tristate
|
||||||
|
|
||||||
# put drivers in the right section, in alphabetical order
|
# put drivers in the right section, in alphabetical order
|
||||||
|
|
||||||
# This symbol is selected by both I2C and SPI expanders
|
# This symbol is selected by both I2C and SPI expanders
|
||||||
|
@ -422,7 +426,7 @@ config GPIO_OMAP
|
||||||
Say yes here to enable GPIO support for TI OMAP SoCs.
|
Say yes here to enable GPIO support for TI OMAP SoCs.
|
||||||
|
|
||||||
config GPIO_PL061
|
config GPIO_PL061
|
||||||
bool "PrimeCell PL061 GPIO support"
|
tristate "PrimeCell PL061 GPIO support"
|
||||||
depends on ARM_AMBA
|
depends on ARM_AMBA
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
|
@ -439,7 +443,7 @@ config GPIO_PMIC_EIC_SPRD
|
||||||
|
|
||||||
config GPIO_PXA
|
config GPIO_PXA
|
||||||
bool "PXA GPIO support"
|
bool "PXA GPIO support"
|
||||||
depends on ARCH_PXA || ARCH_MMP
|
depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
|
||||||
help
|
help
|
||||||
Say yes here to support the PXA GPIO device
|
Say yes here to support the PXA GPIO device
|
||||||
|
|
||||||
|
@ -638,7 +642,7 @@ config GPIO_XGENE
|
||||||
|
|
||||||
config GPIO_XGENE_SB
|
config GPIO_XGENE_SB
|
||||||
tristate "APM X-Gene GPIO standby controller support"
|
tristate "APM X-Gene GPIO standby controller support"
|
||||||
depends on ARCH_XGENE && OF_GPIO
|
depends on (ARCH_XGENE || COMPILE_TEST)
|
||||||
select GPIO_GENERIC
|
select GPIO_GENERIC
|
||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
select IRQ_DOMAIN_HIERARCHY
|
select IRQ_DOMAIN_HIERARCHY
|
||||||
|
@ -952,7 +956,7 @@ config GPIO_PCA953X
|
||||||
|
|
||||||
config GPIO_PCA953X_IRQ
|
config GPIO_PCA953X_IRQ
|
||||||
bool "Interrupt controller support for PCA953x"
|
bool "Interrupt controller support for PCA953x"
|
||||||
depends on GPIO_PCA953X=y
|
depends on GPIO_PCA953X
|
||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
help
|
help
|
||||||
Say yes here to enable the pca953x to be used as an interrupt
|
Say yes here to enable the pca953x to be used as an interrupt
|
||||||
|
@ -1541,6 +1545,18 @@ config GPIO_VIPERBOARD
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
config GPIO_AGGREGATOR
|
||||||
|
tristate "GPIO Aggregator"
|
||||||
|
help
|
||||||
|
Say yes here to enable the GPIO Aggregator, which provides a way to
|
||||||
|
aggregate existing GPIO lines into a new virtual GPIO chip.
|
||||||
|
This can serve the following purposes:
|
||||||
|
- Assign permissions for a collection of GPIO lines to a user,
|
||||||
|
- Export a collection of GPIO lines to a virtual machine,
|
||||||
|
- Provide a generic driver for a GPIO-operated device in an
|
||||||
|
industrial control context, to be operated from userspace using
|
||||||
|
the GPIO chardev interface.
|
||||||
|
|
||||||
config GPIO_MOCKUP
|
config GPIO_MOCKUP
|
||||||
tristate "GPIO Testing Driver"
|
tristate "GPIO Testing Driver"
|
||||||
select IRQ_SIM
|
select IRQ_SIM
|
||||||
|
|
|
@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
|
||||||
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
|
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
|
||||||
|
|
||||||
# Device drivers. Generally keep list sorted alphabetically
|
# Device drivers. Generally keep list sorted alphabetically
|
||||||
|
obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o
|
||||||
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
|
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
|
||||||
|
|
||||||
# directly supported by gpio-generic
|
# directly supported by gpio-generic
|
||||||
|
@ -25,6 +26,7 @@ obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
|
||||||
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
|
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
|
||||||
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
|
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
|
||||||
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
|
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
|
||||||
|
obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o
|
||||||
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
|
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
|
||||||
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
|
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
|
||||||
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
|
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
|
||||||
|
|
|
@ -99,6 +99,10 @@ similar and probe a proper driver in the gpiolib subsystem.
|
||||||
In some cases it makes sense to create a GPIO chip from the local driver
|
In some cases it makes sense to create a GPIO chip from the local driver
|
||||||
for a few GPIOs. Those should stay where they are.
|
for a few GPIOs. Those should stay where they are.
|
||||||
|
|
||||||
|
At the same time it makes sense to get rid of code duplication in existing or
|
||||||
|
new coming drivers. For example, gpio-ml-ioh should be incorporated into
|
||||||
|
gpio-pch. In similar way gpio-intel-mid into gpio-pxa.
|
||||||
|
|
||||||
|
|
||||||
Generic MMIO GPIO
|
Generic MMIO GPIO
|
||||||
|
|
||||||
|
|
568
drivers/gpio/gpio-aggregator.c
Normal file
568
drivers/gpio/gpio-aggregator.c
Normal file
|
@ -0,0 +1,568 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
//
|
||||||
|
// GPIO Aggregator
|
||||||
|
//
|
||||||
|
// Copyright (C) 2019-2020 Glider bv
|
||||||
|
|
||||||
|
#define DRV_NAME "gpio-aggregator"
|
||||||
|
#define pr_fmt(fmt) DRV_NAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/bitmap.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/gpio/driver.h>
|
||||||
|
#include <linux/gpio/machine.h>
|
||||||
|
#include <linux/idr.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GPIO Aggregator sysfs interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct gpio_aggregator {
|
||||||
|
struct gpiod_lookup_table *lookups;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
char args[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */
|
||||||
|
static DEFINE_IDR(gpio_aggregator_idr);
|
||||||
|
|
||||||
|
static char *get_arg(char **args)
|
||||||
|
{
|
||||||
|
char *start = *args, *end;
|
||||||
|
|
||||||
|
start = skip_spaces(start);
|
||||||
|
if (!*start)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (*start == '"') {
|
||||||
|
/* Quoted arg */
|
||||||
|
end = strchr(++start, '"');
|
||||||
|
if (!end)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
} else {
|
||||||
|
/* Unquoted arg */
|
||||||
|
for (end = start; *end && !isspace(*end); end++) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*end)
|
||||||
|
*end++ = '\0';
|
||||||
|
|
||||||
|
*args = end;
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isrange(const char *s)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(s))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
n = strspn(s, "0123456789");
|
||||||
|
if (!n)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s += n;
|
||||||
|
|
||||||
|
switch (*s++) {
|
||||||
|
case '\0':
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
case ',':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
|
||||||
|
int hwnum, unsigned int *n)
|
||||||
|
{
|
||||||
|
struct gpiod_lookup_table *lookups;
|
||||||
|
|
||||||
|
lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!lookups)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
lookups->table[*n] =
|
||||||
|
(struct gpiod_lookup)GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
|
||||||
|
|
||||||
|
(*n)++;
|
||||||
|
memset(&lookups->table[*n], 0, sizeof(lookups->table[*n]));
|
||||||
|
|
||||||
|
aggr->lookups = lookups;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aggr_parse(struct gpio_aggregator *aggr)
|
||||||
|
{
|
||||||
|
unsigned int first_index, last_index, i, n = 0;
|
||||||
|
char *name, *offsets, *first, *last, *next;
|
||||||
|
char *args = aggr->args;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
for (name = get_arg(&args), offsets = get_arg(&args); name;
|
||||||
|
offsets = get_arg(&args)) {
|
||||||
|
if (IS_ERR(name)) {
|
||||||
|
pr_err("Cannot get GPIO specifier: %pe\n", name);
|
||||||
|
return PTR_ERR(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isrange(offsets)) {
|
||||||
|
/* Named GPIO line */
|
||||||
|
error = aggr_add_gpio(aggr, name, U16_MAX, &n);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
name = offsets;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GPIO chip + offset(s) */
|
||||||
|
for (first = offsets; *first; first = next) {
|
||||||
|
next = strchrnul(first, ',');
|
||||||
|
if (*next)
|
||||||
|
*next++ = '\0';
|
||||||
|
|
||||||
|
last = strchr(first, '-');
|
||||||
|
if (last)
|
||||||
|
*last++ = '\0';
|
||||||
|
|
||||||
|
if (kstrtouint(first, 10, &first_index)) {
|
||||||
|
pr_err("Cannot parse GPIO index %s\n", first);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!last) {
|
||||||
|
last_index = first_index;
|
||||||
|
} else if (kstrtouint(last, 10, &last_index)) {
|
||||||
|
pr_err("Cannot parse GPIO index %s\n", last);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = first_index; i <= last_index; i++) {
|
||||||
|
error = aggr_add_gpio(aggr, name, i, &n);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name = get_arg(&args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!n) {
|
||||||
|
pr_err("No GPIOs specified\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t new_device_store(struct device_driver *driver, const char *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
struct gpio_aggregator *aggr;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
int res, id;
|
||||||
|
|
||||||
|
/* kernfs guarantees string termination, so count + 1 is safe */
|
||||||
|
aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL);
|
||||||
|
if (!aggr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memcpy(aggr->args, buf, count + 1);
|
||||||
|
|
||||||
|
aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!aggr->lookups) {
|
||||||
|
res = -ENOMEM;
|
||||||
|
goto free_ga;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&gpio_aggregator_lock);
|
||||||
|
id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL);
|
||||||
|
mutex_unlock(&gpio_aggregator_lock);
|
||||||
|
|
||||||
|
if (id < 0) {
|
||||||
|
res = id;
|
||||||
|
goto free_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id);
|
||||||
|
if (!aggr->lookups->dev_id) {
|
||||||
|
res = -ENOMEM;
|
||||||
|
goto remove_idr;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = aggr_parse(aggr);
|
||||||
|
if (res)
|
||||||
|
goto free_dev_id;
|
||||||
|
|
||||||
|
gpiod_add_lookup_table(aggr->lookups);
|
||||||
|
|
||||||
|
pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0);
|
||||||
|
if (IS_ERR(pdev)) {
|
||||||
|
res = PTR_ERR(pdev);
|
||||||
|
goto remove_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
aggr->pdev = pdev;
|
||||||
|
return count;
|
||||||
|
|
||||||
|
remove_table:
|
||||||
|
gpiod_remove_lookup_table(aggr->lookups);
|
||||||
|
free_dev_id:
|
||||||
|
kfree(aggr->lookups->dev_id);
|
||||||
|
remove_idr:
|
||||||
|
mutex_lock(&gpio_aggregator_lock);
|
||||||
|
idr_remove(&gpio_aggregator_idr, id);
|
||||||
|
mutex_unlock(&gpio_aggregator_lock);
|
||||||
|
free_table:
|
||||||
|
kfree(aggr->lookups);
|
||||||
|
free_ga:
|
||||||
|
kfree(aggr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DRIVER_ATTR_WO(new_device);
|
||||||
|
|
||||||
|
static void gpio_aggregator_free(struct gpio_aggregator *aggr)
|
||||||
|
{
|
||||||
|
platform_device_unregister(aggr->pdev);
|
||||||
|
gpiod_remove_lookup_table(aggr->lookups);
|
||||||
|
kfree(aggr->lookups->dev_id);
|
||||||
|
kfree(aggr->lookups);
|
||||||
|
kfree(aggr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t delete_device_store(struct device_driver *driver,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct gpio_aggregator *aggr;
|
||||||
|
unsigned int id;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (!str_has_prefix(buf, DRV_NAME "."))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
mutex_lock(&gpio_aggregator_lock);
|
||||||
|
aggr = idr_remove(&gpio_aggregator_idr, id);
|
||||||
|
mutex_unlock(&gpio_aggregator_lock);
|
||||||
|
if (!aggr)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
gpio_aggregator_free(aggr);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static DRIVER_ATTR_WO(delete_device);
|
||||||
|
|
||||||
|
static struct attribute *gpio_aggregator_attrs[] = {
|
||||||
|
&driver_attr_new_device.attr,
|
||||||
|
&driver_attr_delete_device.attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
ATTRIBUTE_GROUPS(gpio_aggregator);
|
||||||
|
|
||||||
|
static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data)
|
||||||
|
{
|
||||||
|
gpio_aggregator_free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit gpio_aggregator_remove_all(void)
|
||||||
|
{
|
||||||
|
mutex_lock(&gpio_aggregator_lock);
|
||||||
|
idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL);
|
||||||
|
idr_destroy(&gpio_aggregator_idr);
|
||||||
|
mutex_unlock(&gpio_aggregator_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GPIO Forwarder
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct gpiochip_fwd {
|
||||||
|
struct gpio_chip chip;
|
||||||
|
struct gpio_desc **descs;
|
||||||
|
union {
|
||||||
|
struct mutex mlock; /* protects tmp[] if can_sleep */
|
||||||
|
spinlock_t slock; /* protects tmp[] if !can_sleep */
|
||||||
|
};
|
||||||
|
unsigned long tmp[]; /* values and descs for multiple ops */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
return gpiod_get_direction(fwd->descs[offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_fwd_direction_input(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
return gpiod_direction_input(fwd->descs[offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_fwd_direction_output(struct gpio_chip *chip,
|
||||||
|
unsigned int offset, int value)
|
||||||
|
{
|
||||||
|
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
return gpiod_direction_output(fwd->descs[offset], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
return gpiod_get_value(fwd->descs[offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_fwd_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||||
|
unsigned long *bits)
|
||||||
|
{
|
||||||
|
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||||
|
unsigned long *values, flags = 0;
|
||||||
|
struct gpio_desc **descs;
|
||||||
|
unsigned int i, j = 0;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (chip->can_sleep)
|
||||||
|
mutex_lock(&fwd->mlock);
|
||||||
|
else
|
||||||
|
spin_lock_irqsave(&fwd->slock, flags);
|
||||||
|
|
||||||
|
/* Both values bitmap and desc pointers are stored in tmp[] */
|
||||||
|
values = &fwd->tmp[0];
|
||||||
|
descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)];
|
||||||
|
|
||||||
|
bitmap_clear(values, 0, fwd->chip.ngpio);
|
||||||
|
for_each_set_bit(i, mask, fwd->chip.ngpio)
|
||||||
|
descs[j++] = fwd->descs[i];
|
||||||
|
|
||||||
|
error = gpiod_get_array_value(j, descs, NULL, values);
|
||||||
|
if (!error) {
|
||||||
|
j = 0;
|
||||||
|
for_each_set_bit(i, mask, fwd->chip.ngpio)
|
||||||
|
__assign_bit(i, bits, test_bit(j++, values));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip->can_sleep)
|
||||||
|
mutex_unlock(&fwd->mlock);
|
||||||
|
else
|
||||||
|
spin_unlock_irqrestore(&fwd->slock, flags);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value)
|
||||||
|
{
|
||||||
|
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
gpiod_set_value(fwd->descs[offset], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpio_fwd_set_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||||
|
unsigned long *bits)
|
||||||
|
{
|
||||||
|
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||||
|
unsigned long *values, flags = 0;
|
||||||
|
struct gpio_desc **descs;
|
||||||
|
unsigned int i, j = 0;
|
||||||
|
|
||||||
|
if (chip->can_sleep)
|
||||||
|
mutex_lock(&fwd->mlock);
|
||||||
|
else
|
||||||
|
spin_lock_irqsave(&fwd->slock, flags);
|
||||||
|
|
||||||
|
/* Both values bitmap and desc pointers are stored in tmp[] */
|
||||||
|
values = &fwd->tmp[0];
|
||||||
|
descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)];
|
||||||
|
|
||||||
|
for_each_set_bit(i, mask, fwd->chip.ngpio) {
|
||||||
|
__assign_bit(j, values, test_bit(i, bits));
|
||||||
|
descs[j++] = fwd->descs[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
gpiod_set_array_value(j, descs, NULL, values);
|
||||||
|
|
||||||
|
if (chip->can_sleep)
|
||||||
|
mutex_unlock(&fwd->mlock);
|
||||||
|
else
|
||||||
|
spin_unlock_irqrestore(&fwd->slock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset,
|
||||||
|
unsigned long config)
|
||||||
|
{
|
||||||
|
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
return gpiod_set_config(fwd->descs[offset], config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpiochip_fwd_create() - Create a new GPIO forwarder
|
||||||
|
* @dev: Parent device pointer
|
||||||
|
* @ngpios: Number of GPIOs in the forwarder.
|
||||||
|
* @descs: Array containing the GPIO descriptors to forward to.
|
||||||
|
* This array must contain @ngpios entries, and must not be deallocated
|
||||||
|
* before the forwarder has been destroyed again.
|
||||||
|
*
|
||||||
|
* This function creates a new gpiochip, which forwards all GPIO operations to
|
||||||
|
* the passed GPIO descriptors.
|
||||||
|
*
|
||||||
|
* Return: An opaque object pointer, or an ERR_PTR()-encoded negative error
|
||||||
|
* code on failure.
|
||||||
|
*/
|
||||||
|
static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
|
||||||
|
unsigned int ngpios,
|
||||||
|
struct gpio_desc *descs[])
|
||||||
|
{
|
||||||
|
const char *label = dev_name(dev);
|
||||||
|
struct gpiochip_fwd *fwd;
|
||||||
|
struct gpio_chip *chip;
|
||||||
|
unsigned int i;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
fwd = devm_kzalloc(dev, struct_size(fwd, tmp,
|
||||||
|
BITS_TO_LONGS(ngpios) + ngpios), GFP_KERNEL);
|
||||||
|
if (!fwd)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
chip = &fwd->chip;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If any of the GPIO lines are sleeping, then the entire forwarder
|
||||||
|
* will be sleeping.
|
||||||
|
* If any of the chips support .set_config(), then the forwarder will
|
||||||
|
* support setting configs.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < ngpios; i++) {
|
||||||
|
struct gpio_chip *parent = gpiod_to_chip(descs[i]);
|
||||||
|
|
||||||
|
dev_dbg(dev, "%u => gpio-%d\n", i, desc_to_gpio(descs[i]));
|
||||||
|
|
||||||
|
if (gpiod_cansleep(descs[i]))
|
||||||
|
chip->can_sleep = true;
|
||||||
|
if (parent && parent->set_config)
|
||||||
|
chip->set_config = gpio_fwd_set_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->label = label;
|
||||||
|
chip->parent = dev;
|
||||||
|
chip->owner = THIS_MODULE;
|
||||||
|
chip->get_direction = gpio_fwd_get_direction;
|
||||||
|
chip->direction_input = gpio_fwd_direction_input;
|
||||||
|
chip->direction_output = gpio_fwd_direction_output;
|
||||||
|
chip->get = gpio_fwd_get;
|
||||||
|
chip->get_multiple = gpio_fwd_get_multiple;
|
||||||
|
chip->set = gpio_fwd_set;
|
||||||
|
chip->set_multiple = gpio_fwd_set_multiple;
|
||||||
|
chip->base = -1;
|
||||||
|
chip->ngpio = ngpios;
|
||||||
|
fwd->descs = descs;
|
||||||
|
|
||||||
|
if (chip->can_sleep)
|
||||||
|
mutex_init(&fwd->mlock);
|
||||||
|
else
|
||||||
|
spin_lock_init(&fwd->slock);
|
||||||
|
|
||||||
|
error = devm_gpiochip_add_data(dev, chip, fwd);
|
||||||
|
if (error)
|
||||||
|
return ERR_PTR(error);
|
||||||
|
|
||||||
|
return fwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GPIO Aggregator platform device
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int gpio_aggregator_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct gpio_desc **descs;
|
||||||
|
struct gpiochip_fwd *fwd;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
n = gpiod_count(dev, NULL);
|
||||||
|
if (n < 0)
|
||||||
|
return n;
|
||||||
|
|
||||||
|
descs = devm_kmalloc_array(dev, n, sizeof(*descs), GFP_KERNEL);
|
||||||
|
if (!descs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
|
||||||
|
if (IS_ERR(descs[i]))
|
||||||
|
return PTR_ERR(descs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fwd = gpiochip_fwd_create(dev, n, descs);
|
||||||
|
if (IS_ERR(fwd))
|
||||||
|
return PTR_ERR(fwd);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, fwd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id gpio_aggregator_dt_ids[] = {
|
||||||
|
/*
|
||||||
|
* Add GPIO-operated devices controlled from userspace below,
|
||||||
|
* or use "driver_override" in sysfs
|
||||||
|
*/
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct platform_driver gpio_aggregator_driver = {
|
||||||
|
.probe = gpio_aggregator_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.groups = gpio_aggregator_groups,
|
||||||
|
.of_match_table = of_match_ptr(gpio_aggregator_dt_ids),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init gpio_aggregator_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&gpio_aggregator_driver);
|
||||||
|
}
|
||||||
|
module_init(gpio_aggregator_init);
|
||||||
|
|
||||||
|
static void __exit gpio_aggregator_exit(void)
|
||||||
|
{
|
||||||
|
gpio_aggregator_remove_all();
|
||||||
|
platform_driver_unregister(&gpio_aggregator_driver);
|
||||||
|
}
|
||||||
|
module_exit(gpio_aggregator_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
|
||||||
|
MODULE_DESCRIPTION("GPIO Aggregator");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -49,7 +49,9 @@
|
||||||
#define GPIO_EXT_PORTC 0x58
|
#define GPIO_EXT_PORTC 0x58
|
||||||
#define GPIO_EXT_PORTD 0x5c
|
#define GPIO_EXT_PORTD 0x5c
|
||||||
|
|
||||||
|
#define DWAPB_DRIVER_NAME "gpio-dwapb"
|
||||||
#define DWAPB_MAX_PORTS 4
|
#define DWAPB_MAX_PORTS 4
|
||||||
|
|
||||||
#define GPIO_EXT_PORT_STRIDE 0x04 /* register stride 32 bits */
|
#define GPIO_EXT_PORT_STRIDE 0x04 /* register stride 32 bits */
|
||||||
#define GPIO_SWPORT_DR_STRIDE 0x0c /* register stride 3*32 bits */
|
#define GPIO_SWPORT_DR_STRIDE 0x0c /* register stride 3*32 bits */
|
||||||
#define GPIO_SWPORT_DDR_STRIDE 0x0c /* register stride 3*32 bits */
|
#define GPIO_SWPORT_DDR_STRIDE 0x0c /* register stride 3*32 bits */
|
||||||
|
@ -62,6 +64,8 @@
|
||||||
#define GPIO_INTSTATUS_V2 0x3c
|
#define GPIO_INTSTATUS_V2 0x3c
|
||||||
#define GPIO_PORTA_EOI_V2 0x40
|
#define GPIO_PORTA_EOI_V2 0x40
|
||||||
|
|
||||||
|
#define DWAPB_NR_CLOCKS 2
|
||||||
|
|
||||||
struct dwapb_gpio;
|
struct dwapb_gpio;
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
@ -97,7 +101,7 @@ struct dwapb_gpio {
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
struct reset_control *rst;
|
struct reset_control *rst;
|
||||||
struct clk *clk;
|
struct clk_bulk_data clks[DWAPB_NR_CLOCKS];
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline u32 gpio_reg_v2_convert(unsigned int offset)
|
static inline u32 gpio_reg_v2_convert(unsigned int offset)
|
||||||
|
@ -189,22 +193,21 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
|
||||||
|
|
||||||
static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
|
static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
|
||||||
{
|
{
|
||||||
u32 irq_status = dwapb_read(gpio, GPIO_INTSTATUS);
|
unsigned long irq_status;
|
||||||
u32 ret = irq_status;
|
irq_hw_number_t hwirq;
|
||||||
|
|
||||||
while (irq_status) {
|
irq_status = dwapb_read(gpio, GPIO_INTSTATUS);
|
||||||
int hwirq = fls(irq_status) - 1;
|
for_each_set_bit(hwirq, &irq_status, 32) {
|
||||||
int gpio_irq = irq_find_mapping(gpio->domain, hwirq);
|
int gpio_irq = irq_find_mapping(gpio->domain, hwirq);
|
||||||
|
u32 irq_type = irq_get_trigger_type(gpio_irq);
|
||||||
|
|
||||||
generic_handle_irq(gpio_irq);
|
generic_handle_irq(gpio_irq);
|
||||||
irq_status &= ~BIT(hwirq);
|
|
||||||
|
|
||||||
if ((irq_get_trigger_type(gpio_irq) & IRQ_TYPE_SENSE_MASK)
|
if ((irq_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
|
||||||
== IRQ_TYPE_EDGE_BOTH)
|
|
||||||
dwapb_toggle_trigger(gpio, hwirq);
|
dwapb_toggle_trigger(gpio, hwirq);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return irq_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwapb_irq_handler(struct irq_desc *desc)
|
static void dwapb_irq_handler(struct irq_desc *desc)
|
||||||
|
@ -212,10 +215,9 @@ static void dwapb_irq_handler(struct irq_desc *desc)
|
||||||
struct dwapb_gpio *gpio = irq_desc_get_handler_data(desc);
|
struct dwapb_gpio *gpio = irq_desc_get_handler_data(desc);
|
||||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||||
|
|
||||||
|
chained_irq_enter(chip, desc);
|
||||||
dwapb_do_irq(gpio);
|
dwapb_do_irq(gpio);
|
||||||
|
chained_irq_exit(chip, desc);
|
||||||
if (chip->irq_eoi)
|
|
||||||
chip->irq_eoi(irq_desc_get_irq_data(desc));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwapb_irq_enable(struct irq_data *d)
|
static void dwapb_irq_enable(struct irq_data *d)
|
||||||
|
@ -228,7 +230,7 @@ static void dwapb_irq_enable(struct irq_data *d)
|
||||||
|
|
||||||
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
||||||
val = dwapb_read(gpio, GPIO_INTEN);
|
val = dwapb_read(gpio, GPIO_INTEN);
|
||||||
val |= BIT(d->hwirq);
|
val |= BIT(irqd_to_hwirq(d));
|
||||||
dwapb_write(gpio, GPIO_INTEN, val);
|
dwapb_write(gpio, GPIO_INTEN, val);
|
||||||
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -243,46 +245,20 @@ static void dwapb_irq_disable(struct irq_data *d)
|
||||||
|
|
||||||
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
||||||
val = dwapb_read(gpio, GPIO_INTEN);
|
val = dwapb_read(gpio, GPIO_INTEN);
|
||||||
val &= ~BIT(d->hwirq);
|
val &= ~BIT(irqd_to_hwirq(d));
|
||||||
dwapb_write(gpio, GPIO_INTEN, val);
|
dwapb_write(gpio, GPIO_INTEN, val);
|
||||||
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dwapb_irq_reqres(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
|
|
||||||
struct dwapb_gpio *gpio = igc->private;
|
|
||||||
struct gpio_chip *gc = &gpio->ports[0].gc;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = gpiochip_lock_as_irq(gc, irqd_to_hwirq(d));
|
|
||||||
if (ret) {
|
|
||||||
dev_err(gpio->dev, "unable to lock HW IRQ %lu for IRQ\n",
|
|
||||||
irqd_to_hwirq(d));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dwapb_irq_relres(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
|
|
||||||
struct dwapb_gpio *gpio = igc->private;
|
|
||||||
struct gpio_chip *gc = &gpio->ports[0].gc;
|
|
||||||
|
|
||||||
gpiochip_unlock_as_irq(gc, irqd_to_hwirq(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dwapb_irq_set_type(struct irq_data *d, u32 type)
|
static int dwapb_irq_set_type(struct irq_data *d, u32 type)
|
||||||
{
|
{
|
||||||
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
|
||||||
struct dwapb_gpio *gpio = igc->private;
|
struct dwapb_gpio *gpio = igc->private;
|
||||||
struct gpio_chip *gc = &gpio->ports[0].gc;
|
struct gpio_chip *gc = &gpio->ports[0].gc;
|
||||||
int bit = d->hwirq;
|
irq_hw_number_t bit = irqd_to_hwirq(d);
|
||||||
unsigned long level, polarity, flags;
|
unsigned long level, polarity, flags;
|
||||||
|
|
||||||
if (type & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
|
if (type & ~IRQ_TYPE_SENSE_MASK)
|
||||||
IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
||||||
|
@ -328,11 +304,12 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
|
||||||
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
|
||||||
struct dwapb_gpio *gpio = igc->private;
|
struct dwapb_gpio *gpio = igc->private;
|
||||||
struct dwapb_context *ctx = gpio->ports[0].ctx;
|
struct dwapb_context *ctx = gpio->ports[0].ctx;
|
||||||
|
irq_hw_number_t bit = irqd_to_hwirq(d);
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
ctx->wake_en |= BIT(d->hwirq);
|
ctx->wake_en |= BIT(bit);
|
||||||
else
|
else
|
||||||
ctx->wake_en &= ~BIT(d->hwirq);
|
ctx->wake_en &= ~BIT(bit);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -350,9 +327,10 @@ static int dwapb_gpio_set_debounce(struct gpio_chip *gc,
|
||||||
|
|
||||||
val_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
|
val_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
|
||||||
if (debounce)
|
if (debounce)
|
||||||
dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb | mask);
|
val_deb |= mask;
|
||||||
else
|
else
|
||||||
dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb & ~mask);
|
val_deb &= ~mask;
|
||||||
|
dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
||||||
|
|
||||||
|
@ -373,12 +351,7 @@ static int dwapb_gpio_set_config(struct gpio_chip *gc, unsigned offset,
|
||||||
|
|
||||||
static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id)
|
static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
u32 worked;
|
return IRQ_RETVAL(dwapb_do_irq(dev_id));
|
||||||
struct dwapb_gpio *gpio = dev_id;
|
|
||||||
|
|
||||||
worked = dwapb_do_irq(gpio);
|
|
||||||
|
|
||||||
return worked ? IRQ_HANDLED : IRQ_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
|
static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
|
||||||
|
@ -388,17 +361,23 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
|
||||||
struct gpio_chip *gc = &port->gc;
|
struct gpio_chip *gc = &port->gc;
|
||||||
struct fwnode_handle *fwnode = pp->fwnode;
|
struct fwnode_handle *fwnode = pp->fwnode;
|
||||||
struct irq_chip_generic *irq_gc = NULL;
|
struct irq_chip_generic *irq_gc = NULL;
|
||||||
unsigned int hwirq, ngpio = gc->ngpio;
|
unsigned int ngpio = gc->ngpio;
|
||||||
struct irq_chip_type *ct;
|
struct irq_chip_type *ct;
|
||||||
|
irq_hw_number_t hwirq;
|
||||||
int err, i;
|
int err, i;
|
||||||
|
|
||||||
|
if (memchr_inv(pp->irq, 0, sizeof(pp->irq)) == NULL) {
|
||||||
|
dev_warn(gpio->dev, "no IRQ for port%d\n", pp->idx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
gpio->domain = irq_domain_create_linear(fwnode, ngpio,
|
gpio->domain = irq_domain_create_linear(fwnode, ngpio,
|
||||||
&irq_generic_chip_ops, gpio);
|
&irq_generic_chip_ops, gpio);
|
||||||
if (!gpio->domain)
|
if (!gpio->domain)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
err = irq_alloc_domain_generic_chips(gpio->domain, ngpio, 2,
|
err = irq_alloc_domain_generic_chips(gpio->domain, ngpio, 2,
|
||||||
"gpio-dwapb", handle_level_irq,
|
DWAPB_DRIVER_NAME, handle_bad_irq,
|
||||||
IRQ_NOREQUEST, 0,
|
IRQ_NOREQUEST, 0,
|
||||||
IRQ_GC_INIT_NESTED_LOCK);
|
IRQ_GC_INIT_NESTED_LOCK);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -426,8 +405,6 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
|
||||||
ct->chip.irq_set_type = dwapb_irq_set_type;
|
ct->chip.irq_set_type = dwapb_irq_set_type;
|
||||||
ct->chip.irq_enable = dwapb_irq_enable;
|
ct->chip.irq_enable = dwapb_irq_enable;
|
||||||
ct->chip.irq_disable = dwapb_irq_disable;
|
ct->chip.irq_disable = dwapb_irq_disable;
|
||||||
ct->chip.irq_request_resources = dwapb_irq_reqres;
|
|
||||||
ct->chip.irq_release_resources = dwapb_irq_relres;
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
ct->chip.irq_set_wake = dwapb_irq_set_wake;
|
ct->chip.irq_set_wake = dwapb_irq_set_wake;
|
||||||
#endif
|
#endif
|
||||||
|
@ -437,6 +414,7 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
|
||||||
}
|
}
|
||||||
|
|
||||||
irq_gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
|
irq_gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
|
||||||
|
irq_gc->chip_types[0].handler = handle_level_irq;
|
||||||
irq_gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
|
irq_gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
|
||||||
irq_gc->chip_types[1].handler = handle_edge_irq;
|
irq_gc->chip_types[1].handler = handle_edge_irq;
|
||||||
|
|
||||||
|
@ -444,7 +422,7 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < pp->ngpio; i++) {
|
for (i = 0; i < pp->ngpio; i++) {
|
||||||
if (pp->irq[i] >= 0)
|
if (pp->irq[i])
|
||||||
irq_set_chained_handler_and_data(pp->irq[i],
|
irq_set_chained_handler_and_data(pp->irq[i],
|
||||||
dwapb_irq_handler, gpio);
|
dwapb_irq_handler, gpio);
|
||||||
}
|
}
|
||||||
|
@ -455,7 +433,7 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
|
||||||
*/
|
*/
|
||||||
err = devm_request_irq(gpio->dev, pp->irq[0],
|
err = devm_request_irq(gpio->dev, pp->irq[0],
|
||||||
dwapb_irq_handler_mfd,
|
dwapb_irq_handler_mfd,
|
||||||
IRQF_SHARED, "gpio-dwapb-mfd", gpio);
|
IRQF_SHARED, DWAPB_DRIVER_NAME, gpio);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(gpio->dev, "error requesting IRQ\n");
|
dev_err(gpio->dev, "error requesting IRQ\n");
|
||||||
irq_domain_remove(gpio->domain);
|
irq_domain_remove(gpio->domain);
|
||||||
|
@ -464,7 +442,7 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
|
for (hwirq = 0; hwirq < ngpio; hwirq++)
|
||||||
irq_create_mapping(gpio->domain, hwirq);
|
irq_create_mapping(gpio->domain, hwirq);
|
||||||
|
|
||||||
port->gc.to_irq = dwapb_gpio_to_irq;
|
port->gc.to_irq = dwapb_gpio_to_irq;
|
||||||
|
@ -480,7 +458,7 @@ static void dwapb_irq_teardown(struct dwapb_gpio *gpio)
|
||||||
if (!gpio->domain)
|
if (!gpio->domain)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
|
for (hwirq = 0; hwirq < ngpio; hwirq++)
|
||||||
irq_dispose_mapping(irq_find_mapping(gpio->domain, hwirq));
|
irq_dispose_mapping(irq_find_mapping(gpio->domain, hwirq));
|
||||||
|
|
||||||
irq_domain_remove(gpio->domain);
|
irq_domain_remove(gpio->domain);
|
||||||
|
@ -505,10 +483,9 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_STRIDE);
|
dat = gpio->regs + GPIO_EXT_PORTA + pp->idx * GPIO_EXT_PORT_STRIDE;
|
||||||
set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_STRIDE);
|
set = gpio->regs + GPIO_SWPORTA_DR + pp->idx * GPIO_SWPORT_DR_STRIDE;
|
||||||
dirout = gpio->regs + GPIO_SWPORTA_DDR +
|
dirout = gpio->regs + GPIO_SWPORTA_DDR + pp->idx * GPIO_SWPORT_DDR_STRIDE;
|
||||||
(pp->idx * GPIO_SWPORT_DDR_STRIDE);
|
|
||||||
|
|
||||||
/* This registers 32 GPIO lines per port */
|
/* This registers 32 GPIO lines per port */
|
||||||
err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
|
err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
|
||||||
|
@ -529,40 +506,66 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
|
||||||
if (pp->idx == 0)
|
if (pp->idx == 0)
|
||||||
port->gc.set_config = dwapb_gpio_set_config;
|
port->gc.set_config = dwapb_gpio_set_config;
|
||||||
|
|
||||||
if (pp->has_irq)
|
/* Only port A can provide interrupts in all configurations of the IP */
|
||||||
|
if (pp->idx == 0)
|
||||||
dwapb_configure_irqs(gpio, port, pp);
|
dwapb_configure_irqs(gpio, port, pp);
|
||||||
|
|
||||||
err = gpiochip_add_data(&port->gc, port);
|
err = gpiochip_add_data(&port->gc, port);
|
||||||
if (err)
|
if (err) {
|
||||||
dev_err(gpio->dev, "failed to register gpiochip for port%d\n",
|
dev_err(gpio->dev, "failed to register gpiochip for port%d\n",
|
||||||
port->idx);
|
port->idx);
|
||||||
else
|
return err;
|
||||||
port->is_registered = true;
|
}
|
||||||
|
|
||||||
/* Add GPIO-signaled ACPI event support */
|
/* Add GPIO-signaled ACPI event support */
|
||||||
if (pp->has_irq)
|
|
||||||
acpi_gpiochip_request_interrupts(&port->gc);
|
acpi_gpiochip_request_interrupts(&port->gc);
|
||||||
|
|
||||||
return err;
|
port->is_registered = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwapb_gpio_unregister(struct dwapb_gpio *gpio)
|
static void dwapb_gpio_unregister(struct dwapb_gpio *gpio)
|
||||||
{
|
{
|
||||||
unsigned int m;
|
unsigned int m;
|
||||||
|
|
||||||
for (m = 0; m < gpio->nr_ports; ++m)
|
for (m = 0; m < gpio->nr_ports; ++m) {
|
||||||
if (gpio->ports[m].is_registered)
|
struct dwapb_gpio_port *port = &gpio->ports[m];
|
||||||
gpiochip_remove(&gpio->ports[m].gc);
|
|
||||||
|
if (!port->is_registered)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
acpi_gpiochip_free_interrupts(&port->gc);
|
||||||
|
gpiochip_remove(&port->gc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dwapb_platform_data *
|
static void dwapb_get_irq(struct device *dev, struct fwnode_handle *fwnode,
|
||||||
dwapb_gpio_get_pdata(struct device *dev)
|
struct dwapb_port_property *pp)
|
||||||
|
{
|
||||||
|
struct device_node *np = NULL;
|
||||||
|
int irq = -ENXIO, j;
|
||||||
|
|
||||||
|
if (fwnode_property_read_bool(fwnode, "interrupt-controller"))
|
||||||
|
np = to_of_node(fwnode);
|
||||||
|
|
||||||
|
for (j = 0; j < pp->ngpio; j++) {
|
||||||
|
if (np)
|
||||||
|
irq = of_irq_get(np, j);
|
||||||
|
else if (has_acpi_companion(dev))
|
||||||
|
irq = platform_get_irq_optional(to_platform_device(dev), j);
|
||||||
|
if (irq > 0)
|
||||||
|
pp->irq[j] = irq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev)
|
||||||
{
|
{
|
||||||
struct fwnode_handle *fwnode;
|
struct fwnode_handle *fwnode;
|
||||||
struct dwapb_platform_data *pdata;
|
struct dwapb_platform_data *pdata;
|
||||||
struct dwapb_port_property *pp;
|
struct dwapb_port_property *pp;
|
||||||
int nports;
|
int nports;
|
||||||
int i, j;
|
int i;
|
||||||
|
|
||||||
nports = device_get_child_node_count(dev);
|
nports = device_get_child_node_count(dev);
|
||||||
if (nports == 0)
|
if (nports == 0)
|
||||||
|
@ -580,8 +583,6 @@ dwapb_gpio_get_pdata(struct device *dev)
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
device_for_each_child_node(dev, fwnode) {
|
device_for_each_child_node(dev, fwnode) {
|
||||||
struct device_node *np = NULL;
|
|
||||||
|
|
||||||
pp = &pdata->properties[i++];
|
pp = &pdata->properties[i++];
|
||||||
pp->fwnode = fwnode;
|
pp->fwnode = fwnode;
|
||||||
|
|
||||||
|
@ -593,8 +594,7 @@ dwapb_gpio_get_pdata(struct device *dev)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fwnode_property_read_u32(fwnode, "snps,nr-gpios",
|
if (fwnode_property_read_u32(fwnode, "snps,nr-gpios", &pp->ngpio)) {
|
||||||
&pp->ngpio)) {
|
|
||||||
dev_info(dev,
|
dev_info(dev,
|
||||||
"failed to get number of gpios for port%d\n",
|
"failed to get number of gpios for port%d\n",
|
||||||
i);
|
i);
|
||||||
|
@ -608,28 +608,8 @@ dwapb_gpio_get_pdata(struct device *dev)
|
||||||
* Only port A can provide interrupts in all configurations of
|
* Only port A can provide interrupts in all configurations of
|
||||||
* the IP.
|
* the IP.
|
||||||
*/
|
*/
|
||||||
if (pp->idx != 0)
|
if (pp->idx == 0)
|
||||||
continue;
|
dwapb_get_irq(dev, fwnode, pp);
|
||||||
|
|
||||||
if (dev->of_node && fwnode_property_read_bool(fwnode,
|
|
||||||
"interrupt-controller")) {
|
|
||||||
np = to_of_node(fwnode);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < pp->ngpio; j++) {
|
|
||||||
pp->irq[j] = -ENXIO;
|
|
||||||
|
|
||||||
if (np)
|
|
||||||
pp->irq[j] = of_irq_get(np, j);
|
|
||||||
else if (has_acpi_companion(dev))
|
|
||||||
pp->irq[j] = platform_get_irq(to_platform_device(dev), j);
|
|
||||||
|
|
||||||
if (pp->irq[j] >= 0)
|
|
||||||
pp->has_irq = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pp->has_irq)
|
|
||||||
dev_warn(dev, "no irq for port%d\n", pp->idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pdata;
|
return pdata;
|
||||||
|
@ -689,28 +669,23 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(gpio->regs))
|
if (IS_ERR(gpio->regs))
|
||||||
return PTR_ERR(gpio->regs);
|
return PTR_ERR(gpio->regs);
|
||||||
|
|
||||||
/* Optional bus clock */
|
/* Optional bus and debounce clocks */
|
||||||
gpio->clk = devm_clk_get(&pdev->dev, "bus");
|
gpio->clks[0].id = "bus";
|
||||||
if (!IS_ERR(gpio->clk)) {
|
gpio->clks[1].id = "db";
|
||||||
err = clk_prepare_enable(gpio->clk);
|
err = devm_clk_bulk_get_optional(&pdev->dev, DWAPB_NR_CLOCKS,
|
||||||
|
gpio->clks);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_info(&pdev->dev, "Cannot enable clock\n");
|
dev_err(&pdev->dev, "Cannot get APB/Debounce clocks\n");
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks);
|
||||||
|
if (err) {
|
||||||
|
dev_err(&pdev->dev, "Cannot enable APB/Debounce clocks\n");
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio->flags = 0;
|
gpio->flags = (uintptr_t)device_get_match_data(dev);
|
||||||
if (dev->of_node) {
|
|
||||||
gpio->flags = (uintptr_t)of_device_get_match_data(dev);
|
|
||||||
} else if (has_acpi_companion(dev)) {
|
|
||||||
const struct acpi_device_id *acpi_id;
|
|
||||||
|
|
||||||
acpi_id = acpi_match_device(dwapb_acpi_match, dev);
|
|
||||||
if (acpi_id) {
|
|
||||||
if (acpi_id->driver_data)
|
|
||||||
gpio->flags = acpi_id->driver_data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < gpio->nr_ports; i++) {
|
for (i = 0; i < gpio->nr_ports; i++) {
|
||||||
err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i);
|
err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i);
|
||||||
|
@ -724,7 +699,7 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
|
||||||
out_unregister:
|
out_unregister:
|
||||||
dwapb_gpio_unregister(gpio);
|
dwapb_gpio_unregister(gpio);
|
||||||
dwapb_irq_teardown(gpio);
|
dwapb_irq_teardown(gpio);
|
||||||
clk_disable_unprepare(gpio->clk);
|
clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -736,7 +711,7 @@ static int dwapb_gpio_remove(struct platform_device *pdev)
|
||||||
dwapb_gpio_unregister(gpio);
|
dwapb_gpio_unregister(gpio);
|
||||||
dwapb_irq_teardown(gpio);
|
dwapb_irq_teardown(gpio);
|
||||||
reset_control_assert(gpio->rst);
|
reset_control_assert(gpio->rst);
|
||||||
clk_disable_unprepare(gpio->clk);
|
clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -755,8 +730,6 @@ static int dwapb_gpio_suspend(struct device *dev)
|
||||||
unsigned int idx = gpio->ports[i].idx;
|
unsigned int idx = gpio->ports[i].idx;
|
||||||
struct dwapb_context *ctx = gpio->ports[i].ctx;
|
struct dwapb_context *ctx = gpio->ports[i].ctx;
|
||||||
|
|
||||||
BUG_ON(!ctx);
|
|
||||||
|
|
||||||
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
|
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
|
||||||
ctx->dir = dwapb_read(gpio, offset);
|
ctx->dir = dwapb_read(gpio, offset);
|
||||||
|
|
||||||
|
@ -775,13 +748,12 @@ static int dwapb_gpio_suspend(struct device *dev)
|
||||||
ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
|
ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
|
||||||
|
|
||||||
/* Mask out interrupts */
|
/* Mask out interrupts */
|
||||||
dwapb_write(gpio, GPIO_INTMASK,
|
dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en);
|
||||||
0xffffffff & ~ctx->wake_en);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
||||||
|
|
||||||
clk_disable_unprepare(gpio->clk);
|
clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -791,10 +763,13 @@ static int dwapb_gpio_resume(struct device *dev)
|
||||||
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
|
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
|
||||||
struct gpio_chip *gc = &gpio->ports[0].gc;
|
struct gpio_chip *gc = &gpio->ports[0].gc;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int i;
|
int i, err;
|
||||||
|
|
||||||
if (!IS_ERR(gpio->clk))
|
err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks);
|
||||||
clk_prepare_enable(gpio->clk);
|
if (err) {
|
||||||
|
dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
||||||
for (i = 0; i < gpio->nr_ports; i++) {
|
for (i = 0; i < gpio->nr_ports; i++) {
|
||||||
|
@ -802,8 +777,6 @@ static int dwapb_gpio_resume(struct device *dev)
|
||||||
unsigned int idx = gpio->ports[i].idx;
|
unsigned int idx = gpio->ports[i].idx;
|
||||||
struct dwapb_context *ctx = gpio->ports[i].ctx;
|
struct dwapb_context *ctx = gpio->ports[i].ctx;
|
||||||
|
|
||||||
BUG_ON(!ctx);
|
|
||||||
|
|
||||||
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
|
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
|
||||||
dwapb_write(gpio, offset, ctx->data);
|
dwapb_write(gpio, offset, ctx->data);
|
||||||
|
|
||||||
|
@ -836,10 +809,10 @@ static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend,
|
||||||
|
|
||||||
static struct platform_driver dwapb_gpio_driver = {
|
static struct platform_driver dwapb_gpio_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gpio-dwapb",
|
.name = DWAPB_DRIVER_NAME,
|
||||||
.pm = &dwapb_gpio_pm_ops,
|
.pm = &dwapb_gpio_pm_ops,
|
||||||
.of_match_table = of_match_ptr(dwapb_of_match),
|
.of_match_table = dwapb_of_match,
|
||||||
.acpi_match_table = ACPI_PTR(dwapb_acpi_match),
|
.acpi_match_table = dwapb_acpi_match,
|
||||||
},
|
},
|
||||||
.probe = dwapb_gpio_probe,
|
.probe = dwapb_gpio_probe,
|
||||||
.remove = dwapb_gpio_remove,
|
.remove = dwapb_gpio_remove,
|
||||||
|
@ -850,3 +823,4 @@ module_platform_driver(dwapb_gpio_driver);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Jamie Iles");
|
MODULE_AUTHOR("Jamie Iles");
|
||||||
MODULE_DESCRIPTION("Synopsys DesignWare APB GPIO driver");
|
MODULE_DESCRIPTION("Synopsys DesignWare APB GPIO driver");
|
||||||
|
MODULE_ALIAS("platform:" DWAPB_DRIVER_NAME);
|
||||||
|
|
|
@ -36,9 +36,19 @@
|
||||||
#define SIO_F71889A_ID 0x1005 /* F71889A chipset ID */
|
#define SIO_F71889A_ID 0x1005 /* F71889A chipset ID */
|
||||||
#define SIO_F81866_ID 0x1010 /* F81866 chipset ID */
|
#define SIO_F81866_ID 0x1010 /* F81866 chipset ID */
|
||||||
#define SIO_F81804_ID 0x1502 /* F81804 chipset ID, same for f81966 */
|
#define SIO_F81804_ID 0x1502 /* F81804 chipset ID, same for f81966 */
|
||||||
|
#define SIO_F81865_ID 0x0704 /* F81865 chipset ID */
|
||||||
|
|
||||||
|
|
||||||
enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866, f81804 };
|
enum chips {
|
||||||
|
f71869,
|
||||||
|
f71869a,
|
||||||
|
f71882fg,
|
||||||
|
f71889a,
|
||||||
|
f71889f,
|
||||||
|
f81866,
|
||||||
|
f81804,
|
||||||
|
f81865,
|
||||||
|
};
|
||||||
|
|
||||||
static const char * const f7188x_names[] = {
|
static const char * const f7188x_names[] = {
|
||||||
"f71869",
|
"f71869",
|
||||||
|
@ -48,6 +58,7 @@ static const char * const f7188x_names[] = {
|
||||||
"f71889f",
|
"f71889f",
|
||||||
"f81866",
|
"f81866",
|
||||||
"f81804",
|
"f81804",
|
||||||
|
"f81865",
|
||||||
};
|
};
|
||||||
|
|
||||||
struct f7188x_sio {
|
struct f7188x_sio {
|
||||||
|
@ -233,6 +244,15 @@ static struct f7188x_gpio_bank f81804_gpio_bank[] = {
|
||||||
F7188X_GPIO_BANK(90, 8, 0x98),
|
F7188X_GPIO_BANK(90, 8, 0x98),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct f7188x_gpio_bank f81865_gpio_bank[] = {
|
||||||
|
F7188X_GPIO_BANK(0, 8, 0xF0),
|
||||||
|
F7188X_GPIO_BANK(10, 8, 0xE0),
|
||||||
|
F7188X_GPIO_BANK(20, 8, 0xD0),
|
||||||
|
F7188X_GPIO_BANK(30, 8, 0xC0),
|
||||||
|
F7188X_GPIO_BANK(40, 8, 0xB0),
|
||||||
|
F7188X_GPIO_BANK(50, 8, 0xA0),
|
||||||
|
F7188X_GPIO_BANK(60, 5, 0x90),
|
||||||
|
};
|
||||||
|
|
||||||
static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
|
static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
|
||||||
{
|
{
|
||||||
|
@ -425,6 +445,10 @@ static int f7188x_gpio_probe(struct platform_device *pdev)
|
||||||
data->nr_bank = ARRAY_SIZE(f81804_gpio_bank);
|
data->nr_bank = ARRAY_SIZE(f81804_gpio_bank);
|
||||||
data->bank = f81804_gpio_bank;
|
data->bank = f81804_gpio_bank;
|
||||||
break;
|
break;
|
||||||
|
case f81865:
|
||||||
|
data->nr_bank = ARRAY_SIZE(f81865_gpio_bank);
|
||||||
|
data->bank = f81865_gpio_bank;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -490,6 +514,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio)
|
||||||
case SIO_F81804_ID:
|
case SIO_F81804_ID:
|
||||||
sio->type = f81804;
|
sio->type = f81804;
|
||||||
break;
|
break;
|
||||||
|
case SIO_F81865_ID:
|
||||||
|
sio->type = f81865;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
|
pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
|
||||||
goto err;
|
goto err;
|
||||||
|
|
|
@ -193,7 +193,7 @@ static int ftgpio_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
|
||||||
if (val == deb_div) {
|
if (val == deb_div) {
|
||||||
/*
|
/*
|
||||||
* The debounce timer happens to already be set to the
|
* The debounce timer happens to already be set to the
|
||||||
* desireable value, what a coincidence! We can just enable
|
* desirable value, what a coincidence! We can just enable
|
||||||
* debounce on this GPIO line and return. This happens more
|
* debounce on this GPIO line and return. This happens more
|
||||||
* often than you think, for example when all GPIO keys
|
* often than you think, for example when all GPIO keys
|
||||||
* on a system are requesting the same debounce interval.
|
* on a system are requesting the same debounce interval.
|
||||||
|
|
|
@ -89,7 +89,7 @@ static struct {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct gpio_chip chip;
|
struct gpio_chip chip;
|
||||||
struct resource *gpio_base; /* GPIO IO base */
|
struct resource *gpio_base; /* GPIO IO base */
|
||||||
struct resource *pm_base; /* Power Mangagment IO base */
|
struct resource *pm_base; /* Power Management IO base */
|
||||||
struct ichx_desc *desc; /* Pointer to chipset-specific description */
|
struct ichx_desc *desc; /* Pointer to chipset-specific description */
|
||||||
u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */
|
u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */
|
||||||
u8 use_gpio; /* Which GPIO groups are usable */
|
u8 use_gpio; /* Which GPIO groups are usable */
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
|
|
||||||
static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
|
static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||||
{
|
{
|
||||||
struct max7301 *ts = gpiochip_get_data(chip);
|
struct max7301 *ts = container_of(chip, struct max7301, chip);
|
||||||
u8 *config;
|
u8 *config;
|
||||||
u8 offset_bits, pin_config;
|
u8 offset_bits, pin_config;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -89,7 +89,7 @@ static int __max7301_set(struct max7301 *ts, unsigned offset, int value)
|
||||||
static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,
|
static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,
|
||||||
int value)
|
int value)
|
||||||
{
|
{
|
||||||
struct max7301 *ts = gpiochip_get_data(chip);
|
struct max7301 *ts = container_of(chip, struct max7301, chip);
|
||||||
u8 *config;
|
u8 *config;
|
||||||
u8 offset_bits;
|
u8 offset_bits;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -189,10 +189,6 @@ int __max730x_probe(struct max7301 *ts)
|
||||||
ts->chip.parent = dev;
|
ts->chip.parent = dev;
|
||||||
ts->chip.owner = THIS_MODULE;
|
ts->chip.owner = THIS_MODULE;
|
||||||
|
|
||||||
ret = gpiochip_add_data(&ts->chip, ts);
|
|
||||||
if (ret)
|
|
||||||
goto exit_destroy;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* initialize pullups according to platform data and cache the
|
* initialize pullups according to platform data and cache the
|
||||||
* register values for later use.
|
* register values for later use.
|
||||||
|
@ -214,6 +210,8 @@ int __max730x_probe(struct max7301 *ts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = gpiochip_add_data(&ts->chip, ts);
|
||||||
|
if (!ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
exit_destroy:
|
exit_destroy:
|
||||||
|
|
|
@ -145,7 +145,9 @@ static int mb86s70_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
|
||||||
|
|
||||||
for (index = 0;; index++) {
|
for (index = 0;; index++) {
|
||||||
irq = platform_get_irq(to_platform_device(gc->parent), index);
|
irq = platform_get_irq(to_platform_device(gc->parent), index);
|
||||||
if (irq <= 0)
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
if (irq == 0)
|
||||||
break;
|
break;
|
||||||
if (irq_get_irq_data(irq)->hwirq == offset)
|
if (irq_get_irq_data(irq)->hwirq == offset)
|
||||||
return irq;
|
return irq;
|
||||||
|
@ -168,15 +170,13 @@ static int mb86s70_gpio_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(gchip->base))
|
if (IS_ERR(gchip->base))
|
||||||
return PTR_ERR(gchip->base);
|
return PTR_ERR(gchip->base);
|
||||||
|
|
||||||
if (!has_acpi_companion(&pdev->dev)) {
|
gchip->clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||||
gchip->clk = devm_clk_get(&pdev->dev, NULL);
|
|
||||||
if (IS_ERR(gchip->clk))
|
if (IS_ERR(gchip->clk))
|
||||||
return PTR_ERR(gchip->clk);
|
return PTR_ERR(gchip->clk);
|
||||||
|
|
||||||
ret = clk_prepare_enable(gchip->clk);
|
ret = clk_prepare_enable(gchip->clk);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_init(&gchip->lock);
|
spin_lock_init(&gchip->lock);
|
||||||
|
|
||||||
|
@ -186,15 +186,13 @@ static int mb86s70_gpio_probe(struct platform_device *pdev)
|
||||||
gchip->gc.free = mb86s70_gpio_free;
|
gchip->gc.free = mb86s70_gpio_free;
|
||||||
gchip->gc.get = mb86s70_gpio_get;
|
gchip->gc.get = mb86s70_gpio_get;
|
||||||
gchip->gc.set = mb86s70_gpio_set;
|
gchip->gc.set = mb86s70_gpio_set;
|
||||||
|
gchip->gc.to_irq = mb86s70_gpio_to_irq;
|
||||||
gchip->gc.label = dev_name(&pdev->dev);
|
gchip->gc.label = dev_name(&pdev->dev);
|
||||||
gchip->gc.ngpio = 32;
|
gchip->gc.ngpio = 32;
|
||||||
gchip->gc.owner = THIS_MODULE;
|
gchip->gc.owner = THIS_MODULE;
|
||||||
gchip->gc.parent = &pdev->dev;
|
gchip->gc.parent = &pdev->dev;
|
||||||
gchip->gc.base = -1;
|
gchip->gc.base = -1;
|
||||||
|
|
||||||
if (has_acpi_companion(&pdev->dev))
|
|
||||||
gchip->gc.to_irq = mb86s70_gpio_to_irq;
|
|
||||||
|
|
||||||
ret = gpiochip_add_data(&gchip->gc, gchip);
|
ret = gpiochip_add_data(&gchip->gc, gchip);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "couldn't register gpio driver\n");
|
dev_err(&pdev->dev, "couldn't register gpio driver\n");
|
||||||
|
@ -202,7 +200,6 @@ static int mb86s70_gpio_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_acpi_companion(&pdev->dev))
|
|
||||||
acpi_gpiochip_request_interrupts(&gchip->gc);
|
acpi_gpiochip_request_interrupts(&gchip->gc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -212,7 +209,6 @@ static int mb86s70_gpio_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev);
|
struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
if (has_acpi_companion(&pdev->dev))
|
|
||||||
acpi_gpiochip_free_interrupts(&gchip->gc);
|
acpi_gpiochip_free_interrupts(&gchip->gc);
|
||||||
gpiochip_remove(&gchip->gc);
|
gpiochip_remove(&gchip->gc);
|
||||||
clk_disable_unprepare(gchip->clk);
|
clk_disable_unprepare(gchip->clk);
|
||||||
|
|
|
@ -443,8 +443,8 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
||||||
|
|
||||||
base = pcim_iomap_table(pdev)[1];
|
base = pcim_iomap_table(pdev)[1];
|
||||||
|
|
||||||
irq_base = readl(base);
|
irq_base = readl(base + 0 * sizeof(u32));
|
||||||
gpio_base = readl(sizeof(u32) + base);
|
gpio_base = readl(base + 1 * sizeof(u32));
|
||||||
|
|
||||||
/* Release the IO mapping, since we already get the info from BAR1 */
|
/* Release the IO mapping, since we already get the info from BAR1 */
|
||||||
pcim_iounmap_regions(pdev, BIT(1));
|
pcim_iounmap_regions(pdev, BIT(1));
|
||||||
|
@ -473,6 +473,10 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
||||||
|
|
||||||
raw_spin_lock_init(&priv->lock);
|
raw_spin_lock_init(&priv->lock);
|
||||||
|
|
||||||
|
retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
|
||||||
|
if (retval < 0)
|
||||||
|
return retval;
|
||||||
|
|
||||||
girq = &priv->chip.irq;
|
girq = &priv->chip.irq;
|
||||||
girq->chip = &mrfld_irqchip;
|
girq->chip = &mrfld_irqchip;
|
||||||
girq->init_hw = mrfld_irq_init_hw;
|
girq->init_hw = mrfld_irq_init_hw;
|
||||||
|
@ -482,7 +486,7 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
||||||
sizeof(*girq->parents), GFP_KERNEL);
|
sizeof(*girq->parents), GFP_KERNEL);
|
||||||
if (!girq->parents)
|
if (!girq->parents)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
girq->parents[0] = pdev->irq;
|
girq->parents[0] = pci_irq_vector(pdev, 0);
|
||||||
girq->first = irq_base;
|
girq->first = irq_base;
|
||||||
girq->default_type = IRQ_TYPE_NONE;
|
girq->default_type = IRQ_TYPE_NONE;
|
||||||
girq->handler = handle_bad_irq;
|
girq->handler = handle_bad_irq;
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include <linux/resource.h>
|
#include <linux/resource.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/version.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are 3 YU GPIO blocks:
|
* There are 3 YU GPIO blocks:
|
||||||
|
@ -110,8 +109,8 @@ static int mlxbf2_gpio_get_lock_res(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
yu_arm_gpio_lock_param.io = devm_ioremap(dev, res->start, size);
|
yu_arm_gpio_lock_param.io = devm_ioremap(dev, res->start, size);
|
||||||
if (IS_ERR(yu_arm_gpio_lock_param.io))
|
if (!yu_arm_gpio_lock_param.io)
|
||||||
ret = PTR_ERR(yu_arm_gpio_lock_param.io);
|
ret = -ENOMEM;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mutex_unlock(yu_arm_gpio_lock_param.lock);
|
mutex_unlock(yu_arm_gpio_lock_param.lock);
|
||||||
|
|
|
@ -36,7 +36,7 @@ struct ltq_mm {
|
||||||
* @chip: Pointer to our private data structure.
|
* @chip: Pointer to our private data structure.
|
||||||
*
|
*
|
||||||
* Write the shadow value to the EBU to set the gpios. We need to set the
|
* Write the shadow value to the EBU to set the gpios. We need to set the
|
||||||
* global EBU lock to make sure that PCI/MTD dont break.
|
* global EBU lock to make sure that PCI/MTD don't break.
|
||||||
*/
|
*/
|
||||||
static void ltq_mm_apply(struct ltq_mm *chip)
|
static void ltq_mm_apply(struct ltq_mm *chip)
|
||||||
{
|
{
|
||||||
|
|
|
@ -306,37 +306,39 @@ static const struct regmap_config pca953x_i2c_regmap = {
|
||||||
.writeable_reg = pca953x_writeable_register,
|
.writeable_reg = pca953x_writeable_register,
|
||||||
.volatile_reg = pca953x_volatile_register,
|
.volatile_reg = pca953x_volatile_register,
|
||||||
|
|
||||||
|
.disable_locking = true,
|
||||||
.cache_type = REGCACHE_RBTREE,
|
.cache_type = REGCACHE_RBTREE,
|
||||||
/* REVISIT: should be 0x7f but some 24 bit chips use REG_ADDR_AI */
|
.max_register = 0x7f,
|
||||||
.max_register = 0xff,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off,
|
static const struct regmap_config pca953x_ai_i2c_regmap = {
|
||||||
bool write, bool addrinc)
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
|
||||||
|
.read_flag_mask = REG_ADDR_AI,
|
||||||
|
.write_flag_mask = REG_ADDR_AI,
|
||||||
|
|
||||||
|
.readable_reg = pca953x_readable_register,
|
||||||
|
.writeable_reg = pca953x_writeable_register,
|
||||||
|
.volatile_reg = pca953x_volatile_register,
|
||||||
|
|
||||||
|
.cache_type = REGCACHE_RBTREE,
|
||||||
|
.max_register = 0x7f,
|
||||||
|
};
|
||||||
|
|
||||||
|
static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off)
|
||||||
{
|
{
|
||||||
int bank_shift = pca953x_bank_shift(chip);
|
int bank_shift = pca953x_bank_shift(chip);
|
||||||
int addr = (reg & PCAL_GPIO_MASK) << bank_shift;
|
int addr = (reg & PCAL_GPIO_MASK) << bank_shift;
|
||||||
int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
|
int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
|
||||||
u8 regaddr = pinctrl | addr | (off / BANK_SZ);
|
u8 regaddr = pinctrl | addr | (off / BANK_SZ);
|
||||||
|
|
||||||
/* Single byte read doesn't need AI bit set. */
|
|
||||||
if (!addrinc)
|
|
||||||
return regaddr;
|
|
||||||
|
|
||||||
/* Chips with 24 and more GPIOs always support Auto Increment */
|
|
||||||
if (write && NBANK(chip) > 2)
|
|
||||||
regaddr |= REG_ADDR_AI;
|
|
||||||
|
|
||||||
/* PCA9575 needs address-increment on multi-byte writes */
|
|
||||||
if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE)
|
|
||||||
regaddr |= REG_ADDR_AI;
|
|
||||||
|
|
||||||
return regaddr;
|
return regaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
|
static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
|
||||||
{
|
{
|
||||||
u8 regaddr = pca953x_recalc_addr(chip, reg, 0, true, true);
|
u8 regaddr = pca953x_recalc_addr(chip, reg, 0);
|
||||||
u8 value[MAX_BANK];
|
u8 value[MAX_BANK];
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
|
@ -354,7 +356,7 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long
|
||||||
|
|
||||||
static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
|
static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
|
||||||
{
|
{
|
||||||
u8 regaddr = pca953x_recalc_addr(chip, reg, 0, false, true);
|
u8 regaddr = pca953x_recalc_addr(chip, reg, 0);
|
||||||
u8 value[MAX_BANK];
|
u8 value[MAX_BANK];
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
|
@ -373,8 +375,7 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *
|
||||||
static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
|
static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
|
||||||
{
|
{
|
||||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||||
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
|
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
|
||||||
true, false);
|
|
||||||
u8 bit = BIT(off % BANK_SZ);
|
u8 bit = BIT(off % BANK_SZ);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -388,10 +389,8 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
|
||||||
unsigned off, int val)
|
unsigned off, int val)
|
||||||
{
|
{
|
||||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||||
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
|
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
|
||||||
true, false);
|
u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off);
|
||||||
u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
|
|
||||||
true, false);
|
|
||||||
u8 bit = BIT(off % BANK_SZ);
|
u8 bit = BIT(off % BANK_SZ);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -411,8 +410,7 @@ exit:
|
||||||
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
|
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
|
||||||
{
|
{
|
||||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||||
u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off,
|
u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off);
|
||||||
true, false);
|
|
||||||
u8 bit = BIT(off % BANK_SZ);
|
u8 bit = BIT(off % BANK_SZ);
|
||||||
u32 reg_val;
|
u32 reg_val;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -436,8 +434,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
|
||||||
static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
|
static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
|
||||||
{
|
{
|
||||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||||
u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
|
u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off);
|
||||||
true, false);
|
|
||||||
u8 bit = BIT(off % BANK_SZ);
|
u8 bit = BIT(off % BANK_SZ);
|
||||||
|
|
||||||
mutex_lock(&chip->i2c_lock);
|
mutex_lock(&chip->i2c_lock);
|
||||||
|
@ -448,8 +445,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
|
||||||
static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
|
static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
|
||||||
{
|
{
|
||||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||||
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
|
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
|
||||||
true, false);
|
|
||||||
u8 bit = BIT(off % BANK_SZ);
|
u8 bit = BIT(off % BANK_SZ);
|
||||||
u32 reg_val;
|
u32 reg_val;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -466,6 +462,23 @@ static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
|
||||||
return GPIO_LINE_DIRECTION_OUT;
|
return GPIO_LINE_DIRECTION_OUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pca953x_gpio_get_multiple(struct gpio_chip *gc,
|
||||||
|
unsigned long *mask, unsigned long *bits)
|
||||||
|
{
|
||||||
|
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||||
|
DECLARE_BITMAP(reg_val, MAX_LINE);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&chip->i2c_lock);
|
||||||
|
ret = pca953x_read_regs(chip, chip->regs->input, reg_val);
|
||||||
|
mutex_unlock(&chip->i2c_lock);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
bitmap_replace(bits, bits, reg_val, mask, gc->ngpio);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
|
static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
|
||||||
unsigned long *mask, unsigned long *bits)
|
unsigned long *mask, unsigned long *bits)
|
||||||
{
|
{
|
||||||
|
@ -489,10 +502,8 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
|
||||||
unsigned int offset,
|
unsigned int offset,
|
||||||
unsigned long config)
|
unsigned long config)
|
||||||
{
|
{
|
||||||
u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset,
|
u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset);
|
||||||
true, false);
|
u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset);
|
||||||
u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset,
|
|
||||||
true, false);
|
|
||||||
u8 bit = BIT(offset % BANK_SZ);
|
u8 bit = BIT(offset % BANK_SZ);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -551,6 +562,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
|
||||||
gc->get = pca953x_gpio_get_value;
|
gc->get = pca953x_gpio_get_value;
|
||||||
gc->set = pca953x_gpio_set_value;
|
gc->set = pca953x_gpio_set_value;
|
||||||
gc->get_direction = pca953x_gpio_get_direction;
|
gc->get_direction = pca953x_gpio_get_direction;
|
||||||
|
gc->get_multiple = pca953x_gpio_get_multiple;
|
||||||
gc->set_multiple = pca953x_gpio_set_multiple;
|
gc->set_multiple = pca953x_gpio_set_multiple;
|
||||||
gc->set_config = pca953x_gpio_set_config;
|
gc->set_config = pca953x_gpio_set_config;
|
||||||
gc->can_sleep = true;
|
gc->can_sleep = true;
|
||||||
|
@ -863,6 +875,7 @@ static int pca953x_probe(struct i2c_client *client,
|
||||||
int ret;
|
int ret;
|
||||||
u32 invert = 0;
|
u32 invert = 0;
|
||||||
struct regulator *reg;
|
struct regulator *reg;
|
||||||
|
const struct regmap_config *regmap_config;
|
||||||
|
|
||||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||||
if (chip == NULL)
|
if (chip == NULL)
|
||||||
|
@ -925,7 +938,17 @@ static int pca953x_probe(struct i2c_client *client,
|
||||||
|
|
||||||
i2c_set_clientdata(client, chip);
|
i2c_set_clientdata(client, chip);
|
||||||
|
|
||||||
chip->regmap = devm_regmap_init_i2c(client, &pca953x_i2c_regmap);
|
pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
|
||||||
|
|
||||||
|
if (NBANK(chip) > 2 || PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
|
||||||
|
dev_info(&client->dev, "using AI\n");
|
||||||
|
regmap_config = &pca953x_ai_i2c_regmap;
|
||||||
|
} else {
|
||||||
|
dev_info(&client->dev, "using no AI\n");
|
||||||
|
regmap_config = &pca953x_i2c_regmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->regmap = devm_regmap_init_i2c(client, regmap_config);
|
||||||
if (IS_ERR(chip->regmap)) {
|
if (IS_ERR(chip->regmap)) {
|
||||||
ret = PTR_ERR(chip->regmap);
|
ret = PTR_ERR(chip->regmap);
|
||||||
goto err_exit;
|
goto err_exit;
|
||||||
|
@ -956,7 +979,6 @@ static int pca953x_probe(struct i2c_client *client,
|
||||||
/* initialize cached registers from their original values.
|
/* initialize cached registers from their original values.
|
||||||
* we can't share this chip with another i2c master.
|
* we can't share this chip with another i2c master.
|
||||||
*/
|
*/
|
||||||
pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
|
|
||||||
|
|
||||||
if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
|
if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
|
||||||
chip->regs = &pca953x_regs;
|
chip->regs = &pca953x_regs;
|
||||||
|
@ -1154,7 +1176,7 @@ static struct i2c_driver pca953x_driver = {
|
||||||
.name = "pca953x",
|
.name = "pca953x",
|
||||||
.pm = &pca953x_pm_ops,
|
.pm = &pca953x_pm_ops,
|
||||||
.of_match_table = pca953x_dt_ids,
|
.of_match_table = pca953x_dt_ids,
|
||||||
.acpi_match_table = ACPI_PTR(pca953x_acpi_ids),
|
.acpi_match_table = pca953x_acpi_ids,
|
||||||
},
|
},
|
||||||
.probe = pca953x_probe,
|
.probe = pca953x_probe,
|
||||||
.remove = pca953x_remove,
|
.remove = pca953x_remove,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
|
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
|
||||||
*/
|
*/
|
||||||
|
#include <linux/bits.h>
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
@ -11,11 +12,11 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#define PCH_EDGE_FALLING 0
|
#define PCH_EDGE_FALLING 0
|
||||||
#define PCH_EDGE_RISING BIT(0)
|
#define PCH_EDGE_RISING 1
|
||||||
#define PCH_LEVEL_L BIT(1)
|
#define PCH_LEVEL_L 2
|
||||||
#define PCH_LEVEL_H (BIT(0) | BIT(1))
|
#define PCH_LEVEL_H 3
|
||||||
#define PCH_EDGE_BOTH BIT(2)
|
#define PCH_EDGE_BOTH 4
|
||||||
#define PCH_IM_MASK (BIT(0) | BIT(1) | BIT(2))
|
#define PCH_IM_MASK GENMASK(2, 0)
|
||||||
|
|
||||||
#define PCH_IRQ_BASE 24
|
#define PCH_IRQ_BASE 24
|
||||||
|
|
||||||
|
@ -103,9 +104,9 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
|
||||||
spin_lock_irqsave(&chip->spinlock, flags);
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
reg_val = ioread32(&chip->reg->po);
|
reg_val = ioread32(&chip->reg->po);
|
||||||
if (val)
|
if (val)
|
||||||
reg_val |= (1 << nr);
|
reg_val |= BIT(nr);
|
||||||
else
|
else
|
||||||
reg_val &= ~(1 << nr);
|
reg_val &= ~BIT(nr);
|
||||||
|
|
||||||
iowrite32(reg_val, &chip->reg->po);
|
iowrite32(reg_val, &chip->reg->po);
|
||||||
spin_unlock_irqrestore(&chip->spinlock, flags);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
|
@ -115,7 +116,7 @@ static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
|
||||||
{
|
{
|
||||||
struct pch_gpio *chip = gpiochip_get_data(gpio);
|
struct pch_gpio *chip = gpiochip_get_data(gpio);
|
||||||
|
|
||||||
return (ioread32(&chip->reg->pi) >> nr) & 1;
|
return !!(ioread32(&chip->reg->pi) & BIT(nr));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||||
|
@ -130,13 +131,14 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||||
|
|
||||||
reg_val = ioread32(&chip->reg->po);
|
reg_val = ioread32(&chip->reg->po);
|
||||||
if (val)
|
if (val)
|
||||||
reg_val |= (1 << nr);
|
reg_val |= BIT(nr);
|
||||||
else
|
else
|
||||||
reg_val &= ~(1 << nr);
|
reg_val &= ~BIT(nr);
|
||||||
iowrite32(reg_val, &chip->reg->po);
|
iowrite32(reg_val, &chip->reg->po);
|
||||||
|
|
||||||
pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
|
pm = ioread32(&chip->reg->pm);
|
||||||
pm |= (1 << nr);
|
pm &= BIT(gpio_pins[chip->ioh]) - 1;
|
||||||
|
pm |= BIT(nr);
|
||||||
iowrite32(pm, &chip->reg->pm);
|
iowrite32(pm, &chip->reg->pm);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&chip->spinlock, flags);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
|
@ -151,8 +153,9 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&chip->spinlock, flags);
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
|
pm = ioread32(&chip->reg->pm);
|
||||||
pm &= ~(1 << nr);
|
pm &= BIT(gpio_pins[chip->ioh]) - 1;
|
||||||
|
pm &= ~BIT(nr);
|
||||||
iowrite32(pm, &chip->reg->pm);
|
iowrite32(pm, &chip->reg->pm);
|
||||||
spin_unlock_irqrestore(&chip->spinlock, flags);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
|
|
||||||
|
@ -226,17 +229,15 @@ static int pch_irq_type(struct irq_data *d, unsigned int type)
|
||||||
int ch, irq = d->irq;
|
int ch, irq = d->irq;
|
||||||
|
|
||||||
ch = irq - chip->irq_base;
|
ch = irq - chip->irq_base;
|
||||||
if (irq <= chip->irq_base + 7) {
|
if (irq < chip->irq_base + 8) {
|
||||||
im_reg = &chip->reg->im0;
|
im_reg = &chip->reg->im0;
|
||||||
im_pos = ch;
|
im_pos = ch - 0;
|
||||||
} else {
|
} else {
|
||||||
im_reg = &chip->reg->im1;
|
im_reg = &chip->reg->im1;
|
||||||
im_pos = ch - 8;
|
im_pos = ch - 8;
|
||||||
}
|
}
|
||||||
dev_dbg(chip->dev, "irq=%d type=%d ch=%d pos=%d\n", irq, type, ch, im_pos);
|
dev_dbg(chip->dev, "irq=%d type=%d ch=%d pos=%d\n", irq, type, ch, im_pos);
|
||||||
|
|
||||||
spin_lock_irqsave(&chip->spinlock, flags);
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case IRQ_TYPE_EDGE_RISING:
|
case IRQ_TYPE_EDGE_RISING:
|
||||||
val = PCH_EDGE_RISING;
|
val = PCH_EDGE_RISING;
|
||||||
|
@ -254,20 +255,21 @@ static int pch_irq_type(struct irq_data *d, unsigned int type)
|
||||||
val = PCH_LEVEL_L;
|
val = PCH_LEVEL_L;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
goto unlock;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
|
|
||||||
/* Set interrupt mode */
|
/* Set interrupt mode */
|
||||||
im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4));
|
im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4));
|
||||||
iowrite32(im | (val << (im_pos * 4)), im_reg);
|
iowrite32(im | (val << (im_pos * 4)), im_reg);
|
||||||
|
|
||||||
/* And the handler */
|
/* And the handler */
|
||||||
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
|
if (type & IRQ_TYPE_LEVEL_MASK)
|
||||||
irq_set_handler_locked(d, handle_level_irq);
|
irq_set_handler_locked(d, handle_level_irq);
|
||||||
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
|
else if (type & IRQ_TYPE_EDGE_BOTH)
|
||||||
irq_set_handler_locked(d, handle_edge_irq);
|
irq_set_handler_locked(d, handle_edge_irq);
|
||||||
|
|
||||||
unlock:
|
|
||||||
spin_unlock_irqrestore(&chip->spinlock, flags);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -277,7 +279,7 @@ static void pch_irq_unmask(struct irq_data *d)
|
||||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
struct pch_gpio *chip = gc->private;
|
struct pch_gpio *chip = gc->private;
|
||||||
|
|
||||||
iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imaskclr);
|
iowrite32(BIT(d->irq - chip->irq_base), &chip->reg->imaskclr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pch_irq_mask(struct irq_data *d)
|
static void pch_irq_mask(struct irq_data *d)
|
||||||
|
@ -285,7 +287,7 @@ static void pch_irq_mask(struct irq_data *d)
|
||||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
struct pch_gpio *chip = gc->private;
|
struct pch_gpio *chip = gc->private;
|
||||||
|
|
||||||
iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imask);
|
iowrite32(BIT(d->irq - chip->irq_base), &chip->reg->imask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pch_irq_ack(struct irq_data *d)
|
static void pch_irq_ack(struct irq_data *d)
|
||||||
|
@ -293,21 +295,22 @@ static void pch_irq_ack(struct irq_data *d)
|
||||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
struct pch_gpio *chip = gc->private;
|
struct pch_gpio *chip = gc->private;
|
||||||
|
|
||||||
iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->iclr);
|
iowrite32(BIT(d->irq - chip->irq_base), &chip->reg->iclr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
|
static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct pch_gpio *chip = dev_id;
|
struct pch_gpio *chip = dev_id;
|
||||||
unsigned long reg_val = ioread32(&chip->reg->istatus);
|
unsigned long reg_val = ioread32(&chip->reg->istatus);
|
||||||
int i, ret = IRQ_NONE;
|
int i;
|
||||||
|
|
||||||
for_each_set_bit(i, ®_val, gpio_pins[chip->ioh]) {
|
dev_dbg(chip->dev, "irq=%d status=0x%lx\n", irq, reg_val);
|
||||||
dev_dbg(chip->dev, "[%d]:irq=%d status=0x%lx\n", i, irq, reg_val);
|
|
||||||
|
reg_val &= BIT(gpio_pins[chip->ioh]) - 1;
|
||||||
|
for_each_set_bit(i, ®_val, gpio_pins[chip->ioh])
|
||||||
generic_handle_irq(chip->irq_base + i);
|
generic_handle_irq(chip->irq_base + i);
|
||||||
ret = IRQ_HANDLED;
|
|
||||||
}
|
return IRQ_RETVAL(reg_val);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
|
static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
|
||||||
|
@ -344,7 +347,6 @@ static int pch_gpio_probe(struct pci_dev *pdev,
|
||||||
s32 ret;
|
s32 ret;
|
||||||
struct pch_gpio *chip;
|
struct pch_gpio *chip;
|
||||||
int irq_base;
|
int irq_base;
|
||||||
u32 msk;
|
|
||||||
|
|
||||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||||
if (chip == NULL)
|
if (chip == NULL)
|
||||||
|
@ -357,7 +359,7 @@ static int pch_gpio_probe(struct pci_dev *pdev,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pcim_iomap_regions(pdev, 1 << 1, KBUILD_MODNAME);
|
ret = pcim_iomap_regions(pdev, BIT(1), KBUILD_MODNAME);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret);
|
dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -393,9 +395,8 @@ static int pch_gpio_probe(struct pci_dev *pdev,
|
||||||
chip->irq_base = irq_base;
|
chip->irq_base = irq_base;
|
||||||
|
|
||||||
/* Mask all interrupts, but enable them */
|
/* Mask all interrupts, but enable them */
|
||||||
msk = (1 << gpio_pins[chip->ioh]) - 1;
|
iowrite32(BIT(gpio_pins[chip->ioh]) - 1, &chip->reg->imask);
|
||||||
iowrite32(msk, &chip->reg->imask);
|
iowrite32(BIT(gpio_pins[chip->ioh]) - 1, &chip->reg->ien);
|
||||||
iowrite32(msk, &chip->reg->ien);
|
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, pdev->irq, pch_gpio_handler,
|
ret = devm_request_irq(&pdev->dev, pdev->irq, pch_gpio_handler,
|
||||||
IRQF_SHARED, KBUILD_MODNAME, chip);
|
IRQF_SHARED, KBUILD_MODNAME, chip);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/irqchip/chained_irq.h>
|
#include <linux/irqchip/chained_irq.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
@ -408,6 +409,7 @@ static const struct amba_id pl061_ids[] = {
|
||||||
},
|
},
|
||||||
{ 0, 0 },
|
{ 0, 0 },
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(amba, pl061_ids);
|
||||||
|
|
||||||
static struct amba_driver pl061_gpio_driver = {
|
static struct amba_driver pl061_gpio_driver = {
|
||||||
.drv = {
|
.drv = {
|
||||||
|
@ -419,9 +421,6 @@ static struct amba_driver pl061_gpio_driver = {
|
||||||
.id_table = pl061_ids,
|
.id_table = pl061_ids,
|
||||||
.probe = pl061_probe,
|
.probe = pl061_probe,
|
||||||
};
|
};
|
||||||
|
module_amba_driver(pl061_gpio_driver);
|
||||||
|
|
||||||
static int __init pl061_gpio_init(void)
|
MODULE_LICENSE("GPL v2");
|
||||||
{
|
|
||||||
return amba_driver_register(&pl061_gpio_driver);
|
|
||||||
}
|
|
||||||
device_initcall(pl061_gpio_init);
|
|
||||||
|
|
|
@ -250,8 +250,10 @@ static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset)
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = pm_runtime_get_sync(p->dev);
|
error = pm_runtime_get_sync(p->dev);
|
||||||
if (error < 0)
|
if (error < 0) {
|
||||||
|
pm_runtime_put(p->dev);
|
||||||
return error;
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
error = pinctrl_gpio_request(chip->base + offset);
|
error = pinctrl_gpio_request(chip->base + offset);
|
||||||
if (error)
|
if (error)
|
||||||
|
|
349
drivers/gpio/gpio-regmap.c
Normal file
349
drivers/gpio/gpio-regmap.c
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* regmap based generic GPIO driver
|
||||||
|
*
|
||||||
|
* Copyright 2020 Michael Walle <michael@walle.cc>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/gpio/driver.h>
|
||||||
|
#include <linux/gpio/regmap.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
struct gpio_regmap {
|
||||||
|
struct device *parent;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct gpio_chip gpio_chip;
|
||||||
|
|
||||||
|
int reg_stride;
|
||||||
|
int ngpio_per_reg;
|
||||||
|
unsigned int reg_dat_base;
|
||||||
|
unsigned int reg_set_base;
|
||||||
|
unsigned int reg_clr_base;
|
||||||
|
unsigned int reg_dir_in_base;
|
||||||
|
unsigned int reg_dir_out_base;
|
||||||
|
|
||||||
|
int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
|
||||||
|
unsigned int offset, unsigned int *reg,
|
||||||
|
unsigned int *mask);
|
||||||
|
|
||||||
|
void *driver_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int gpio_regmap_addr(unsigned int addr)
|
||||||
|
{
|
||||||
|
if (addr == GPIO_REGMAP_ADDR_ZERO)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
|
||||||
|
unsigned int base, unsigned int offset,
|
||||||
|
unsigned int *reg, unsigned int *mask)
|
||||||
|
{
|
||||||
|
unsigned int line = offset % gpio->ngpio_per_reg;
|
||||||
|
unsigned int stride = offset / gpio->ngpio_per_reg;
|
||||||
|
|
||||||
|
*reg = base + stride * gpio->reg_stride;
|
||||||
|
*mask = BIT(line);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
struct gpio_regmap *gpio = gpiochip_get_data(chip);
|
||||||
|
unsigned int base, val, reg, mask;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* we might not have an output register if we are input only */
|
||||||
|
if (gpio->reg_dat_base)
|
||||||
|
base = gpio_regmap_addr(gpio->reg_dat_base);
|
||||||
|
else
|
||||||
|
base = gpio_regmap_addr(gpio->reg_set_base);
|
||||||
|
|
||||||
|
ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = regmap_read(gpio->regmap, reg, &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return !!(val & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
|
||||||
|
int val)
|
||||||
|
{
|
||||||
|
struct gpio_regmap *gpio = gpiochip_get_data(chip);
|
||||||
|
unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
|
||||||
|
unsigned int reg, mask;
|
||||||
|
|
||||||
|
gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
|
||||||
|
if (val)
|
||||||
|
regmap_update_bits(gpio->regmap, reg, mask, mask);
|
||||||
|
else
|
||||||
|
regmap_update_bits(gpio->regmap, reg, mask, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpio_regmap_set_with_clear(struct gpio_chip *chip,
|
||||||
|
unsigned int offset, int val)
|
||||||
|
{
|
||||||
|
struct gpio_regmap *gpio = gpiochip_get_data(chip);
|
||||||
|
unsigned int base, reg, mask;
|
||||||
|
|
||||||
|
if (val)
|
||||||
|
base = gpio_regmap_addr(gpio->reg_set_base);
|
||||||
|
else
|
||||||
|
base = gpio_regmap_addr(gpio->reg_clr_base);
|
||||||
|
|
||||||
|
gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
|
||||||
|
regmap_write(gpio->regmap, reg, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_regmap_get_direction(struct gpio_chip *chip,
|
||||||
|
unsigned int offset)
|
||||||
|
{
|
||||||
|
struct gpio_regmap *gpio = gpiochip_get_data(chip);
|
||||||
|
unsigned int base, val, reg, mask;
|
||||||
|
int invert, ret;
|
||||||
|
|
||||||
|
if (gpio->reg_dir_out_base) {
|
||||||
|
base = gpio_regmap_addr(gpio->reg_dir_out_base);
|
||||||
|
invert = 0;
|
||||||
|
} else if (gpio->reg_dir_in_base) {
|
||||||
|
base = gpio_regmap_addr(gpio->reg_dir_in_base);
|
||||||
|
invert = 1;
|
||||||
|
} else {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = regmap_read(gpio->regmap, reg, &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!!(val & mask) ^ invert)
|
||||||
|
return GPIO_LINE_DIRECTION_OUT;
|
||||||
|
else
|
||||||
|
return GPIO_LINE_DIRECTION_IN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_regmap_set_direction(struct gpio_chip *chip,
|
||||||
|
unsigned int offset, bool output)
|
||||||
|
{
|
||||||
|
struct gpio_regmap *gpio = gpiochip_get_data(chip);
|
||||||
|
unsigned int base, val, reg, mask;
|
||||||
|
int invert, ret;
|
||||||
|
|
||||||
|
if (gpio->reg_dir_out_base) {
|
||||||
|
base = gpio_regmap_addr(gpio->reg_dir_out_base);
|
||||||
|
invert = 0;
|
||||||
|
} else if (gpio->reg_dir_in_base) {
|
||||||
|
base = gpio_regmap_addr(gpio->reg_dir_in_base);
|
||||||
|
invert = 1;
|
||||||
|
} else {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (invert)
|
||||||
|
val = output ? 0 : mask;
|
||||||
|
else
|
||||||
|
val = output ? mask : 0;
|
||||||
|
|
||||||
|
return regmap_update_bits(gpio->regmap, reg, mask, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_regmap_direction_input(struct gpio_chip *chip,
|
||||||
|
unsigned int offset)
|
||||||
|
{
|
||||||
|
return gpio_regmap_set_direction(chip, offset, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_regmap_direction_output(struct gpio_chip *chip,
|
||||||
|
unsigned int offset, int value)
|
||||||
|
{
|
||||||
|
gpio_regmap_set(chip, offset, value);
|
||||||
|
|
||||||
|
return gpio_regmap_set_direction(chip, offset, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_regmap_set_drvdata(struct gpio_regmap *gpio, void *data)
|
||||||
|
{
|
||||||
|
gpio->driver_data = data;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpio_regmap_set_drvdata);
|
||||||
|
|
||||||
|
void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
|
||||||
|
{
|
||||||
|
return gpio->driver_data;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpio_regmap_register() - Register a generic regmap GPIO controller
|
||||||
|
* @config: configuration for gpio_regmap
|
||||||
|
*
|
||||||
|
* Return: A pointer to the registered gpio_regmap or ERR_PTR error value.
|
||||||
|
*/
|
||||||
|
struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)
|
||||||
|
{
|
||||||
|
struct gpio_regmap *gpio;
|
||||||
|
struct gpio_chip *chip;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!config->parent)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
if (!config->ngpio)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
/* we need at least one */
|
||||||
|
if (!config->reg_dat_base && !config->reg_set_base)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
/* if we have a direction register we need both input and output */
|
||||||
|
if ((config->reg_dir_out_base || config->reg_dir_in_base) &&
|
||||||
|
(!config->reg_dat_base || !config->reg_set_base))
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
/* we don't support having both registers simultaneously for now */
|
||||||
|
if (config->reg_dir_out_base && config->reg_dir_in_base)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
|
||||||
|
if (!gpio)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
gpio->parent = config->parent;
|
||||||
|
gpio->regmap = config->regmap;
|
||||||
|
gpio->ngpio_per_reg = config->ngpio_per_reg;
|
||||||
|
gpio->reg_stride = config->reg_stride;
|
||||||
|
gpio->reg_mask_xlate = config->reg_mask_xlate;
|
||||||
|
gpio->reg_dat_base = config->reg_dat_base;
|
||||||
|
gpio->reg_set_base = config->reg_set_base;
|
||||||
|
gpio->reg_clr_base = config->reg_clr_base;
|
||||||
|
gpio->reg_dir_in_base = config->reg_dir_in_base;
|
||||||
|
gpio->reg_dir_out_base = config->reg_dir_out_base;
|
||||||
|
|
||||||
|
/* if not set, assume there is only one register */
|
||||||
|
if (!gpio->ngpio_per_reg)
|
||||||
|
gpio->ngpio_per_reg = config->ngpio;
|
||||||
|
|
||||||
|
/* if not set, assume they are consecutive */
|
||||||
|
if (!gpio->reg_stride)
|
||||||
|
gpio->reg_stride = 1;
|
||||||
|
|
||||||
|
if (!gpio->reg_mask_xlate)
|
||||||
|
gpio->reg_mask_xlate = gpio_regmap_simple_xlate;
|
||||||
|
|
||||||
|
chip = &gpio->gpio_chip;
|
||||||
|
chip->parent = config->parent;
|
||||||
|
chip->base = -1;
|
||||||
|
chip->ngpio = config->ngpio;
|
||||||
|
chip->names = config->names;
|
||||||
|
chip->label = config->label ?: dev_name(config->parent);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If our regmap is fast_io we should probably set can_sleep to false.
|
||||||
|
* Right now, the regmap doesn't save this property, nor is there any
|
||||||
|
* access function for it.
|
||||||
|
* The only regmap type which uses fast_io is regmap-mmio. For now,
|
||||||
|
* assume a safe default of true here.
|
||||||
|
*/
|
||||||
|
chip->can_sleep = true;
|
||||||
|
|
||||||
|
chip->get = gpio_regmap_get;
|
||||||
|
if (gpio->reg_set_base && gpio->reg_clr_base)
|
||||||
|
chip->set = gpio_regmap_set_with_clear;
|
||||||
|
else if (gpio->reg_set_base)
|
||||||
|
chip->set = gpio_regmap_set;
|
||||||
|
|
||||||
|
if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) {
|
||||||
|
chip->get_direction = gpio_regmap_get_direction;
|
||||||
|
chip->direction_input = gpio_regmap_direction_input;
|
||||||
|
chip->direction_output = gpio_regmap_direction_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpiochip_add_data(chip, gpio);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_free_gpio;
|
||||||
|
|
||||||
|
if (config->irq_domain) {
|
||||||
|
ret = gpiochip_irqchip_add_domain(chip, config->irq_domain);
|
||||||
|
if (ret)
|
||||||
|
goto err_remove_gpiochip;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpio;
|
||||||
|
|
||||||
|
err_remove_gpiochip:
|
||||||
|
gpiochip_remove(chip);
|
||||||
|
err_free_gpio:
|
||||||
|
kfree(gpio);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpio_regmap_register);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpio_regmap_unregister() - Unregister a generic regmap GPIO controller
|
||||||
|
* @gpio: gpio_regmap device to unregister
|
||||||
|
*/
|
||||||
|
void gpio_regmap_unregister(struct gpio_regmap *gpio)
|
||||||
|
{
|
||||||
|
gpiochip_remove(&gpio->gpio_chip);
|
||||||
|
kfree(gpio);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpio_regmap_unregister);
|
||||||
|
|
||||||
|
static void devm_gpio_regmap_unregister(struct device *dev, void *res)
|
||||||
|
{
|
||||||
|
gpio_regmap_unregister(*(struct gpio_regmap **)res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_gpio_regmap_register() - resource managed gpio_regmap_register()
|
||||||
|
* @dev: device that is registering this GPIO device
|
||||||
|
* @config: configuration for gpio_regmap
|
||||||
|
*
|
||||||
|
* Managed gpio_regmap_register(). For generic regmap GPIO device registered by
|
||||||
|
* this function, gpio_regmap_unregister() is automatically called on driver
|
||||||
|
* detach. See gpio_regmap_register() for more information.
|
||||||
|
*
|
||||||
|
* Return: A pointer to the registered gpio_regmap or ERR_PTR error value.
|
||||||
|
*/
|
||||||
|
struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
|
||||||
|
const struct gpio_regmap_config *config)
|
||||||
|
{
|
||||||
|
struct gpio_regmap **ptr, *gpio;
|
||||||
|
|
||||||
|
ptr = devres_alloc(devm_gpio_regmap_unregister, sizeof(*ptr),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!ptr)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
gpio = gpio_regmap_register(config);
|
||||||
|
if (!IS_ERR(gpio)) {
|
||||||
|
*ptr = gpio;
|
||||||
|
devres_add(dev, ptr);
|
||||||
|
} else {
|
||||||
|
devres_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpio;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_gpio_regmap_register);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
|
||||||
|
MODULE_DESCRIPTION("GPIO generic regmap driver core");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -894,6 +894,7 @@ static const struct of_device_id tegra186_gpio_of_match[] = {
|
||||||
/* sentinel */
|
/* sentinel */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, tegra186_gpio_of_match);
|
||||||
|
|
||||||
static struct platform_driver tegra186_gpio_driver = {
|
static struct platform_driver tegra186_gpio_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio)
|
||||||
fwspec.fwnode = gc->parent->fwnode;
|
fwspec.fwnode = gc->parent->fwnode;
|
||||||
fwspec.param_count = 2;
|
fwspec.param_count = 2;
|
||||||
fwspec.param[0] = GPIO_TO_HWIRQ(priv, gpio);
|
fwspec.param[0] = GPIO_TO_HWIRQ(priv, gpio);
|
||||||
fwspec.param[1] = IRQ_TYPE_NONE;
|
fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
|
||||||
return irq_create_fwspec_mapping(&fwspec);
|
return irq_create_fwspec_mapping(&fwspec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,10 +290,8 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
|
dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
|
||||||
|
|
||||||
if (priv->nirq > 0) {
|
/* Register interrupt handlers for GPIO signaled ACPI Events */
|
||||||
/* Register interrupt handlers for gpio signaled acpi events */
|
|
||||||
acpi_gpiochip_request_interrupts(&priv->gc);
|
acpi_gpiochip_request_interrupts(&priv->gc);
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -302,9 +300,7 @@ static int xgene_gpio_sb_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);
|
struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
if (priv->nirq > 0) {
|
|
||||||
acpi_gpiochip_free_interrupts(&priv->gc);
|
acpi_gpiochip_free_interrupts(&priv->gc);
|
||||||
}
|
|
||||||
|
|
||||||
irq_domain_remove(priv->irq_domain);
|
irq_domain_remove(priv->irq_domain);
|
||||||
|
|
||||||
|
|
|
@ -1353,7 +1353,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run deferred acpi_gpiochip_request_irqs() */
|
/* Run deferred acpi_gpiochip_request_irqs() */
|
||||||
static int acpi_gpio_handle_deferred_request_irqs(void)
|
static int __init acpi_gpio_handle_deferred_request_irqs(void)
|
||||||
{
|
{
|
||||||
struct acpi_gpio_chip *acpi_gpio, *tmp;
|
struct acpi_gpio_chip *acpi_gpio, *tmp;
|
||||||
|
|
||||||
|
@ -1371,7 +1371,7 @@ static int acpi_gpio_handle_deferred_request_irqs(void)
|
||||||
/* We must use _sync so that this runs after the first deferred_probe run */
|
/* We must use _sync so that this runs after the first deferred_probe run */
|
||||||
late_initcall_sync(acpi_gpio_handle_deferred_request_irqs);
|
late_initcall_sync(acpi_gpio_handle_deferred_request_irqs);
|
||||||
|
|
||||||
static const struct dmi_system_id gpiolib_acpi_quirks[] = {
|
static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The Minix Neo Z83-4 has a micro-USB-B id-pin handler for
|
* The Minix Neo Z83-4 has a micro-USB-B id-pin handler for
|
||||||
|
@ -1455,7 +1455,7 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] = {
|
||||||
{} /* Terminating entry */
|
{} /* Terminating entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
static int acpi_gpio_setup_params(void)
|
static int __init acpi_gpio_setup_params(void)
|
||||||
{
|
{
|
||||||
const struct acpi_gpiolib_dmi_quirk *quirk = NULL;
|
const struct acpi_gpiolib_dmi_quirk *quirk = NULL;
|
||||||
const struct dmi_system_id *id;
|
const struct dmi_system_id *id;
|
||||||
|
|
|
@ -37,8 +37,11 @@ void devprop_gpiochip_set_names(struct gpio_chip *chip,
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (count > gdev->ngpio)
|
if (count > gdev->ngpio) {
|
||||||
|
dev_warn(&gdev->dev, "gpio-line-names is length %d but should be at most length %d",
|
||||||
|
count, gdev->ngpio);
|
||||||
count = gdev->ngpio;
|
count = gdev->ngpio;
|
||||||
|
}
|
||||||
|
|
||||||
names = kcalloc(count, sizeof(*names), GFP_KERNEL);
|
names = kcalloc(count, sizeof(*names), GFP_KERNEL);
|
||||||
if (!names)
|
if (!names)
|
||||||
|
|
|
@ -344,6 +344,12 @@ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
|
||||||
if (transitory)
|
if (transitory)
|
||||||
lflags |= GPIO_TRANSITORY;
|
lflags |= GPIO_TRANSITORY;
|
||||||
|
|
||||||
|
if (flags & OF_GPIO_PULL_UP)
|
||||||
|
lflags |= GPIO_PULL_UP;
|
||||||
|
|
||||||
|
if (flags & OF_GPIO_PULL_DOWN)
|
||||||
|
lflags |= GPIO_PULL_DOWN;
|
||||||
|
|
||||||
ret = gpiod_configure_flags(desc, propname, lflags, dflags);
|
ret = gpiod_configure_flags(desc, propname, lflags, dflags);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
gpiod_put(desc);
|
gpiod_put(desc);
|
||||||
|
@ -585,6 +591,10 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
||||||
*lflags |= GPIO_ACTIVE_LOW;
|
*lflags |= GPIO_ACTIVE_LOW;
|
||||||
if (xlate_flags & OF_GPIO_TRANSITORY)
|
if (xlate_flags & OF_GPIO_TRANSITORY)
|
||||||
*lflags |= GPIO_TRANSITORY;
|
*lflags |= GPIO_TRANSITORY;
|
||||||
|
if (xlate_flags & OF_GPIO_PULL_UP)
|
||||||
|
*lflags |= GPIO_PULL_UP;
|
||||||
|
if (xlate_flags & OF_GPIO_PULL_DOWN)
|
||||||
|
*lflags |= GPIO_PULL_DOWN;
|
||||||
|
|
||||||
if (of_property_read_bool(np, "input"))
|
if (of_property_read_bool(np, "input"))
|
||||||
*dflags |= GPIOD_IN;
|
*dflags |= GPIOD_IN;
|
||||||
|
|
|
@ -296,6 +296,9 @@ static int gpiodev_add_to_list(struct gpio_device *gdev)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert a GPIO name to its descriptor
|
* Convert a GPIO name to its descriptor
|
||||||
|
* Note that there is no guarantee that GPIO names are globally unique!
|
||||||
|
* Hence this function will return, if it exists, a reference to the first GPIO
|
||||||
|
* line found that matches the given name.
|
||||||
*/
|
*/
|
||||||
static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
||||||
{
|
{
|
||||||
|
@ -329,10 +332,12 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Takes the names from gc->names and checks if they are all unique. If they
|
* Take the names from gc->names and assign them to their GPIO descriptors.
|
||||||
* are, they are assigned to their gpio descriptors.
|
* Warn if a name is already used for a GPIO line on a different GPIO chip.
|
||||||
*
|
*
|
||||||
* Warning if one of the names is already used for a different GPIO.
|
* Note that:
|
||||||
|
* 1. Non-unique names are still accepted,
|
||||||
|
* 2. Name collisions within the same GPIO chip are not reported.
|
||||||
*/
|
*/
|
||||||
static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
||||||
{
|
{
|
||||||
|
@ -1267,8 +1272,7 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
|
if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
} else if (cmd == GPIO_GET_LINEINFO_IOCTL ||
|
} else if (cmd == GPIO_GET_LINEINFO_IOCTL) {
|
||||||
cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) {
|
|
||||||
struct gpioline_info lineinfo;
|
struct gpioline_info lineinfo;
|
||||||
|
|
||||||
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
|
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
|
||||||
|
@ -1280,8 +1284,28 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
|
|
||||||
hwgpio = gpio_chip_hwgpio(desc);
|
hwgpio = gpio_chip_hwgpio(desc);
|
||||||
|
|
||||||
if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL &&
|
gpio_desc_to_lineinfo(desc, &lineinfo);
|
||||||
test_bit(hwgpio, priv->watched_lines))
|
|
||||||
|
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
} else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
|
||||||
|
return linehandle_create(gdev, ip);
|
||||||
|
} else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
|
||||||
|
return lineevent_create(gdev, ip);
|
||||||
|
} else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) {
|
||||||
|
struct gpioline_info lineinfo;
|
||||||
|
|
||||||
|
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
desc = gpiochip_get_desc(gc, lineinfo.line_offset);
|
||||||
|
if (IS_ERR(desc))
|
||||||
|
return PTR_ERR(desc);
|
||||||
|
|
||||||
|
hwgpio = gpio_chip_hwgpio(desc);
|
||||||
|
|
||||||
|
if (test_bit(hwgpio, priv->watched_lines))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
gpio_desc_to_lineinfo(desc, &lineinfo);
|
gpio_desc_to_lineinfo(desc, &lineinfo);
|
||||||
|
@ -1289,14 +1313,8 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
|
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL)
|
|
||||||
set_bit(hwgpio, priv->watched_lines);
|
set_bit(hwgpio, priv->watched_lines);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
|
|
||||||
return linehandle_create(gdev, ip);
|
|
||||||
} else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
|
|
||||||
return lineevent_create(gdev, ip);
|
|
||||||
} else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) {
|
} else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) {
|
||||||
if (copy_from_user(&offset, ip, sizeof(offset)))
|
if (copy_from_user(&offset, ip, sizeof(offset)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -1538,9 +1556,8 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
|
||||||
|
|
||||||
/* From this point, the .release() function cleans up gpio_device */
|
/* From this point, the .release() function cleans up gpio_device */
|
||||||
gdev->dev.release = gpiodevice_release;
|
gdev->dev.release = gpiodevice_release;
|
||||||
pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n",
|
dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base,
|
||||||
__func__, gdev->base, gdev->base + gdev->ngpio - 1,
|
gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic");
|
||||||
dev_name(&gdev->dev), gdev->chip->label ? : "generic");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1556,8 +1573,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
|
||||||
|
|
||||||
desc = gpiochip_get_desc(gc, hog->chip_hwnum);
|
desc = gpiochip_get_desc(gc, hog->chip_hwnum);
|
||||||
if (IS_ERR(desc)) {
|
if (IS_ERR(desc)) {
|
||||||
pr_err("%s: unable to get GPIO desc: %ld\n",
|
chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__,
|
||||||
__func__, PTR_ERR(desc));
|
PTR_ERR(desc));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1566,7 +1583,7 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
|
||||||
|
|
||||||
rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags);
|
rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags);
|
||||||
if (rv)
|
if (rv)
|
||||||
pr_err("%s: unable to hog GPIO line (%s:%u): %d\n",
|
gpiod_err(desc, "%s: unable to hog GPIO line (%s:%u): %d\n",
|
||||||
__func__, gc->label, hog->chip_hwnum, rv);
|
__func__, gc->label, hog->chip_hwnum, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1592,8 +1609,8 @@ static void gpiochip_setup_devs(void)
|
||||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||||
ret = gpiochip_setup_dev(gdev);
|
ret = gpiochip_setup_dev(gdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_err("%s: Failed to initialize gpio device (%d)\n",
|
dev_err(&gdev->dev,
|
||||||
dev_name(&gdev->dev), ret);
|
"Failed to initialize gpio device (%d)\n", ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2461,32 +2478,37 @@ static void gpiochip_irq_relres(struct irq_data *d)
|
||||||
gpiochip_relres_irq(gc, d->hwirq);
|
gpiochip_relres_irq(gc, d->hwirq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gpiochip_irq_mask(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
|
if (gc->irq.irq_mask)
|
||||||
|
gc->irq.irq_mask(d);
|
||||||
|
gpiochip_disable_irq(gc, d->hwirq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpiochip_irq_unmask(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
|
gpiochip_enable_irq(gc, d->hwirq);
|
||||||
|
if (gc->irq.irq_unmask)
|
||||||
|
gc->irq.irq_unmask(d);
|
||||||
|
}
|
||||||
|
|
||||||
static void gpiochip_irq_enable(struct irq_data *d)
|
static void gpiochip_irq_enable(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
gpiochip_enable_irq(gc, d->hwirq);
|
gpiochip_enable_irq(gc, d->hwirq);
|
||||||
if (gc->irq.irq_enable)
|
|
||||||
gc->irq.irq_enable(d);
|
gc->irq.irq_enable(d);
|
||||||
else
|
|
||||||
gc->irq.chip->irq_unmask(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpiochip_irq_disable(struct irq_data *d)
|
static void gpiochip_irq_disable(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
/*
|
|
||||||
* Since we override .irq_disable() we need to mimic the
|
|
||||||
* behaviour of __irq_disable() in irq/chip.c.
|
|
||||||
* First call .irq_disable() if it exists, else mimic the
|
|
||||||
* behaviour of mask_irq() which calls .irq_mask() if
|
|
||||||
* it exists.
|
|
||||||
*/
|
|
||||||
if (gc->irq.irq_disable)
|
|
||||||
gc->irq.irq_disable(d);
|
gc->irq.irq_disable(d);
|
||||||
else if (gc->irq.chip->irq_mask)
|
|
||||||
gc->irq.chip->irq_mask(d);
|
|
||||||
gpiochip_disable_irq(gc, d->hwirq);
|
gpiochip_disable_irq(gc, d->hwirq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2511,10 +2533,22 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
|
||||||
"detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
|
"detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gc->irq.irq_enable = irqchip->irq_enable;
|
|
||||||
|
if (irqchip->irq_disable) {
|
||||||
gc->irq.irq_disable = irqchip->irq_disable;
|
gc->irq.irq_disable = irqchip->irq_disable;
|
||||||
irqchip->irq_enable = gpiochip_irq_enable;
|
|
||||||
irqchip->irq_disable = gpiochip_irq_disable;
|
irqchip->irq_disable = gpiochip_irq_disable;
|
||||||
|
} else {
|
||||||
|
gc->irq.irq_mask = irqchip->irq_mask;
|
||||||
|
irqchip->irq_mask = gpiochip_irq_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irqchip->irq_enable) {
|
||||||
|
gc->irq.irq_enable = irqchip->irq_enable;
|
||||||
|
irqchip->irq_enable = gpiochip_irq_enable;
|
||||||
|
} else {
|
||||||
|
gc->irq.irq_unmask = irqchip->irq_unmask;
|
||||||
|
irqchip->irq_unmask = gpiochip_irq_unmask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2702,7 +2736,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!gc->parent) {
|
if (!gc->parent) {
|
||||||
pr_err("missing gpiochip .dev parent pointer\n");
|
chip_err(gc, "missing gpiochip .dev parent pointer\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
gc->irq.threaded = threaded;
|
gc->irq.threaded = threaded;
|
||||||
|
@ -2752,6 +2786,26 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key);
|
EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpiochip_irqchip_add_domain() - adds an irqdomain to a gpiochip
|
||||||
|
* @gc: the gpiochip to add the irqchip to
|
||||||
|
* @domain: the irqdomain to add to the gpiochip
|
||||||
|
*
|
||||||
|
* This function adds an IRQ domain to the gpiochip.
|
||||||
|
*/
|
||||||
|
int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
|
||||||
|
struct irq_domain *domain)
|
||||||
|
{
|
||||||
|
if (!domain)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
gc->to_irq = gpiochip_to_irq;
|
||||||
|
gc->irq.domain = domain;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_domain);
|
||||||
|
|
||||||
#else /* CONFIG_GPIOLIB_IRQCHIP */
|
#else /* CONFIG_GPIOLIB_IRQCHIP */
|
||||||
|
|
||||||
static inline int gpiochip_add_irqchip(struct gpio_chip *gc,
|
static inline int gpiochip_add_irqchip(struct gpio_chip *gc,
|
||||||
|
@ -4653,7 +4707,7 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
|
||||||
if (!table)
|
if (!table)
|
||||||
return desc;
|
return desc;
|
||||||
|
|
||||||
for (p = &table->table[0]; p->chip_label; p++) {
|
for (p = &table->table[0]; p->key; p++) {
|
||||||
struct gpio_chip *gc;
|
struct gpio_chip *gc;
|
||||||
|
|
||||||
/* idx must always match exactly */
|
/* idx must always match exactly */
|
||||||
|
@ -4664,18 +4718,30 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
|
||||||
if (p->con_id && (!con_id || strcmp(p->con_id, con_id)))
|
if (p->con_id && (!con_id || strcmp(p->con_id, con_id)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
gc = find_chip_by_name(p->chip_label);
|
if (p->chip_hwnum == U16_MAX) {
|
||||||
|
desc = gpio_name_to_desc(p->key);
|
||||||
|
if (desc) {
|
||||||
|
*flags = p->flags;
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_warn(dev, "cannot find GPIO line %s, deferring\n",
|
||||||
|
p->key);
|
||||||
|
return ERR_PTR(-EPROBE_DEFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
gc = find_chip_by_name(p->key);
|
||||||
|
|
||||||
if (!gc) {
|
if (!gc) {
|
||||||
/*
|
/*
|
||||||
* As the lookup table indicates a chip with
|
* As the lookup table indicates a chip with
|
||||||
* p->chip_label should exist, assume it may
|
* p->key should exist, assume it may
|
||||||
* still appear later and let the interested
|
* still appear later and let the interested
|
||||||
* consumer be probed again or let the Deferred
|
* consumer be probed again or let the Deferred
|
||||||
* Probe infrastructure handle the error.
|
* Probe infrastructure handle the error.
|
||||||
*/
|
*/
|
||||||
dev_warn(dev, "cannot find GPIO chip %s, deferring\n",
|
dev_warn(dev, "cannot find GPIO chip %s, deferring\n",
|
||||||
p->chip_label);
|
p->key);
|
||||||
return ERR_PTR(-EPROBE_DEFER);
|
return ERR_PTR(-EPROBE_DEFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4706,7 +4772,7 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
|
||||||
if (!table)
|
if (!table)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
for (p = &table->table[0]; p->chip_label; p++) {
|
for (p = &table->table[0]; p->key; p++) {
|
||||||
if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) ||
|
if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) ||
|
||||||
(!con_id && !p->con_id))
|
(!con_id && !p->con_id))
|
||||||
count++;
|
count++;
|
||||||
|
@ -4877,7 +4943,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
|
||||||
|
|
||||||
/* No particular flag request, return here... */
|
/* No particular flag request, return here... */
|
||||||
if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
|
if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
|
||||||
pr_debug("no flags found for %s\n", con_id);
|
gpiod_dbg(desc, "no flags found for %s\n", con_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5108,8 +5174,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
|
||||||
/* Mark GPIO as hogged so it can be identified and removed later */
|
/* Mark GPIO as hogged so it can be identified and removed later */
|
||||||
set_bit(FLAG_IS_HOGGED, &desc->flags);
|
set_bit(FLAG_IS_HOGGED, &desc->flags);
|
||||||
|
|
||||||
pr_info("GPIO line %d (%s) hogged as %s%s\n",
|
gpiod_info(desc, "hogged as %s%s\n",
|
||||||
desc_to_gpio(desc), name,
|
|
||||||
(dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input",
|
(dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input",
|
||||||
(dflags & GPIOD_FLAGS_BIT_DIR_OUT) ?
|
(dflags & GPIOD_FLAGS_BIT_DIR_OUT) ?
|
||||||
(dflags & GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low" : "");
|
(dflags & GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low" : "");
|
||||||
|
|
|
@ -81,8 +81,7 @@ struct gpio_array {
|
||||||
unsigned long invert_mask[];
|
unsigned long invert_mask[];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
|
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
|
||||||
unsigned int hwnum);
|
|
||||||
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
||||||
unsigned int array_size,
|
unsigned int array_size,
|
||||||
struct gpio_desc **desc_array,
|
struct gpio_desc **desc_array,
|
||||||
|
@ -163,18 +162,18 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
|
||||||
|
|
||||||
/* With chip prefix */
|
/* With chip prefix */
|
||||||
|
|
||||||
#define chip_emerg(chip, fmt, ...) \
|
#define chip_emerg(gc, fmt, ...) \
|
||||||
dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
dev_emerg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||||
#define chip_crit(chip, fmt, ...) \
|
#define chip_crit(gc, fmt, ...) \
|
||||||
dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
dev_crit(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||||
#define chip_err(chip, fmt, ...) \
|
#define chip_err(gc, fmt, ...) \
|
||||||
dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||||
#define chip_warn(chip, fmt, ...) \
|
#define chip_warn(gc, fmt, ...) \
|
||||||
dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||||
#define chip_info(chip, fmt, ...) \
|
#define chip_info(gc, fmt, ...) \
|
||||||
dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||||
#define chip_dbg(chip, fmt, ...) \
|
#define chip_dbg(gc, fmt, ...) \
|
||||||
dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||||
|
|
||||||
#ifdef CONFIG_GPIO_SYSFS
|
#ifdef CONFIG_GPIO_SYSFS
|
||||||
|
|
||||||
|
|
|
@ -1439,9 +1439,9 @@ static int i801_add_mux(struct i801_priv *priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
lookup->dev_id = "i2c-mux-gpio";
|
lookup->dev_id = "i2c-mux-gpio";
|
||||||
for (i = 0; i < mux_config->n_gpios; i++) {
|
for (i = 0; i < mux_config->n_gpios; i++) {
|
||||||
lookup->table[i].chip_label = mux_config->gpio_chip;
|
lookup->table[i] = (struct gpiod_lookup)
|
||||||
lookup->table[i].chip_hwnum = mux_config->gpios[i];
|
GPIO_LOOKUP(mux_config->gpio_chip,
|
||||||
lookup->table[i].con_id = "mux";
|
mux_config->gpios[i], "mux", 0);
|
||||||
}
|
}
|
||||||
gpiod_add_lookup_table(lookup);
|
gpiod_add_lookup_table(lookup);
|
||||||
priv->lookup = lookup;
|
priv->lookup = lookup;
|
||||||
|
|
|
@ -216,7 +216,6 @@ static int intel_quark_gpio_setup(struct pci_dev *pdev, struct mfd_cell *cell)
|
||||||
pdata->properties->ngpio = INTEL_QUARK_MFD_NGPIO;
|
pdata->properties->ngpio = INTEL_QUARK_MFD_NGPIO;
|
||||||
pdata->properties->gpio_base = INTEL_QUARK_MFD_GPIO_BASE;
|
pdata->properties->gpio_base = INTEL_QUARK_MFD_GPIO_BASE;
|
||||||
pdata->properties->irq[0] = pdev->irq;
|
pdata->properties->irq[0] = pdev->irq;
|
||||||
pdata->properties->has_irq = true;
|
|
||||||
pdata->properties->irq_shared = true;
|
pdata->properties->irq_shared = true;
|
||||||
|
|
||||||
cell->platform_data = pdata;
|
cell->platform_data = pdata;
|
||||||
|
|
|
@ -1145,22 +1145,14 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
lookup->dev_id = "i2c-gpio";
|
lookup->dev_id = "i2c-gpio";
|
||||||
if (iic->pin_sda < 32)
|
lookup->table[0] = (struct gpiod_lookup)
|
||||||
lookup->table[0].chip_label = "SM501-LOW";
|
GPIO_LOOKUP_IDX(iic->pin_sda < 32 ? "SM501-LOW" : "SM501-HIGH",
|
||||||
else
|
iic->pin_sda % 32, NULL, 0,
|
||||||
lookup->table[0].chip_label = "SM501-HIGH";
|
GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN);
|
||||||
lookup->table[0].chip_hwnum = iic->pin_sda % 32;
|
lookup->table[1] = (struct gpiod_lookup)
|
||||||
lookup->table[0].con_id = NULL;
|
GPIO_LOOKUP_IDX(iic->pin_scl < 32 ? "SM501-LOW" : "SM501-HIGH",
|
||||||
lookup->table[0].idx = 0;
|
iic->pin_scl % 32, NULL, 1,
|
||||||
lookup->table[0].flags = GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN;
|
GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN);
|
||||||
if (iic->pin_scl < 32)
|
|
||||||
lookup->table[1].chip_label = "SM501-LOW";
|
|
||||||
else
|
|
||||||
lookup->table[1].chip_label = "SM501-HIGH";
|
|
||||||
lookup->table[1].chip_hwnum = iic->pin_scl % 32;
|
|
||||||
lookup->table[1].con_id = NULL;
|
|
||||||
lookup->table[1].idx = 1;
|
|
||||||
lookup->table[1].flags = GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN;
|
|
||||||
gpiod_add_lookup_table(lookup);
|
gpiod_add_lookup_table(lookup);
|
||||||
|
|
||||||
icd = dev_get_platdata(&pdev->dev);
|
icd = dev_get_platdata(&pdev->dev);
|
||||||
|
|
|
@ -253,6 +253,19 @@ struct gpio_irq_chip {
|
||||||
* Store old irq_chip irq_disable callback
|
* Store old irq_chip irq_disable callback
|
||||||
*/
|
*/
|
||||||
void (*irq_disable)(struct irq_data *data);
|
void (*irq_disable)(struct irq_data *data);
|
||||||
|
/**
|
||||||
|
* @irq_unmask:
|
||||||
|
*
|
||||||
|
* Store old irq_chip irq_unmask callback
|
||||||
|
*/
|
||||||
|
void (*irq_unmask)(struct irq_data *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @irq_mask:
|
||||||
|
*
|
||||||
|
* Store old irq_chip irq_mask callback
|
||||||
|
*/
|
||||||
|
void (*irq_mask)(struct irq_data *data);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -267,9 +280,9 @@ struct gpio_irq_chip {
|
||||||
* @free: optional hook for chip-specific deactivation, such as
|
* @free: optional hook for chip-specific deactivation, such as
|
||||||
* disabling module power and clock; may sleep
|
* disabling module power and clock; may sleep
|
||||||
* @get_direction: returns direction for signal "offset", 0=out, 1=in,
|
* @get_direction: returns direction for signal "offset", 0=out, 1=in,
|
||||||
* (same as GPIOF_DIR_XXX), or negative error.
|
* (same as GPIO_LINE_DIRECTION_OUT / GPIO_LINE_DIRECTION_IN),
|
||||||
* It is recommended to always implement this function, even on
|
* or negative error. It is recommended to always implement this
|
||||||
* input-only or output-only gpio chips.
|
* function, even on input-only or output-only gpio chips.
|
||||||
* @direction_input: configures signal "offset" as input, or returns error
|
* @direction_input: configures signal "offset" as input, or returns error
|
||||||
* This can be omitted on input-only or output-only gpio chips.
|
* This can be omitted on input-only or output-only gpio chips.
|
||||||
* @direction_output: configures signal "offset" as output, or returns error
|
* @direction_output: configures signal "offset" as output, or returns error
|
||||||
|
@ -349,30 +362,30 @@ struct gpio_chip {
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
|
||||||
int (*request)(struct gpio_chip *gc,
|
int (*request)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned int offset);
|
||||||
void (*free)(struct gpio_chip *gc,
|
void (*free)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned int offset);
|
||||||
int (*get_direction)(struct gpio_chip *gc,
|
int (*get_direction)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned int offset);
|
||||||
int (*direction_input)(struct gpio_chip *gc,
|
int (*direction_input)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned int offset);
|
||||||
int (*direction_output)(struct gpio_chip *gc,
|
int (*direction_output)(struct gpio_chip *gc,
|
||||||
unsigned offset, int value);
|
unsigned int offset, int value);
|
||||||
int (*get)(struct gpio_chip *gc,
|
int (*get)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned int offset);
|
||||||
int (*get_multiple)(struct gpio_chip *gc,
|
int (*get_multiple)(struct gpio_chip *gc,
|
||||||
unsigned long *mask,
|
unsigned long *mask,
|
||||||
unsigned long *bits);
|
unsigned long *bits);
|
||||||
void (*set)(struct gpio_chip *gc,
|
void (*set)(struct gpio_chip *gc,
|
||||||
unsigned offset, int value);
|
unsigned int offset, int value);
|
||||||
void (*set_multiple)(struct gpio_chip *gc,
|
void (*set_multiple)(struct gpio_chip *gc,
|
||||||
unsigned long *mask,
|
unsigned long *mask,
|
||||||
unsigned long *bits);
|
unsigned long *bits);
|
||||||
int (*set_config)(struct gpio_chip *gc,
|
int (*set_config)(struct gpio_chip *gc,
|
||||||
unsigned offset,
|
unsigned int offset,
|
||||||
unsigned long config);
|
unsigned long config);
|
||||||
int (*to_irq)(struct gpio_chip *gc,
|
int (*to_irq)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned int offset);
|
||||||
|
|
||||||
void (*dbg_show)(struct seq_file *s,
|
void (*dbg_show)(struct seq_file *s,
|
||||||
struct gpio_chip *gc);
|
struct gpio_chip *gc);
|
||||||
|
@ -459,7 +472,7 @@ struct gpio_chip {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const char *gpiochip_is_requested(struct gpio_chip *gc,
|
extern const char *gpiochip_is_requested(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned int offset);
|
||||||
|
|
||||||
/* add/remove chips */
|
/* add/remove chips */
|
||||||
extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
||||||
|
@ -599,6 +612,9 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc,
|
||||||
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
|
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
|
||||||
unsigned int offset);
|
unsigned int offset);
|
||||||
|
|
||||||
|
int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
|
||||||
|
struct irq_domain *domain);
|
||||||
|
|
||||||
#ifdef CONFIG_LOCKDEP
|
#ifdef CONFIG_LOCKDEP
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -657,9 +673,9 @@ static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gc,
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_LOCKDEP */
|
#endif /* CONFIG_LOCKDEP */
|
||||||
|
|
||||||
int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset);
|
int gpiochip_generic_request(struct gpio_chip *gc, unsigned int offset);
|
||||||
void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset);
|
void gpiochip_generic_free(struct gpio_chip *gc, unsigned int offset);
|
||||||
int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset,
|
int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset,
|
||||||
unsigned long config);
|
unsigned long config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,8 +20,11 @@ enum gpio_lookup_flags {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct gpiod_lookup - lookup table
|
* struct gpiod_lookup - lookup table
|
||||||
* @chip_label: name of the chip the GPIO belongs to
|
* @key: either the name of the chip the GPIO belongs to, or the GPIO line name
|
||||||
* @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO
|
* Note that GPIO line names are not guaranteed to be globally unique,
|
||||||
|
* so this will use the first match found!
|
||||||
|
* @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO, or
|
||||||
|
* U16_MAX to indicate that @key is a GPIO line name
|
||||||
* @con_id: name of the GPIO from the device's point of view
|
* @con_id: name of the GPIO from the device's point of view
|
||||||
* @idx: index of the GPIO in case several GPIOs share the same name
|
* @idx: index of the GPIO in case several GPIOs share the same name
|
||||||
* @flags: bitmask of gpio_lookup_flags GPIO_* values
|
* @flags: bitmask of gpio_lookup_flags GPIO_* values
|
||||||
|
@ -30,7 +33,7 @@ enum gpio_lookup_flags {
|
||||||
* functions using platform data.
|
* functions using platform data.
|
||||||
*/
|
*/
|
||||||
struct gpiod_lookup {
|
struct gpiod_lookup {
|
||||||
const char *chip_label;
|
const char *key;
|
||||||
u16 chip_hwnum;
|
u16 chip_hwnum;
|
||||||
const char *con_id;
|
const char *con_id;
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
|
@ -63,17 +66,17 @@ struct gpiod_hog {
|
||||||
/*
|
/*
|
||||||
* Simple definition of a single GPIO under a con_id
|
* Simple definition of a single GPIO under a con_id
|
||||||
*/
|
*/
|
||||||
#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _con_id, _flags) \
|
#define GPIO_LOOKUP(_key, _chip_hwnum, _con_id, _flags) \
|
||||||
GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, 0, _flags)
|
GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, 0, _flags)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use this macro if you need to have several GPIOs under the same con_id.
|
* Use this macro if you need to have several GPIOs under the same con_id.
|
||||||
* Each GPIO needs to use a different index and can be accessed using
|
* Each GPIO needs to use a different index and can be accessed using
|
||||||
* gpiod_get_index()
|
* gpiod_get_index()
|
||||||
*/
|
*/
|
||||||
#define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, _idx, _flags) \
|
#define GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, _idx, _flags) \
|
||||||
{ \
|
{ \
|
||||||
.chip_label = _chip_label, \
|
.key = _key, \
|
||||||
.chip_hwnum = _chip_hwnum, \
|
.chip_hwnum = _chip_hwnum, \
|
||||||
.con_id = _con_id, \
|
.con_id = _con_id, \
|
||||||
.idx = _idx, \
|
.idx = _idx, \
|
||||||
|
|
86
include/linux/gpio/regmap.h
Normal file
86
include/linux/gpio/regmap.h
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
#ifndef _LINUX_GPIO_REGMAP_H
|
||||||
|
#define _LINUX_GPIO_REGMAP_H
|
||||||
|
|
||||||
|
struct device;
|
||||||
|
struct gpio_regmap;
|
||||||
|
struct irq_domain;
|
||||||
|
struct regmap;
|
||||||
|
|
||||||
|
#define GPIO_REGMAP_ADDR_ZERO ((unsigned long)(-1))
|
||||||
|
#define GPIO_REGMAP_ADDR(addr) ((addr) ? : GPIO_REGMAP_ADDR_ZERO)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct gpio_regmap_config - Description of a generic regmap gpio_chip.
|
||||||
|
* @parent: The parent device
|
||||||
|
* @regmap: The regmap used to access the registers
|
||||||
|
* given, the name of the device is used
|
||||||
|
* @label: (Optional) Descriptive name for GPIO controller.
|
||||||
|
* If not given, the name of the device is used.
|
||||||
|
* @ngpio: Number of GPIOs
|
||||||
|
* @names: (Optional) Array of names for gpios
|
||||||
|
* @reg_dat_base: (Optional) (in) register base address
|
||||||
|
* @reg_set_base: (Optional) set register base address
|
||||||
|
* @reg_clr_base: (Optional) clear register base address
|
||||||
|
* @reg_dir_in_base: (Optional) in setting register base address
|
||||||
|
* @reg_dir_out_base: (Optional) out setting register base address
|
||||||
|
* @reg_stride: (Optional) May be set if the registers (of the
|
||||||
|
* same type, dat, set, etc) are not consecutive.
|
||||||
|
* @ngpio_per_reg: Number of GPIOs per register
|
||||||
|
* @irq_domain: (Optional) IRQ domain if the controller is
|
||||||
|
* interrupt-capable
|
||||||
|
* @reg_mask_xlate: (Optional) Translates base address and GPIO
|
||||||
|
* offset to a register/bitmask pair. If not
|
||||||
|
* given the default gpio_regmap_simple_xlate()
|
||||||
|
* is used.
|
||||||
|
*
|
||||||
|
* The ->reg_mask_xlate translates a given base address and GPIO offset to
|
||||||
|
* register and mask pair. The base address is one of the given register
|
||||||
|
* base addresses in this structure.
|
||||||
|
*
|
||||||
|
* Although all register base addresses are marked as optional, there are
|
||||||
|
* several rules:
|
||||||
|
* 1. if you only have @reg_dat_base set, then it is input-only
|
||||||
|
* 2. if you only have @reg_set_base set, then it is output-only
|
||||||
|
* 3. if you have either @reg_dir_in_base or @reg_dir_out_base set, then
|
||||||
|
* you have to set both @reg_dat_base and @reg_set_base
|
||||||
|
* 4. if you have @reg_set_base set, you may also set @reg_clr_base to have
|
||||||
|
* two different registers for setting and clearing the output. This is
|
||||||
|
* also valid for the output-only case.
|
||||||
|
* 5. @reg_dir_in_base and @reg_dir_out_base are exclusive; is there really
|
||||||
|
* hardware which has redundant registers?
|
||||||
|
*
|
||||||
|
* Note: All base addresses may have the special value %GPIO_REGMAP_ADDR_ZERO
|
||||||
|
* which forces the address to the value 0.
|
||||||
|
*/
|
||||||
|
struct gpio_regmap_config {
|
||||||
|
struct device *parent;
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
|
const char *label;
|
||||||
|
int ngpio;
|
||||||
|
const char *const *names;
|
||||||
|
|
||||||
|
unsigned int reg_dat_base;
|
||||||
|
unsigned int reg_set_base;
|
||||||
|
unsigned int reg_clr_base;
|
||||||
|
unsigned int reg_dir_in_base;
|
||||||
|
unsigned int reg_dir_out_base;
|
||||||
|
int reg_stride;
|
||||||
|
int ngpio_per_reg;
|
||||||
|
struct irq_domain *irq_domain;
|
||||||
|
|
||||||
|
int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
|
||||||
|
unsigned int offset, unsigned int *reg,
|
||||||
|
unsigned int *mask);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config);
|
||||||
|
void gpio_regmap_unregister(struct gpio_regmap *gpio);
|
||||||
|
struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
|
||||||
|
const struct gpio_regmap_config *config);
|
||||||
|
void gpio_regmap_set_drvdata(struct gpio_regmap *gpio, void *data);
|
||||||
|
void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio);
|
||||||
|
|
||||||
|
#endif /* _LINUX_GPIO_REGMAP_H */
|
|
@ -12,7 +12,6 @@ struct dwapb_port_property {
|
||||||
unsigned int ngpio;
|
unsigned int ngpio;
|
||||||
unsigned int gpio_base;
|
unsigned int gpio_base;
|
||||||
int irq[32];
|
int irq[32];
|
||||||
bool has_irq;
|
|
||||||
bool irq_shared;
|
bool irq_shared;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,18 @@ struct gpio_flag flagnames[] = {
|
||||||
.name = "open-source",
|
.name = "open-source",
|
||||||
.mask = GPIOLINE_FLAG_OPEN_SOURCE,
|
.mask = GPIOLINE_FLAG_OPEN_SOURCE,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "pull-up",
|
||||||
|
.mask = GPIOLINE_FLAG_BIAS_PULL_UP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "pull-down",
|
||||||
|
.mask = GPIOLINE_FLAG_BIAS_PULL_DOWN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "bias-disabled",
|
||||||
|
.mask = GPIOLINE_FLAG_BIAS_DISABLE,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void print_flags(unsigned long flags)
|
void print_flags(unsigned long flags)
|
||||||
|
|
Loading…
Reference in a new issue