This is the bulk of GPIO changes for the v4.20 series:
Core changes: - A patch series from Hans Verkuil to make it possible to enable/disable IRQs on a GPIO line at runtime and drive GPIO lines as output without having to put/get them from scratch. The irqchip callbacks have been improved so that they can use only the fastpatch callbacks to enable/disable irqs like any normal irqchip, especially the gpiod_lock_as_irq() has been improved to be callable in fastpath context. A bunch of rework had to be done to achieve this but it is a big win since I never liked to restrict this to slowpath. The only call requireing slowpath was try_module_get() and this is kept at the .request_resources() slowpath callback. In the GPIO CEC driver this is a big win sine a single line is used for both outgoing and incoming traffic, and this needs to use IRQs for incoming traffic while actively driving the line for outgoing traffic. - Janusz Krzysztofik improved the GPIO array API to pass a "cookie" (struct gpio_array) and a bitmap for setting or getting multiple GPIO lines at once. This improvement orginated in a specific need to speed up an OMAP1 driver and has led to a much better API and real performance gains when the state of the array can be used to bypass a lot of checks and code when we want things to go really fast. The previous code would minimize the number of calls down to the driver callbacks assuming the CPU speed was orders of magnitude faster than the I/O latency, but this assumption was wrong on several platforms: what we needed to do was to profile and improve the speed on the hot path of the array functions and this change is now completed. - Clean out the painful and hard to grasp BNF experiments from the device tree bindings. Future approaches are looking into using JSON schema for this purpose. (Rob Herring is floating a patch series.) New drivers: - The RCAR driver now supports r8a774a1 (RZ/G2M). - Synopsys GPIO via CREGs driver. Major improvements: - Modernization of the EP93xx driver to use irqdomain and other contemporary concepts. - The ingenic driver has been merged into the Ingenic pin control driver and removed from the GPIO subsystem. - Debounce support in the ftgpio010 driver. -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJbzdyOAAoJEEEQszewGV1zfYcP/0HBEAOPhHD/i5OQxfKs1msh mFT/t/IbTmRpCgbEv4CDx4Kc/InE0sUnQr1TL/1WvU6uObM6Ncxq5Z90MvyrgzYu BqQHq2k2tORvkVSNRxcfD/BAAoo1EerXts1kDhutvdKfepfS6DxpENwzvsFgkVlq 2jj1cdZztjv8A+9cspHDpQP+jDvl1VSc10nR5fRu1TttSpUwzRJaB30NBNXJmMJc 5KUr67lEbsQRPsBvFErU11bydPqhfT+pXmODcfIwS0EtATQ8WC5mkSb/Ooei0fvT oZ7uR3Os8tMf7isOKssEyFabKwhnfOEt6TBt9em0TfUtInOo0Dc7r8TfBcn57fyZ xg2R9DQEVRfac8bjhF/BI5KHuN9IMGDDvj6XApumQVliZbISRjMnh3jte6RpcV0A Ejqz8FeDY13qvEdOnW1EPpwmXdDVWiEAq0ebGLStKNls+/4gB2HmyxGUOzJf+og5 hujsxcJzGQqjCe0moeY/1d7vsy0ZjbHoS+p5fy79U212y2O7onEzFU92AX89bxKC rx2eCNmiZxCUy1nqu8edO62VnH6QdnqG3o+a4DJfCSHPvFM/E/NX9zHemZubQQ4I rYXNy4bL4tEG9cqWMfBxWrpiDZw7H6l8kXwdZG8IMyRU9BcKu96amgZ+jBXwzoaB JZelAAUWB9APghJYFr7o =YosT -----END PGP SIGNATURE----- Merge tag 'gpio-v4.20-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 v4.20 series: Core changes: - A patch series from Hans Verkuil to make it possible to enable/disable IRQs on a GPIO line at runtime and drive GPIO lines as output without having to put/get them from scratch. The irqchip callbacks have been improved so that they can use only the fastpatch callbacks to enable/disable irqs like any normal irqchip, especially the gpiod_lock_as_irq() has been improved to be callable in fastpath context. A bunch of rework had to be done to achieve this but it is a big win since I never liked to restrict this to slowpath. The only call requireing slowpath was try_module_get() and this is kept at the .request_resources() slowpath callback. In the GPIO CEC driver this is a big win sine a single line is used for both outgoing and incoming traffic, and this needs to use IRQs for incoming traffic while actively driving the line for outgoing traffic. - Janusz Krzysztofik improved the GPIO array API to pass a "cookie" (struct gpio_array) and a bitmap for setting or getting multiple GPIO lines at once. This improvement orginated in a specific need to speed up an OMAP1 driver and has led to a much better API and real performance gains when the state of the array can be used to bypass a lot of checks and code when we want things to go really fast. The previous code would minimize the number of calls down to the driver callbacks assuming the CPU speed was orders of magnitude faster than the I/O latency, but this assumption was wrong on several platforms: what we needed to do was to profile and improve the speed on the hot path of the array functions and this change is now completed. - Clean out the painful and hard to grasp BNF experiments from the device tree bindings. Future approaches are looking into using JSON schema for this purpose. (Rob Herring is floating a patch series.) New drivers: - The RCAR driver now supports r8a774a1 (RZ/G2M). - Synopsys GPIO via CREGs driver. Major improvements: - Modernization of the EP93xx driver to use irqdomain and other contemporary concepts. - The ingenic driver has been merged into the Ingenic pin control driver and removed from the GPIO subsystem. - Debounce support in the ftgpio010 driver" * tag 'gpio-v4.20-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (116 commits) gpio: Clarify kerneldoc on gpiochip_set_chained_irqchip() gpio: Remove unused 'irqchip' argument to gpiochip_set_cascaded_irqchip() gpio: Drop parent irq assignment during cascade setup mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap gpio: fix SNPS_CREG kconfig dependency warning gpiolib: Initialize gdev field before is used gpio: fix kernel-doc after devres.c file rename gpio: fix doc string for devm_gpiochip_add_data() to not talk about irq_chip gpio: syscon: Fix possible NULL ptr usage gpiolib: Show correct direction from the beginning pinctrl: msm: Use init_valid_mask exported function gpiolib: Add init_valid_mask exported function GPIO: add single-register GPIO via CREG driver dt-bindings: Document the Synopsys GPIO via CREG bindings gpio: mockup: use device properties instead of platform_data gpio: Slightly more helpful debugfs gpio: omap: Remove set but not used variable 'dev' gpio: omap: drop omap_gpio_list Accept partial 'gpio-line-names' property. gpio: omap: get rid of the conditional PM runtime calls ...
This commit is contained in:
commit
114b5f8f7e
|
@ -1,18 +1,9 @@
|
|||
Specifying GPIO information for devices
|
||||
============================================
|
||||
=======================================
|
||||
|
||||
1) gpios property
|
||||
-----------------
|
||||
|
||||
Nodes that makes use of GPIOs should specify them using one or more
|
||||
properties, each containing a 'gpio-list':
|
||||
|
||||
gpio-list ::= <single-gpio> [gpio-list]
|
||||
single-gpio ::= <gpio-phandle> <gpio-specifier>
|
||||
gpio-phandle : phandle to gpio controller node
|
||||
gpio-specifier : Array of #gpio-cells specifying specific gpio
|
||||
(controller specific)
|
||||
|
||||
GPIO properties should be named "[<name>-]gpios", with <name> being the purpose
|
||||
of this GPIO for the device. While a non-existent <name> is considered valid
|
||||
for compatibility reasons (resolving to the "gpios" property), it is not allowed
|
||||
|
@ -33,33 +24,27 @@ The following example could be used to describe GPIO pins used as device enable
|
|||
and bit-banged data signals:
|
||||
|
||||
gpio1: gpio1 {
|
||||
gpio-controller
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
gpio2: gpio2 {
|
||||
gpio-controller
|
||||
#gpio-cells = <1>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
[...]
|
||||
|
||||
enable-gpios = <&gpio2 2>;
|
||||
data-gpios = <&gpio1 12 0>,
|
||||
<&gpio1 13 0>,
|
||||
<&gpio1 14 0>,
|
||||
<&gpio1 15 0>;
|
||||
|
||||
Note that gpio-specifier length is controller dependent. In the
|
||||
above example, &gpio1 uses 2 cells to specify a gpio, while &gpio2
|
||||
only uses one.
|
||||
In the above example, &gpio1 uses 2 cells to specify a gpio. The first cell is
|
||||
a local offset to the GPIO line and the second cell represent consumer flags,
|
||||
such as if the consumer desire the line to be active low (inverted) or open
|
||||
drain. This is the recommended practice.
|
||||
|
||||
gpio-specifier may encode: bank, pin position inside the bank,
|
||||
whether pin is open-drain and whether pin is logically inverted.
|
||||
The exact meaning of each specifier cell is controller specific, and must be
|
||||
documented in the device tree binding for the device, but it is strongly
|
||||
recommended to use the two-cell approach.
|
||||
|
||||
Exact meaning of each specifier cell is controller specific, and must
|
||||
be documented in the device tree binding for the device.
|
||||
|
||||
Most controllers are however specifying a generic flag bitfield
|
||||
in the last cell, so for these, use the macros defined in
|
||||
Most controllers are specifying a generic flag bitfield in the last cell, so
|
||||
for these, use the macros defined in
|
||||
include/dt-bindings/gpio/gpio.h whenever possible:
|
||||
|
||||
Example of a node using GPIOs:
|
||||
|
@ -236,46 +221,40 @@ Example of two SOC GPIO banks defined as gpio-controller nodes:
|
|||
|
||||
Some or all of the GPIOs provided by a GPIO controller may be routed to pins
|
||||
on the package via a pin controller. This allows muxing those pins between
|
||||
GPIO and other functions.
|
||||
GPIO and other functions. It is a fairly common practice among silicon
|
||||
engineers.
|
||||
|
||||
2.2) Ordinary (numerical) GPIO ranges
|
||||
-------------------------------------
|
||||
|
||||
It is useful to represent which GPIOs correspond to which pins on which pin
|
||||
controllers. The gpio-ranges property described below represents this, and
|
||||
contains information structures as follows:
|
||||
controllers. The gpio-ranges property described below represents this with
|
||||
a discrete set of ranges mapping pins from the pin controller local number space
|
||||
to pins in the GPIO controller local number space.
|
||||
|
||||
gpio-range-list ::= <single-gpio-range> [gpio-range-list]
|
||||
single-gpio-range ::= <numeric-gpio-range> | <named-gpio-range>
|
||||
numeric-gpio-range ::=
|
||||
<pinctrl-phandle> <gpio-base> <pinctrl-base> <count>
|
||||
named-gpio-range ::= <pinctrl-phandle> <gpio-base> '<0 0>'
|
||||
pinctrl-phandle : phandle to pin controller node
|
||||
gpio-base : Base GPIO ID in the GPIO controller
|
||||
pinctrl-base : Base pinctrl pin ID in the pin controller
|
||||
count : The number of GPIOs/pins in this range
|
||||
The format is: <[pin controller phandle], [GPIO controller offset],
|
||||
[pin controller offset], [number of pins]>;
|
||||
|
||||
The "pin controller node" mentioned above must conform to the bindings
|
||||
described in ../pinctrl/pinctrl-bindings.txt.
|
||||
The GPIO controller offset pertains to the GPIO controller node containing the
|
||||
range definition.
|
||||
|
||||
In case named gpio ranges are used (ranges with both <pinctrl-base> and
|
||||
<count> set to 0), the property gpio-ranges-group-names contains one string
|
||||
for every single-gpio-range in gpio-ranges:
|
||||
gpiorange-names-list ::= <gpiorange-name> [gpiorange-names-list]
|
||||
gpiorange-name : Name of the pingroup associated to the GPIO range in
|
||||
the respective pin controller.
|
||||
The pin controller node referenced by the phandle must conform to the bindings
|
||||
described in pinctrl/pinctrl-bindings.txt.
|
||||
|
||||
Elements of gpiorange-names-list corresponding to numeric ranges contain
|
||||
the empty string. Elements of gpiorange-names-list corresponding to named
|
||||
ranges contain the name of a pin group defined in the respective pin
|
||||
controller. The number of pins/GPIOs in the range is the number of pins in
|
||||
that pin group.
|
||||
Each offset runs from 0 to N. It is perfectly fine to pile any number of
|
||||
ranges with just one pin-to-GPIO line mapping if the ranges are concocted, but
|
||||
in practice these ranges are often lumped in discrete sets.
|
||||
|
||||
Previous versions of this binding required all pin controller nodes that
|
||||
were referenced by any gpio-ranges property to contain a property named
|
||||
#gpio-range-cells with value <3>. This requirement is now deprecated.
|
||||
However, that property may still exist in older device trees for
|
||||
compatibility reasons, and would still be required even in new device
|
||||
trees that need to be compatible with older software.
|
||||
Example:
|
||||
|
||||
Example 1:
|
||||
gpio-ranges = <&foo 0 20 10>, <&bar 10 50 20>;
|
||||
|
||||
This means:
|
||||
- pins 20..29 on pin controller "foo" is mapped to GPIO line 0..9 and
|
||||
- pins 50..69 on pin controller "bar" is mapped to GPIO line 10..29
|
||||
|
||||
|
||||
Verbose example:
|
||||
|
||||
qe_pio_e: gpio-controller@1460 {
|
||||
#gpio-cells = <2>;
|
||||
|
@ -289,7 +268,28 @@ Here, a single GPIO controller has GPIOs 0..9 routed to pin controller
|
|||
pinctrl1's pins 20..29, and GPIOs 10..29 routed to pin controller pinctrl2's
|
||||
pins 50..69.
|
||||
|
||||
Example 2:
|
||||
|
||||
2.3) GPIO ranges from named pin groups
|
||||
--------------------------------------
|
||||
|
||||
It is also possible to use pin groups for gpio ranges when pin groups are the
|
||||
easiest and most convenient mapping.
|
||||
|
||||
Both both <pinctrl-base> and <count> must set to 0 when using named pin groups
|
||||
names.
|
||||
|
||||
The property gpio-ranges-group-names must contain exactly one string for each
|
||||
range.
|
||||
|
||||
Elements of gpio-ranges-group-names must contain the name of a pin group
|
||||
defined in the respective pin controller. The number of pins/GPIO lines in the
|
||||
range is the number of pins in that pin group. The number of pins of that
|
||||
group is defined int the implementation and not in the device tree.
|
||||
|
||||
If numerical and named pin groups are mixed, the string corresponding to a
|
||||
numerical pin range in gpio-ranges-group-names must be empty.
|
||||
|
||||
Example:
|
||||
|
||||
gpio_pio_i: gpio-controller@14b0 {
|
||||
#gpio-cells = <2>;
|
||||
|
@ -306,6 +306,14 @@ Example 2:
|
|||
"bar";
|
||||
};
|
||||
|
||||
Here, three GPIO ranges are defined wrt. two pin controllers. pinctrl1 GPIO
|
||||
ranges are defined using pin numbers whereas the GPIO ranges wrt. pinctrl2
|
||||
are named "foo" and "bar".
|
||||
Here, three GPIO ranges are defined referring to two pin controllers.
|
||||
|
||||
pinctrl1 GPIO ranges are defined using pin numbers whereas the GPIO ranges
|
||||
in pinctrl2 are defined using the pin groups named "foo" and "bar".
|
||||
|
||||
Previous versions of this binding required all pin controller nodes that
|
||||
were referenced by any gpio-ranges property to contain a property named
|
||||
#gpio-range-cells with value <3>. This requirement is now deprecated.
|
||||
However, that property may still exist in older device trees for
|
||||
compatibility reasons, and would still be required even in new device
|
||||
trees that need to be compatible with older software.
|
||||
|
|
|
@ -4,8 +4,10 @@ Required Properties:
|
|||
|
||||
- compatible: should contain one or more of the following:
|
||||
- "renesas,gpio-r8a7743": for R8A7743 (RZ/G1M) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7744": for R8A7744 (RZ/G1N) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7745": for R8A7745 (RZ/G1E) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a77470": for R8A77470 (RZ/G1C) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a774a1": for R8A774A1 (RZ/G2M) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7778": for R8A7778 (R-Car M1) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller.
|
||||
|
@ -22,7 +24,7 @@ Required Properties:
|
|||
- "renesas,gpio-r8a77995": for R8A77995 (R-Car D3) compatible GPIO controller.
|
||||
- "renesas,rcar-gen1-gpio": for a generic R-Car Gen1 GPIO controller.
|
||||
- "renesas,rcar-gen2-gpio": for a generic R-Car Gen2 or RZ/G1 GPIO controller.
|
||||
- "renesas,rcar-gen3-gpio": for a generic R-Car Gen3 GPIO controller.
|
||||
- "renesas,rcar-gen3-gpio": for a generic R-Car Gen3 or RZ/G2 GPIO controller.
|
||||
- "renesas,gpio-rcar": deprecated.
|
||||
|
||||
When compatible with the generic version nodes must list the
|
||||
|
@ -38,7 +40,7 @@ Required Properties:
|
|||
- #gpio-cells: Should be 2. The first cell is the GPIO number and the second
|
||||
cell specifies GPIO flags, as defined in <dt-bindings/gpio/gpio.h>. Only the
|
||||
GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported.
|
||||
- gpio-ranges: Range of pins managed by the GPIO controller.
|
||||
- gpio-ranges: See gpio.txt.
|
||||
|
||||
Optional properties:
|
||||
|
||||
|
@ -46,35 +48,44 @@ Optional properties:
|
|||
mandatory if the hardware implements a controllable functional clock for
|
||||
the GPIO instance.
|
||||
|
||||
Please refer to gpio.txt in this directory for details of gpio-ranges property
|
||||
and the common GPIO bindings used by client devices.
|
||||
- gpio-reserved-ranges: See gpio.txt.
|
||||
|
||||
Please refer to gpio.txt in this directory for the common GPIO bindings used by
|
||||
client devices.
|
||||
|
||||
The GPIO controller also acts as an interrupt controller. It uses the default
|
||||
two cells specifier as described in Documentation/devicetree/bindings/
|
||||
interrupt-controller/interrupts.txt.
|
||||
|
||||
Example: R8A7779 (R-Car H1) GPIO controller nodes
|
||||
Example: R8A77470 (RZ/G1C) GPIO controller nodes
|
||||
|
||||
gpio0: gpio@ffc40000 {
|
||||
compatible = "renesas,gpio-r8a7779", "renesas,rcar-gen1-gpio";
|
||||
reg = <0xffc40000 0x2c>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 141 0x4>;
|
||||
#gpio-cells = <2>;
|
||||
gpio-controller;
|
||||
gpio-ranges = <&pfc 0 0 32>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
gpio0: gpio@e6050000 {
|
||||
compatible = "renesas,gpio-r8a77470",
|
||||
"renesas,rcar-gen2-gpio";
|
||||
reg = <0 0xe6050000 0 0x50>;
|
||||
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#gpio-cells = <2>;
|
||||
gpio-controller;
|
||||
gpio-ranges = <&pfc 0 0 23>;
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-controller;
|
||||
clocks = <&cpg CPG_MOD 912>;
|
||||
power-domains = <&sysc R8A77470_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 912>;
|
||||
};
|
||||
...
|
||||
gpio6: gpio@ffc46000 {
|
||||
compatible = "renesas,gpio-r8a7779", "renesas,rcar-gen1-gpio";
|
||||
reg = <0xffc46000 0x2c>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 147 0x4>;
|
||||
#gpio-cells = <2>;
|
||||
gpio-controller;
|
||||
gpio-ranges = <&pfc 0 192 9>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
gpio3: gpio@e6053000 {
|
||||
compatible = "renesas,gpio-r8a77470",
|
||||
"renesas,rcar-gen2-gpio";
|
||||
reg = <0 0xe6053000 0 0x50>;
|
||||
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#gpio-cells = <2>;
|
||||
gpio-controller;
|
||||
gpio-ranges = <&pfc 0 96 30>;
|
||||
gpio-reserved-ranges = <17 10>;
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-controller;
|
||||
clocks = <&cpg CPG_MOD 909>;
|
||||
power-domains = <&sysc R8A77470_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 909>;
|
||||
};
|
||||
|
|
21
Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt
Normal file
21
Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
Synopsys GPIO via CREG (Control REGisters) driver
|
||||
|
||||
Required properties:
|
||||
- compatible : "snps,creg-gpio-hsdk" or "snps,creg-gpio-axs10x".
|
||||
- reg : Exactly one register range with length 0x4.
|
||||
- #gpio-cells : Since the generic GPIO binding is used, the
|
||||
amount of cells must be specified as 2. The first cell is the
|
||||
pin number, the second cell is used to specify optional parameters:
|
||||
See "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt.
|
||||
- gpio-controller : Marks the device node as a GPIO controller.
|
||||
- ngpios: Number of GPIO pins.
|
||||
|
||||
Example:
|
||||
|
||||
gpio: gpio@f00014b0 {
|
||||
compatible = "snps,creg-gpio-hsdk";
|
||||
reg = <0xf00014b0 0x4>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <2>;
|
||||
};
|
|
@ -193,3 +193,27 @@ And the table can be added to the board code as follows::
|
|||
|
||||
The line will be hogged as soon as the gpiochip is created or - in case the
|
||||
chip was created earlier - when the hog table is registered.
|
||||
|
||||
Arrays of pins
|
||||
--------------
|
||||
In addition to requesting pins belonging to a function one by one, a device may
|
||||
also request an array of pins assigned to the function. The way those pins are
|
||||
mapped to the device determines if the array qualifies for fast bitmap
|
||||
processing. If yes, a bitmap is passed over get/set array functions directly
|
||||
between a caller and a respective .get/set_multiple() callback of a GPIO chip.
|
||||
|
||||
In order to qualify for fast bitmap processing, the array must meet the
|
||||
following requirements:
|
||||
- pin hardware number of array member 0 must also be 0,
|
||||
- pin hardware numbers of consecutive array members which belong to the same
|
||||
chip as member 0 does must also match their array indexes.
|
||||
|
||||
Otherwise fast bitmap processing path is not used in order to avoid consecutive
|
||||
pins which belong to the same chip but are not in hardware order being processed
|
||||
separately.
|
||||
|
||||
If the array applies for fast bitmap processing path, pins which belong to
|
||||
different chips than member 0 does, as well as those with indexes different from
|
||||
their hardware pin numbers, are excluded from the fast path, both input and
|
||||
output. Moreover, open drain and open source pins are excluded from fast bitmap
|
||||
output processing.
|
||||
|
|
|
@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call::
|
|||
enum gpiod_flags flags)
|
||||
|
||||
This function returns a struct gpio_descs which contains an array of
|
||||
descriptors::
|
||||
descriptors. It also contains a pointer to a gpiolib private structure which,
|
||||
if passed back to get/set array functions, may speed up I/O proocessing::
|
||||
|
||||
struct gpio_descs {
|
||||
struct gpio_array *info;
|
||||
unsigned int ndescs;
|
||||
struct gpio_desc *desc[];
|
||||
}
|
||||
|
@ -323,29 +325,37 @@ The following functions get or set the values of an array of GPIOs::
|
|||
|
||||
int gpiod_get_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
int gpiod_get_raw_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
int gpiod_get_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
|
||||
void gpiod_set_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
void gpiod_set_raw_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
void gpiod_set_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
int gpiod_set_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
int gpiod_set_raw_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
int gpiod_set_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
|
||||
The array can be an arbitrary set of GPIOs. The functions will try to access
|
||||
GPIOs belonging to the same bank or chip simultaneously if supported by the
|
||||
|
@ -356,8 +366,9 @@ accessed sequentially.
|
|||
The functions take three arguments:
|
||||
* array_size - the number of array elements
|
||||
* desc_array - an array of GPIO descriptors
|
||||
* value_array - an array to store the GPIOs' values (get) or
|
||||
an array of values to assign to the GPIOs (set)
|
||||
* array_info - optional information obtained from gpiod_array_get()
|
||||
* value_bitmap - a bitmap to store the GPIOs' values (get) or
|
||||
a bitmap of values to assign to the GPIOs (set)
|
||||
|
||||
The descriptor array can be obtained using the gpiod_get_array() function
|
||||
or one of its variants. If the group of descriptors returned by that function
|
||||
|
@ -366,16 +377,25 @@ the struct gpio_descs returned by gpiod_get_array()::
|
|||
|
||||
struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
|
||||
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
|
||||
my_gpio_values);
|
||||
my_gpio_descs->info, my_gpio_value_bitmap);
|
||||
|
||||
It is also possible to access a completely arbitrary array of descriptors. The
|
||||
descriptors may be obtained using any combination of gpiod_get() and
|
||||
gpiod_get_array(). Afterwards the array of descriptors has to be setup
|
||||
manually before it can be passed to one of the above functions.
|
||||
manually before it can be passed to one of the above functions. In that case,
|
||||
array_info should be set to NULL.
|
||||
|
||||
Note that for optimal performance GPIOs belonging to the same chip should be
|
||||
contiguous within the array of descriptors.
|
||||
|
||||
Still better performance may be achieved if array indexes of the descriptors
|
||||
match hardware pin numbers of a single chip. If an array passed to a get/set
|
||||
array function matches the one obtained from gpiod_get_array() and array_info
|
||||
associated with the array is also passed, the function may take a fast bitmap
|
||||
processing path, passing the value_bitmap argument directly to the respective
|
||||
.get/set_multiple() callback of the chip. That allows for utilization of GPIO
|
||||
banks as data I/O ports without much loss of performance.
|
||||
|
||||
The return value of gpiod_get_array_value() and its variants is 0 on success
|
||||
or negative on error. Note the difference to gpiod_get_value(), which returns
|
||||
0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
|
||||
|
|
|
@ -374,7 +374,28 @@ When implementing an irqchip inside a GPIO driver, these two functions should
|
|||
typically be called in the .startup() and .shutdown() callbacks from the
|
||||
irqchip.
|
||||
|
||||
When using the gpiolib irqchip helpers, these callback are automatically
|
||||
When using the gpiolib irqchip helpers, these callbacks are automatically
|
||||
assigned.
|
||||
|
||||
|
||||
Disabling and enabling IRQs
|
||||
---------------------------
|
||||
When a GPIO is used as an IRQ signal, then gpiolib also needs to know if
|
||||
the IRQ is enabled or disabled. In order to inform gpiolib about this,
|
||||
a driver should call::
|
||||
|
||||
void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset)
|
||||
|
||||
This allows drivers to drive the GPIO as an output while the IRQ is
|
||||
disabled. When the IRQ is enabled again, a driver should call::
|
||||
|
||||
void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset)
|
||||
|
||||
When implementing an irqchip inside a GPIO driver, these two functions should
|
||||
typically be called in the .irq_disable() and .irq_enable() callbacks from the
|
||||
irqchip.
|
||||
|
||||
When using the gpiolib irqchip helpers, these callbacks are automatically
|
||||
assigned.
|
||||
|
||||
Real-Time compliance for GPIO IRQ chips
|
||||
|
|
|
@ -38,7 +38,7 @@ Device tree support
|
|||
Device-managed API
|
||||
==================
|
||||
|
||||
.. kernel-doc:: drivers/gpio/devres.c
|
||||
.. kernel-doc:: drivers/gpio/gpiolib-devres.c
|
||||
:export:
|
||||
|
||||
sysfs helpers
|
||||
|
|
13
MAINTAINERS
13
MAINTAINERS
|
@ -7396,6 +7396,12 @@ T: git https://github.com/intel/gvt-linux.git
|
|||
S: Supported
|
||||
F: drivers/gpu/drm/i915/gvt/
|
||||
|
||||
INTEL PMIC GPIO DRIVER
|
||||
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-*cove.c
|
||||
F: drivers/gpio/gpio-msic.c
|
||||
|
||||
INTEL HID EVENT DRIVER
|
||||
M: Alex Hung <alex.hung@canonical.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
|
@ -13352,6 +13358,7 @@ M: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
|
|||
R: Pengutronix Kernel Team <kernel@pengutronix.de>
|
||||
S: Supported
|
||||
F: drivers/siox/*
|
||||
F: drivers/gpio/gpio-siox.c
|
||||
F: include/trace/events/siox.h
|
||||
|
||||
SIS 190 ETHERNET DRIVER
|
||||
|
@ -14079,6 +14086,12 @@ S: Supported
|
|||
F: drivers/reset/reset-axs10x.c
|
||||
F: Documentation/devicetree/bindings/reset/snps,axs10x-reset.txt
|
||||
|
||||
SYNOPSYS CREG GPIO DRIVER
|
||||
M: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-creg-snps.c
|
||||
F: Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt
|
||||
|
||||
SYNOPSYS DESIGNWARE 8250 UART DRIVER
|
||||
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
S: Maintained
|
||||
|
|
|
@ -130,10 +130,10 @@ static struct platform_device davinci_fb_device = {
|
|||
};
|
||||
|
||||
static const struct gpio_led ntosd2_leds[] = {
|
||||
{ .name = "led1_green", .gpio = GPIO(10), },
|
||||
{ .name = "led1_red", .gpio = GPIO(11), },
|
||||
{ .name = "led2_green", .gpio = GPIO(12), },
|
||||
{ .name = "led2_red", .gpio = GPIO(13), },
|
||||
{ .name = "led1_green", .gpio = 10, },
|
||||
{ .name = "led1_red", .gpio = 11, },
|
||||
{ .name = "led2_green", .gpio = 12, },
|
||||
{ .name = "led2_red", .gpio = 13, },
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data ntosd2_leds_data = {
|
||||
|
|
|
@ -141,6 +141,15 @@ EXPORT_SYMBOL_GPL(ep93xx_chip_revision);
|
|||
*************************************************************************/
|
||||
static struct resource ep93xx_gpio_resource[] = {
|
||||
DEFINE_RES_MEM(EP93XX_GPIO_PHYS_BASE, 0xcc),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO_AB),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO0MUX),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO1MUX),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO2MUX),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO3MUX),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO4MUX),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO5MUX),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO6MUX),
|
||||
DEFINE_RES_IRQ(IRQ_EP93XX_GPIO7MUX),
|
||||
};
|
||||
|
||||
static struct platform_device ep93xx_gpio_device = {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
@ -29,8 +30,6 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/platform_data/gpio-omap.h>
|
||||
|
||||
#include <asm/fncpy.h>
|
||||
|
||||
|
@ -87,7 +86,7 @@ static int omap2_enter_full_retention(void)
|
|||
l = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0) | OMAP24XX_USBSTANDBYCTRL;
|
||||
omap_ctrl_writel(l, OMAP2_CONTROL_DEVCONF0);
|
||||
|
||||
omap2_gpio_prepare_for_idle(0);
|
||||
cpu_cluster_pm_enter();
|
||||
|
||||
/* One last check for pending IRQs to avoid extra latency due
|
||||
* to sleeping unnecessarily. */
|
||||
|
@ -100,7 +99,7 @@ static int omap2_enter_full_retention(void)
|
|||
OMAP_SDRC_REGADDR(SDRC_POWER));
|
||||
|
||||
no_sleep:
|
||||
omap2_gpio_resume_after_idle();
|
||||
cpu_cluster_pm_exit();
|
||||
|
||||
clk_enable(osc_ck);
|
||||
|
||||
|
|
|
@ -18,19 +18,18 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/omap-dma.h>
|
||||
#include <linux/omap-gpmc.h>
|
||||
#include <linux/platform_data/gpio-omap.h>
|
||||
|
||||
#include <trace/events/power.h>
|
||||
|
||||
|
@ -197,7 +196,6 @@ void omap_sram_idle(void)
|
|||
int mpu_next_state = PWRDM_POWER_ON;
|
||||
int per_next_state = PWRDM_POWER_ON;
|
||||
int core_next_state = PWRDM_POWER_ON;
|
||||
int per_going_off;
|
||||
u32 sdrc_pwr = 0;
|
||||
|
||||
mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
|
||||
|
@ -227,10 +225,8 @@ void omap_sram_idle(void)
|
|||
pwrdm_pre_transition(NULL);
|
||||
|
||||
/* PER */
|
||||
if (per_next_state < PWRDM_POWER_ON) {
|
||||
per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
|
||||
omap2_gpio_prepare_for_idle(per_going_off);
|
||||
}
|
||||
if (per_next_state == PWRDM_POWER_OFF)
|
||||
cpu_cluster_pm_enter();
|
||||
|
||||
/* CORE */
|
||||
if (core_next_state < PWRDM_POWER_ON) {
|
||||
|
@ -295,8 +291,8 @@ void omap_sram_idle(void)
|
|||
pwrdm_post_transition(NULL);
|
||||
|
||||
/* PER */
|
||||
if (per_next_state < PWRDM_POWER_ON)
|
||||
omap2_gpio_resume_after_idle();
|
||||
if (per_next_state == PWRDM_POWER_OFF)
|
||||
cpu_cluster_pm_exit();
|
||||
}
|
||||
|
||||
static void omap3_pm_idle(void)
|
||||
|
|
|
@ -51,12 +51,4 @@ typedef enum {
|
|||
|
||||
extern void vr41xx_set_irq_level(unsigned int pin, irq_level_t level);
|
||||
|
||||
typedef enum {
|
||||
GPIO_PULL_DOWN,
|
||||
GPIO_PULL_UP,
|
||||
GPIO_PULL_DISABLE,
|
||||
} gpio_pull_t;
|
||||
|
||||
extern int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull);
|
||||
|
||||
#endif /* __NEC_VR41XX_GIU_H */
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_data/gpio-ts5500.h>
|
||||
#include <linux/platform_data/max197.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -62,20 +62,15 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
|
|||
/* write to an LCD panel register in 8 bit GPIO mode */
|
||||
static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
|
||||
{
|
||||
int values[10]; /* for DATA[0-7], RS, RW */
|
||||
unsigned int i, n;
|
||||
DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */
|
||||
unsigned int n;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
values[PIN_DATA0 + i] = !!(val & BIT(i));
|
||||
values[PIN_CTRL_RS] = rs;
|
||||
n = 9;
|
||||
if (hd->pins[PIN_CTRL_RW]) {
|
||||
values[PIN_CTRL_RW] = 0;
|
||||
n++;
|
||||
}
|
||||
values[0] = val;
|
||||
__assign_bit(8, values, rs);
|
||||
n = hd->pins[PIN_CTRL_RW] ? 10 : 9;
|
||||
|
||||
/* Present the data to the port */
|
||||
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
|
||||
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL, values);
|
||||
|
||||
hd44780_strobe_gpio(hd);
|
||||
}
|
||||
|
@ -83,32 +78,25 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
|
|||
/* write to an LCD panel register in 4 bit GPIO mode */
|
||||
static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
|
||||
{
|
||||
int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
|
||||
unsigned int i, n;
|
||||
DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
|
||||
unsigned int n;
|
||||
|
||||
/* High nibble + RS, RW */
|
||||
for (i = 4; i < 8; i++)
|
||||
values[PIN_DATA0 + i] = !!(val & BIT(i));
|
||||
values[PIN_CTRL_RS] = rs;
|
||||
n = 5;
|
||||
if (hd->pins[PIN_CTRL_RW]) {
|
||||
values[PIN_CTRL_RW] = 0;
|
||||
n++;
|
||||
}
|
||||
values[0] = val >> 4;
|
||||
__assign_bit(4, values, rs);
|
||||
n = hd->pins[PIN_CTRL_RW] ? 6 : 5;
|
||||
|
||||
/* Present the data to the port */
|
||||
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
|
||||
&values[PIN_DATA4]);
|
||||
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);
|
||||
|
||||
hd44780_strobe_gpio(hd);
|
||||
|
||||
/* Low nibble */
|
||||
for (i = 0; i < 4; i++)
|
||||
values[PIN_DATA4 + i] = !!(val & BIT(i));
|
||||
values[0] &= ~0x0fUL;
|
||||
values[0] |= val & 0x0f;
|
||||
|
||||
/* Present the data to the port */
|
||||
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
|
||||
&values[PIN_DATA4]);
|
||||
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);
|
||||
|
||||
hd44780_strobe_gpio(hd);
|
||||
}
|
||||
|
@ -155,23 +143,16 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
|
|||
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
|
||||
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
|
||||
{
|
||||
int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
|
||||
DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
|
||||
struct hd44780 *hd = lcd->drvdata;
|
||||
unsigned int i, n;
|
||||
unsigned int n;
|
||||
|
||||
/* Command nibble + RS, RW */
|
||||
for (i = 0; i < 4; i++)
|
||||
values[PIN_DATA4 + i] = !!(cmd & BIT(i));
|
||||
values[PIN_CTRL_RS] = 0;
|
||||
n = 5;
|
||||
if (hd->pins[PIN_CTRL_RW]) {
|
||||
values[PIN_CTRL_RW] = 0;
|
||||
n++;
|
||||
}
|
||||
values[0] = cmd & 0x0f;
|
||||
n = hd->pins[PIN_CTRL_RW] ? 6 : 5;
|
||||
|
||||
/* Present the data to the port */
|
||||
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
|
||||
&values[PIN_DATA4]);
|
||||
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);
|
||||
|
||||
hd44780_strobe_gpio(hd);
|
||||
}
|
||||
|
|
|
@ -110,13 +110,12 @@ static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
|
|||
*/
|
||||
static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
|
||||
{
|
||||
int i;
|
||||
int values[8];
|
||||
DECLARE_BITMAP(values, 8);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
values[i] = 0;
|
||||
values[0] = 0;
|
||||
|
||||
gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
|
||||
gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
|
||||
ts_nbus->data->info, values);
|
||||
gpiod_set_value_cansleep(ts_nbus->csn, 0);
|
||||
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
|
||||
gpiod_set_value_cansleep(ts_nbus->ale, 0);
|
||||
|
@ -157,16 +156,11 @@ static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
|
|||
static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
|
||||
{
|
||||
struct gpio_descs *gpios = ts_nbus->data;
|
||||
int i;
|
||||
int values[8];
|
||||
DECLARE_BITMAP(values, 8);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
if (byte & BIT(i))
|
||||
values[i] = 1;
|
||||
else
|
||||
values[i] = 0;
|
||||
values[0] = byte;
|
||||
|
||||
gpiod_set_array_value_cansleep(8, gpios->desc, values);
|
||||
gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info, values);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -200,6 +200,7 @@ config GPIO_EP93XX
|
|||
def_bool y
|
||||
depends on ARCH_EP93XX
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
|
||||
config GPIO_EXAR
|
||||
tristate "Support for GPIO pins on XR17V352/354/358"
|
||||
|
@ -428,6 +429,24 @@ config GPIO_REG
|
|||
A 32-bit single register GPIO fixed in/out implementation. This
|
||||
can be used to represent any register as a set of GPIO signals.
|
||||
|
||||
config GPIO_SIOX
|
||||
tristate "SIOX GPIO support"
|
||||
depends on SIOX
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support SIOX I/O devices. These are units connected
|
||||
via a SIOX bus and have a number of fixed-direction I/O lines.
|
||||
|
||||
config GPIO_SNPS_CREG
|
||||
bool "Synopsys GPIO via CREG (Control REGisters) driver"
|
||||
depends on ARC || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
help
|
||||
This driver supports GPIOs via CREG on various Synopsys SoCs.
|
||||
This is a single-register MMIO GPIO driver for complex cases
|
||||
where only several fields in register belong to GPIO lines and
|
||||
each GPIO line owns a field with different length and on/off value.
|
||||
|
||||
config GPIO_SPEAR_SPICS
|
||||
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
|
||||
depends on PLAT_SPEAR
|
||||
|
@ -469,6 +488,7 @@ config GPIO_SYSCON
|
|||
|
||||
config GPIO_TB10X
|
||||
bool
|
||||
select GPIO_GENERIC
|
||||
select GENERIC_IRQ_CHIP
|
||||
select OF_GPIO
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
|
||||
|
||||
obj-$(CONFIG_GPIOLIB) += devres.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib-devprop.o
|
||||
obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
|
||||
|
@ -110,6 +110,7 @@ obj-$(CONFIG_GPIO_REG) += gpio-reg.o
|
|||
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
|
||||
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
|
||||
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
|
||||
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
|
||||
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
|
||||
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
|
||||
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
|
||||
|
@ -124,6 +125,7 @@ obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
|
|||
obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o
|
||||
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
|
||||
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
|
||||
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
|
||||
obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o
|
||||
obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o
|
||||
obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o
|
||||
|
|
|
@ -172,7 +172,7 @@ static struct platform_driver adp5520_gpio_driver = {
|
|||
|
||||
module_platform_driver(adp5520_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("GPIO ADP5520 Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:adp5520-gpio");
|
||||
|
|
|
@ -494,6 +494,6 @@ static struct i2c_driver adp5588_gpio_driver = {
|
|||
|
||||
module_i2c_driver(adp5588_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -373,6 +373,7 @@ static void bcm_kona_gpio_irq_mask(struct irq_data *d)
|
|||
val = readl(reg_base + GPIO_INT_MASK(bank_id));
|
||||
val |= BIT(bit);
|
||||
writel(val, reg_base + GPIO_INT_MASK(bank_id));
|
||||
gpiochip_disable_irq(&kona_gpio->gpio_chip, gpio);
|
||||
|
||||
raw_spin_unlock_irqrestore(&kona_gpio->lock, flags);
|
||||
}
|
||||
|
@ -394,6 +395,7 @@ static void bcm_kona_gpio_irq_unmask(struct irq_data *d)
|
|||
val = readl(reg_base + GPIO_INT_MSKCLR(bank_id));
|
||||
val |= BIT(bit);
|
||||
writel(val, reg_base + GPIO_INT_MSKCLR(bank_id));
|
||||
gpiochip_enable_irq(&kona_gpio->gpio_chip, gpio);
|
||||
|
||||
raw_spin_unlock_irqrestore(&kona_gpio->lock, flags);
|
||||
}
|
||||
|
@ -485,23 +487,15 @@ static void bcm_kona_gpio_irq_handler(struct irq_desc *desc)
|
|||
static int bcm_kona_gpio_irq_reqres(struct irq_data *d)
|
||||
{
|
||||
struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d);
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_lock_as_irq(&kona_gpio->gpio_chip, d->hwirq);
|
||||
if (ret) {
|
||||
dev_err(kona_gpio->gpio_chip.parent,
|
||||
"unable to lock HW IRQ %lu for IRQ\n",
|
||||
d->hwirq);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
return gpiochip_reqres_irq(&kona_gpio->gpio_chip, d->hwirq);
|
||||
}
|
||||
|
||||
static void bcm_kona_gpio_irq_relres(struct irq_data *d)
|
||||
{
|
||||
struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d);
|
||||
|
||||
gpiochip_unlock_as_irq(&kona_gpio->gpio_chip, d->hwirq);
|
||||
gpiochip_relres_irq(&kona_gpio->gpio_chip, d->hwirq);
|
||||
}
|
||||
|
||||
static struct irq_chip bcm_gpio_irq_chip = {
|
||||
|
|
|
@ -664,6 +664,18 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
|
|||
struct brcmstb_gpio_bank *bank;
|
||||
struct gpio_chip *gc;
|
||||
|
||||
/*
|
||||
* If bank_width is 0, then there is an empty bank in the
|
||||
* register block. Special handling for this case.
|
||||
*/
|
||||
if (bank_width == 0) {
|
||||
dev_dbg(dev, "Width 0 found: Empty bank @ %d\n",
|
||||
num_banks);
|
||||
num_banks++;
|
||||
gpio_base += MAX_GPIO_PER_BANK;
|
||||
continue;
|
||||
}
|
||||
|
||||
bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
|
||||
if (!bank) {
|
||||
err = -ENOMEM;
|
||||
|
@ -740,9 +752,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
dev_info(dev, "Registered %d banks (GPIO(s): %d-%d)\n",
|
||||
num_banks, priv->gpio_base, gpio_base - 1);
|
||||
|
||||
if (priv->parent_wake_irq && need_wakeup_event)
|
||||
pm_wakeup_event(dev, 0);
|
||||
|
||||
|
|
191
drivers/gpio/gpio-creg-snps.c
Normal file
191
drivers/gpio/gpio-creg-snps.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Synopsys CREG (Control REGisters) GPIO driver
|
||||
//
|
||||
// Copyright (C) 2018 Synopsys
|
||||
// Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#define MAX_GPIO 32
|
||||
|
||||
struct creg_layout {
|
||||
u8 ngpio;
|
||||
u8 shift[MAX_GPIO];
|
||||
u8 on[MAX_GPIO];
|
||||
u8 off[MAX_GPIO];
|
||||
u8 bit_per_gpio[MAX_GPIO];
|
||||
};
|
||||
|
||||
struct creg_gpio {
|
||||
struct gpio_chip gc;
|
||||
void __iomem *regs;
|
||||
spinlock_t lock;
|
||||
const struct creg_layout *layout;
|
||||
};
|
||||
|
||||
static void creg_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
|
||||
{
|
||||
struct creg_gpio *hcg = gpiochip_get_data(gc);
|
||||
const struct creg_layout *layout = hcg->layout;
|
||||
u32 reg, reg_shift, value;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
value = val ? hcg->layout->on[offset] : hcg->layout->off[offset];
|
||||
|
||||
reg_shift = layout->shift[offset];
|
||||
for (i = 0; i < offset; i++)
|
||||
reg_shift += layout->bit_per_gpio[i] + layout->shift[i];
|
||||
|
||||
spin_lock_irqsave(&hcg->lock, flags);
|
||||
reg = readl(hcg->regs);
|
||||
reg &= ~(GENMASK(layout->bit_per_gpio[i] - 1, 0) << reg_shift);
|
||||
reg |= (value << reg_shift);
|
||||
writel(reg, hcg->regs);
|
||||
spin_unlock_irqrestore(&hcg->lock, flags);
|
||||
}
|
||||
|
||||
static int creg_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)
|
||||
{
|
||||
creg_gpio_set(gc, offset, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg,
|
||||
int i)
|
||||
{
|
||||
const struct creg_layout *layout = hcg->layout;
|
||||
|
||||
if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check that on valiue fits it's placeholder */
|
||||
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i])
|
||||
return -EINVAL;
|
||||
|
||||
/* Check that off valiue fits it's placeholder */
|
||||
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i])
|
||||
return -EINVAL;
|
||||
|
||||
if (layout->on[i] == layout->off[i])
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int creg_gpio_validate(struct device *dev, struct creg_gpio *hcg,
|
||||
u32 ngpios)
|
||||
{
|
||||
u32 reg_len = 0;
|
||||
int i;
|
||||
|
||||
if (hcg->layout->ngpio < 1 || hcg->layout->ngpio > MAX_GPIO)
|
||||
return -EINVAL;
|
||||
|
||||
if (ngpios < 1 || ngpios > hcg->layout->ngpio) {
|
||||
dev_err(dev, "ngpios must be in [1:%u]\n", hcg->layout->ngpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < hcg->layout->ngpio; i++) {
|
||||
if (creg_gpio_validate_pg(dev, hcg, i))
|
||||
return -EINVAL;
|
||||
|
||||
reg_len += hcg->layout->shift[i] + hcg->layout->bit_per_gpio[i];
|
||||
}
|
||||
|
||||
/* Check that we fit in 32 bit register */
|
||||
if (reg_len > 32)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct creg_layout hsdk_cs_ctl = {
|
||||
.ngpio = 10,
|
||||
.shift = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
.off = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
|
||||
.on = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
|
||||
.bit_per_gpio = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }
|
||||
};
|
||||
|
||||
static const struct creg_layout axs10x_flsh_cs_ctl = {
|
||||
.ngpio = 1,
|
||||
.shift = { 0 },
|
||||
.off = { 1 },
|
||||
.on = { 3 },
|
||||
.bit_per_gpio = { 2 }
|
||||
};
|
||||
|
||||
static const struct of_device_id creg_gpio_ids[] = {
|
||||
{
|
||||
.compatible = "snps,creg-gpio-axs10x",
|
||||
.data = &axs10x_flsh_cs_ctl
|
||||
}, {
|
||||
.compatible = "snps,creg-gpio-hsdk",
|
||||
.data = &hsdk_cs_ctl
|
||||
}, { /* sentinel */ }
|
||||
};
|
||||
|
||||
static int creg_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct creg_gpio *hcg;
|
||||
struct resource *mem;
|
||||
u32 ngpios;
|
||||
int ret;
|
||||
|
||||
hcg = devm_kzalloc(dev, sizeof(struct creg_gpio), GFP_KERNEL);
|
||||
if (!hcg)
|
||||
return -ENOMEM;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hcg->regs = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(hcg->regs))
|
||||
return PTR_ERR(hcg->regs);
|
||||
|
||||
match = of_match_node(creg_gpio_ids, pdev->dev.of_node);
|
||||
hcg->layout = match->data;
|
||||
if (!hcg->layout)
|
||||
return -EINVAL;
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "ngpios", &ngpios);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = creg_gpio_validate(dev, hcg, ngpios);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_init(&hcg->lock);
|
||||
|
||||
hcg->gc.label = dev_name(dev);
|
||||
hcg->gc.base = -1;
|
||||
hcg->gc.ngpio = ngpios;
|
||||
hcg->gc.set = creg_gpio_set;
|
||||
hcg->gc.direction_output = creg_gpio_dir_out;
|
||||
hcg->gc.of_node = dev->of_node;
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(dev, "GPIO controller with %d gpios probed\n", ngpios);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver creg_gpio_snps_driver = {
|
||||
.driver = {
|
||||
.name = "snps-creg-gpio",
|
||||
.of_match_table = creg_gpio_ids,
|
||||
},
|
||||
.probe = creg_gpio_probe,
|
||||
};
|
||||
builtin_platform_driver(creg_gpio_snps_driver);
|
|
@ -9,6 +9,7 @@
|
|||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -24,6 +25,12 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/gpio-davinci.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm-generic/gpio.h>
|
||||
|
||||
#define MAX_REGS_BANKS 5
|
||||
#define MAX_INT_PER_BANK 32
|
||||
|
||||
struct davinci_gpio_regs {
|
||||
u32 dir;
|
||||
|
@ -41,11 +48,31 @@ struct davinci_gpio_regs {
|
|||
typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq);
|
||||
|
||||
#define BINTEN 0x8 /* GPIO Interrupt Per-Bank Enable Register */
|
||||
#define MAX_LABEL_SIZE 20
|
||||
|
||||
static void __iomem *gpio_base;
|
||||
static unsigned int offset_array[5] = {0x10, 0x38, 0x60, 0x88, 0xb0};
|
||||
|
||||
struct davinci_gpio_irq_data {
|
||||
void __iomem *regs;
|
||||
struct davinci_gpio_controller *chip;
|
||||
int bank_num;
|
||||
};
|
||||
|
||||
struct davinci_gpio_controller {
|
||||
struct gpio_chip chip;
|
||||
struct irq_domain *irq_domain;
|
||||
/* Serialize access to GPIO registers */
|
||||
spinlock_t lock;
|
||||
void __iomem *regs[MAX_REGS_BANKS];
|
||||
int gpio_unbanked;
|
||||
int irqs[MAX_INT_PER_BANK];
|
||||
};
|
||||
|
||||
static inline u32 __gpio_mask(unsigned gpio)
|
||||
{
|
||||
return 1 << (gpio % 32);
|
||||
}
|
||||
|
||||
static inline struct davinci_gpio_regs __iomem *irq2regs(struct irq_data *d)
|
||||
{
|
||||
struct davinci_gpio_regs __iomem *g;
|
||||
|
@ -166,14 +193,12 @@ of_err:
|
|||
|
||||
static int davinci_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
static int ctrl_num, bank_base;
|
||||
int gpio, bank, i, ret = 0;
|
||||
int bank, i, ret = 0;
|
||||
unsigned int ngpio, nbank, nirq;
|
||||
struct davinci_gpio_controller *chips;
|
||||
struct davinci_gpio_platform_data *pdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
char label[MAX_LABEL_SIZE];
|
||||
|
||||
pdata = davinci_gpio_get_pdata(pdev);
|
||||
if (!pdata) {
|
||||
|
@ -207,10 +232,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
|
|||
else
|
||||
nirq = DIV_ROUND_UP(ngpio, 16);
|
||||
|
||||
nbank = DIV_ROUND_UP(ngpio, 32);
|
||||
chips = devm_kcalloc(dev,
|
||||
nbank, sizeof(struct davinci_gpio_controller),
|
||||
GFP_KERNEL);
|
||||
chips = devm_kzalloc(dev, sizeof(*chips), GFP_KERNEL);
|
||||
if (!chips)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -228,10 +250,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
snprintf(label, MAX_LABEL_SIZE, "davinci_gpio.%d", ctrl_num++);
|
||||
chips->chip.label = devm_kstrdup(dev, label, GFP_KERNEL);
|
||||
if (!chips->chip.label)
|
||||
return -ENOMEM;
|
||||
chips->chip.label = dev_name(dev);
|
||||
|
||||
chips->chip.direction_input = davinci_direction_in;
|
||||
chips->chip.get = davinci_gpio_get;
|
||||
|
@ -239,7 +258,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
|
|||
chips->chip.set = davinci_gpio_set;
|
||||
|
||||
chips->chip.ngpio = ngpio;
|
||||
chips->chip.base = bank_base;
|
||||
chips->chip.base = -1;
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
chips->chip.of_gpio_n_cells = 2;
|
||||
|
@ -252,28 +271,21 @@ static int davinci_gpio_probe(struct platform_device *pdev)
|
|||
}
|
||||
#endif
|
||||
spin_lock_init(&chips->lock);
|
||||
bank_base += ngpio;
|
||||
|
||||
for (gpio = 0, bank = 0; gpio < ngpio; gpio += 32, bank++)
|
||||
nbank = DIV_ROUND_UP(ngpio, 32);
|
||||
for (bank = 0; bank < nbank; bank++)
|
||||
chips->regs[bank] = gpio_base + offset_array[bank];
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, &chips->chip, chips);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, chips);
|
||||
ret = davinci_gpio_irq_setup(pdev);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
/* Revert the static variable increments */
|
||||
ctrl_num--;
|
||||
bank_base -= ngpio;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generic EP93xx GPIO handling
|
||||
*
|
||||
|
@ -6,10 +7,6 @@
|
|||
*
|
||||
* Based on code originally from:
|
||||
* linux/arch/arm/mach-ep93xx/core.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
|
@ -19,16 +16,26 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
/* FIXME: this is here for gpio_to_irq() - get rid of this! */
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/gpio-ep93xx.h>
|
||||
#define EP93XX_GPIO_F_INT_STATUS 0x5c
|
||||
#define EP93XX_GPIO_A_INT_STATUS 0xa0
|
||||
#define EP93XX_GPIO_B_INT_STATUS 0xbc
|
||||
|
||||
#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0))
|
||||
/* Maximum value for gpio line identifiers */
|
||||
#define EP93XX_GPIO_LINE_MAX 63
|
||||
|
||||
/* Maximum value for irq capable line identifiers */
|
||||
#define EP93XX_GPIO_LINE_MAX_IRQ 23
|
||||
|
||||
/*
|
||||
* Static mapping of GPIO bank F IRQS:
|
||||
* F0..F7 (16..24) to irq 80..87.
|
||||
*/
|
||||
#define EP93XX_GPIO_F_IRQ_BASE 80
|
||||
|
||||
struct ep93xx_gpio {
|
||||
void __iomem *mmio_base;
|
||||
void __iomem *base;
|
||||
struct gpio_chip gc[8];
|
||||
};
|
||||
|
||||
|
@ -48,27 +55,45 @@ static const u8 eoi_register_offset[3] = { 0x98, 0xb4, 0x54 };
|
|||
static const u8 int_en_register_offset[3] = { 0x9c, 0xb8, 0x58 };
|
||||
static const u8 int_debounce_register_offset[3] = { 0xa8, 0xc4, 0x64 };
|
||||
|
||||
static void ep93xx_gpio_update_int_params(unsigned port)
|
||||
static void ep93xx_gpio_update_int_params(struct ep93xx_gpio *epg, unsigned port)
|
||||
{
|
||||
BUG_ON(port > 2);
|
||||
|
||||
writeb_relaxed(0, EP93XX_GPIO_REG(int_en_register_offset[port]));
|
||||
writeb_relaxed(0, epg->base + int_en_register_offset[port]);
|
||||
|
||||
writeb_relaxed(gpio_int_type2[port],
|
||||
EP93XX_GPIO_REG(int_type2_register_offset[port]));
|
||||
epg->base + int_type2_register_offset[port]);
|
||||
|
||||
writeb_relaxed(gpio_int_type1[port],
|
||||
EP93XX_GPIO_REG(int_type1_register_offset[port]));
|
||||
epg->base + int_type1_register_offset[port]);
|
||||
|
||||
writeb(gpio_int_unmasked[port] & gpio_int_enabled[port],
|
||||
EP93XX_GPIO_REG(int_en_register_offset[port]));
|
||||
epg->base + int_en_register_offset[port]);
|
||||
}
|
||||
|
||||
static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable)
|
||||
static int ep93xx_gpio_port(struct gpio_chip *gc)
|
||||
{
|
||||
int line = irq_to_gpio(irq);
|
||||
int port = line >> 3;
|
||||
int port_mask = 1 << (line & 7);
|
||||
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
|
||||
int port = 0;
|
||||
|
||||
while (port < ARRAY_SIZE(epg->gc) && gc != &epg->gc[port])
|
||||
port++;
|
||||
|
||||
/* This should not happen but is there as a last safeguard */
|
||||
if (port == ARRAY_SIZE(epg->gc)) {
|
||||
pr_crit("can't find the GPIO port\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
static void ep93xx_gpio_int_debounce(struct gpio_chip *gc,
|
||||
unsigned int offset, bool enable)
|
||||
{
|
||||
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
|
||||
int port = ep93xx_gpio_port(gc);
|
||||
int port_mask = BIT(offset);
|
||||
|
||||
if (enable)
|
||||
gpio_int_debounce[port] |= port_mask;
|
||||
|
@ -76,29 +101,36 @@ static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable)
|
|||
gpio_int_debounce[port] &= ~port_mask;
|
||||
|
||||
writeb(gpio_int_debounce[port],
|
||||
EP93XX_GPIO_REG(int_debounce_register_offset[port]));
|
||||
epg->base + int_debounce_register_offset[port]);
|
||||
}
|
||||
|
||||
static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
unsigned char status;
|
||||
int i;
|
||||
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
|
||||
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
|
||||
struct irq_chip *irqchip = irq_desc_get_chip(desc);
|
||||
unsigned long stat;
|
||||
int offset;
|
||||
|
||||
status = readb(EP93XX_GPIO_A_INT_STATUS);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (status & (1 << i)) {
|
||||
int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_A(0)) + i;
|
||||
generic_handle_irq(gpio_irq);
|
||||
}
|
||||
}
|
||||
chained_irq_enter(irqchip, desc);
|
||||
|
||||
status = readb(EP93XX_GPIO_B_INT_STATUS);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (status & (1 << i)) {
|
||||
int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_B(0)) + i;
|
||||
generic_handle_irq(gpio_irq);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Dispatch the IRQs to the irqdomain of each A and B
|
||||
* gpiochip irqdomains depending on what has fired.
|
||||
* The tricky part is that the IRQ line is shared
|
||||
* between bank A and B and each has their own gpiochip.
|
||||
*/
|
||||
stat = readb(epg->base + EP93XX_GPIO_A_INT_STATUS);
|
||||
for_each_set_bit(offset, &stat, 8)
|
||||
generic_handle_irq(irq_find_mapping(epg->gc[0].irq.domain,
|
||||
offset));
|
||||
|
||||
stat = readb(epg->base + EP93XX_GPIO_B_INT_STATUS);
|
||||
for_each_set_bit(offset, &stat, 8)
|
||||
generic_handle_irq(irq_find_mapping(epg->gc[1].irq.domain,
|
||||
offset));
|
||||
|
||||
chained_irq_exit(irqchip, desc);
|
||||
}
|
||||
|
||||
static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc)
|
||||
|
@ -106,60 +138,67 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc)
|
|||
/*
|
||||
* map discontiguous hw irq range to continuous sw irq range:
|
||||
*
|
||||
* IRQ_EP93XX_GPIO{0..7}MUX -> gpio_to_irq(EP93XX_GPIO_LINE_F({0..7})
|
||||
* IRQ_EP93XX_GPIO{0..7}MUX -> EP93XX_GPIO_LINE_F{0..7}
|
||||
*/
|
||||
struct irq_chip *irqchip = irq_desc_get_chip(desc);
|
||||
unsigned int irq = irq_desc_get_irq(desc);
|
||||
int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */
|
||||
int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_F(0)) + port_f_idx;
|
||||
int gpio_irq = EP93XX_GPIO_F_IRQ_BASE + port_f_idx;
|
||||
|
||||
chained_irq_enter(irqchip, desc);
|
||||
generic_handle_irq(gpio_irq);
|
||||
chained_irq_exit(irqchip, desc);
|
||||
}
|
||||
|
||||
static void ep93xx_gpio_irq_ack(struct irq_data *d)
|
||||
{
|
||||
int line = irq_to_gpio(d->irq);
|
||||
int port = line >> 3;
|
||||
int port_mask = 1 << (line & 7);
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
|
||||
int port = ep93xx_gpio_port(gc);
|
||||
int port_mask = BIT(d->irq & 7);
|
||||
|
||||
if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
|
||||
gpio_int_type2[port] ^= port_mask; /* switch edge direction */
|
||||
ep93xx_gpio_update_int_params(port);
|
||||
ep93xx_gpio_update_int_params(epg, port);
|
||||
}
|
||||
|
||||
writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
|
||||
writeb(port_mask, epg->base + eoi_register_offset[port]);
|
||||
}
|
||||
|
||||
static void ep93xx_gpio_irq_mask_ack(struct irq_data *d)
|
||||
{
|
||||
int line = irq_to_gpio(d->irq);
|
||||
int port = line >> 3;
|
||||
int port_mask = 1 << (line & 7);
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
|
||||
int port = ep93xx_gpio_port(gc);
|
||||
int port_mask = BIT(d->irq & 7);
|
||||
|
||||
if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH)
|
||||
gpio_int_type2[port] ^= port_mask; /* switch edge direction */
|
||||
|
||||
gpio_int_unmasked[port] &= ~port_mask;
|
||||
ep93xx_gpio_update_int_params(port);
|
||||
ep93xx_gpio_update_int_params(epg, port);
|
||||
|
||||
writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
|
||||
writeb(port_mask, epg->base + eoi_register_offset[port]);
|
||||
}
|
||||
|
||||
static void ep93xx_gpio_irq_mask(struct irq_data *d)
|
||||
{
|
||||
int line = irq_to_gpio(d->irq);
|
||||
int port = line >> 3;
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
|
||||
int port = ep93xx_gpio_port(gc);
|
||||
|
||||
gpio_int_unmasked[port] &= ~(1 << (line & 7));
|
||||
ep93xx_gpio_update_int_params(port);
|
||||
gpio_int_unmasked[port] &= ~BIT(d->irq & 7);
|
||||
ep93xx_gpio_update_int_params(epg, port);
|
||||
}
|
||||
|
||||
static void ep93xx_gpio_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
int line = irq_to_gpio(d->irq);
|
||||
int port = line >> 3;
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
|
||||
int port = ep93xx_gpio_port(gc);
|
||||
|
||||
gpio_int_unmasked[port] |= 1 << (line & 7);
|
||||
ep93xx_gpio_update_int_params(port);
|
||||
gpio_int_unmasked[port] |= BIT(d->irq & 7);
|
||||
ep93xx_gpio_update_int_params(epg, port);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -169,12 +208,14 @@ static void ep93xx_gpio_irq_unmask(struct irq_data *d)
|
|||
*/
|
||||
static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
const int gpio = irq_to_gpio(d->irq);
|
||||
const int port = gpio >> 3;
|
||||
const int port_mask = 1 << (gpio & 7);
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
|
||||
int port = ep93xx_gpio_port(gc);
|
||||
int offset = d->irq & 7;
|
||||
int port_mask = BIT(offset);
|
||||
irq_flow_handler_t handler;
|
||||
|
||||
gpio_direction_input(gpio);
|
||||
gc->direction_input(gc, offset);
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
|
@ -200,7 +241,7 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
|
|||
case IRQ_TYPE_EDGE_BOTH:
|
||||
gpio_int_type1[port] |= port_mask;
|
||||
/* set initial polarity based on current input level */
|
||||
if (gpio_get_value(gpio))
|
||||
if (gc->get(gc, offset))
|
||||
gpio_int_type2[port] &= ~port_mask; /* falling */
|
||||
else
|
||||
gpio_int_type2[port] |= port_mask; /* rising */
|
||||
|
@ -214,7 +255,7 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
|
|||
|
||||
gpio_int_enabled[port] |= port_mask;
|
||||
|
||||
ep93xx_gpio_update_int_params(port);
|
||||
ep93xx_gpio_update_int_params(epg, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -228,35 +269,53 @@ static struct irq_chip ep93xx_gpio_irq_chip = {
|
|||
.irq_set_type = ep93xx_gpio_irq_type,
|
||||
};
|
||||
|
||||
static void ep93xx_gpio_init_irq(void)
|
||||
static int ep93xx_gpio_init_irq(struct platform_device *pdev,
|
||||
struct ep93xx_gpio *epg)
|
||||
{
|
||||
int ab_parent_irq = platform_get_irq(pdev, 0);
|
||||
struct device *dev = &pdev->dev;
|
||||
int gpio_irq;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (gpio_irq = gpio_to_irq(0);
|
||||
gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) {
|
||||
/* The A bank */
|
||||
ret = gpiochip_irqchip_add(&epg->gc[0], &ep93xx_gpio_irq_chip,
|
||||
64, handle_level_irq,
|
||||
IRQ_TYPE_NONE);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not add irqchip 0\n");
|
||||
return ret;
|
||||
}
|
||||
gpiochip_set_chained_irqchip(&epg->gc[0], &ep93xx_gpio_irq_chip,
|
||||
ab_parent_irq,
|
||||
ep93xx_gpio_ab_irq_handler);
|
||||
|
||||
/* The B bank */
|
||||
ret = gpiochip_irqchip_add(&epg->gc[1], &ep93xx_gpio_irq_chip,
|
||||
72, handle_level_irq,
|
||||
IRQ_TYPE_NONE);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not add irqchip 1\n");
|
||||
return ret;
|
||||
}
|
||||
gpiochip_set_chained_irqchip(&epg->gc[1], &ep93xx_gpio_irq_chip,
|
||||
ab_parent_irq,
|
||||
ep93xx_gpio_ab_irq_handler);
|
||||
|
||||
/* The F bank */
|
||||
for (i = 0; i < 8; i++) {
|
||||
gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i;
|
||||
irq_set_chip_data(gpio_irq, &epg->gc[5]);
|
||||
irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip,
|
||||
handle_level_irq);
|
||||
irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST);
|
||||
}
|
||||
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO_AB,
|
||||
ep93xx_gpio_ab_irq_handler);
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO0MUX,
|
||||
ep93xx_gpio_f_irq_handler);
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO1MUX,
|
||||
ep93xx_gpio_f_irq_handler);
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO2MUX,
|
||||
ep93xx_gpio_f_irq_handler);
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO3MUX,
|
||||
ep93xx_gpio_f_irq_handler);
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO4MUX,
|
||||
ep93xx_gpio_f_irq_handler);
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO5MUX,
|
||||
ep93xx_gpio_f_irq_handler);
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO6MUX,
|
||||
ep93xx_gpio_f_irq_handler);
|
||||
irq_set_chained_handler(IRQ_EP93XX_GPIO7MUX,
|
||||
ep93xx_gpio_f_irq_handler);
|
||||
for (i = 1; i <= 8; i++)
|
||||
irq_set_chained_handler_and_data(platform_get_irq(pdev, i),
|
||||
ep93xx_gpio_f_irq_handler,
|
||||
&epg->gc[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -268,68 +327,54 @@ struct ep93xx_gpio_bank {
|
|||
int data;
|
||||
int dir;
|
||||
int base;
|
||||
bool has_debounce;
|
||||
bool has_irq;
|
||||
};
|
||||
|
||||
#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _debounce) \
|
||||
#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _has_irq) \
|
||||
{ \
|
||||
.label = _label, \
|
||||
.data = _data, \
|
||||
.dir = _dir, \
|
||||
.base = _base, \
|
||||
.has_debounce = _debounce, \
|
||||
.has_irq = _has_irq, \
|
||||
}
|
||||
|
||||
static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = {
|
||||
EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true),
|
||||
EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true),
|
||||
EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true), /* Bank A has 8 IRQs */
|
||||
EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true), /* Bank B has 8 IRQs */
|
||||
EP93XX_GPIO_BANK("C", 0x08, 0x18, 40, false),
|
||||
EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24, false),
|
||||
EP93XX_GPIO_BANK("E", 0x20, 0x24, 32, false),
|
||||
EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true),
|
||||
EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true), /* Bank F has 8 IRQs */
|
||||
EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false),
|
||||
EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false),
|
||||
};
|
||||
|
||||
static int ep93xx_gpio_set_config(struct gpio_chip *chip, unsigned offset,
|
||||
static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset,
|
||||
unsigned long config)
|
||||
{
|
||||
int gpio = chip->base + offset;
|
||||
int irq = gpio_to_irq(gpio);
|
||||
u32 debounce;
|
||||
|
||||
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (irq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
debounce = pinconf_to_config_argument(config);
|
||||
ep93xx_gpio_int_debounce(irq, debounce ? true : false);
|
||||
ep93xx_gpio_int_debounce(gc, offset, debounce ? true : false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map GPIO A0..A7 (0..7) to irq 64..71,
|
||||
* B0..B7 (7..15) to irq 72..79, and
|
||||
* F0..F7 (16..24) to irq 80..87.
|
||||
*/
|
||||
static int ep93xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
static int ep93xx_gpio_f_to_irq(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
int gpio = chip->base + offset;
|
||||
|
||||
if (gpio > EP93XX_GPIO_LINE_MAX_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
return 64 + gpio;
|
||||
return EP93XX_GPIO_F_IRQ_BASE + offset;
|
||||
}
|
||||
|
||||
static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev,
|
||||
void __iomem *mmio_base, struct ep93xx_gpio_bank *bank)
|
||||
struct ep93xx_gpio *epg,
|
||||
struct ep93xx_gpio_bank *bank)
|
||||
{
|
||||
void __iomem *data = mmio_base + bank->data;
|
||||
void __iomem *dir = mmio_base + bank->dir;
|
||||
void __iomem *data = epg->base + bank->data;
|
||||
void __iomem *dir = epg->base + bank->dir;
|
||||
int err;
|
||||
|
||||
err = bgpio_init(gc, dev, 1, data, NULL, NULL, dir, NULL, 0);
|
||||
|
@ -339,41 +384,41 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev,
|
|||
gc->label = bank->label;
|
||||
gc->base = bank->base;
|
||||
|
||||
if (bank->has_debounce) {
|
||||
if (bank->has_irq)
|
||||
gc->set_config = ep93xx_gpio_set_config;
|
||||
gc->to_irq = ep93xx_gpio_to_irq;
|
||||
}
|
||||
|
||||
return devm_gpiochip_add_data(dev, gc, NULL);
|
||||
return devm_gpiochip_add_data(dev, gc, epg);
|
||||
}
|
||||
|
||||
static int ep93xx_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ep93xx_gpio *ep93xx_gpio;
|
||||
struct ep93xx_gpio *epg;
|
||||
struct resource *res;
|
||||
int i;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
ep93xx_gpio = devm_kzalloc(dev, sizeof(struct ep93xx_gpio), GFP_KERNEL);
|
||||
if (!ep93xx_gpio)
|
||||
epg = devm_kzalloc(dev, sizeof(*epg), GFP_KERNEL);
|
||||
if (!epg)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ep93xx_gpio->mmio_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(ep93xx_gpio->mmio_base))
|
||||
return PTR_ERR(ep93xx_gpio->mmio_base);
|
||||
epg->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(epg->base))
|
||||
return PTR_ERR(epg->base);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) {
|
||||
struct gpio_chip *gc = &ep93xx_gpio->gc[i];
|
||||
struct gpio_chip *gc = &epg->gc[i];
|
||||
struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i];
|
||||
|
||||
if (ep93xx_gpio_add_bank(gc, &pdev->dev,
|
||||
ep93xx_gpio->mmio_base, bank))
|
||||
if (ep93xx_gpio_add_bank(gc, &pdev->dev, epg, bank))
|
||||
dev_warn(&pdev->dev, "Unable to add gpio bank %s\n",
|
||||
bank->label);
|
||||
bank->label);
|
||||
/* Only bank F has especially funky IRQ handling */
|
||||
if (i == 5)
|
||||
gc->to_irq = ep93xx_gpio_f_to_irq;
|
||||
}
|
||||
|
||||
ep93xx_gpio_init_irq();
|
||||
ep93xx_gpio_init_irq(pdev, epg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
/* GPIO registers definition */
|
||||
#define GPIO_DATA_OUT 0x00
|
||||
|
@ -40,11 +41,14 @@
|
|||
* struct ftgpio_gpio - Gemini GPIO state container
|
||||
* @dev: containing device for this instance
|
||||
* @gc: gpiochip for this instance
|
||||
* @base: remapped I/O-memory base
|
||||
* @clk: silicon clock
|
||||
*/
|
||||
struct ftgpio_gpio {
|
||||
struct device *dev;
|
||||
struct gpio_chip gc;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void ftgpio_gpio_ack_irq(struct irq_data *d)
|
||||
|
@ -157,6 +161,73 @@ static void ftgpio_gpio_irq_handler(struct irq_desc *desc)
|
|||
chained_irq_exit(irqchip, desc);
|
||||
}
|
||||
|
||||
static int ftgpio_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
|
||||
unsigned long config)
|
||||
{
|
||||
enum pin_config_param param = pinconf_to_config_param(config);
|
||||
u32 arg = pinconf_to_config_argument(config);
|
||||
struct ftgpio_gpio *g = gpiochip_get_data(gc);
|
||||
unsigned long pclk_freq;
|
||||
u32 deb_div;
|
||||
u32 val;
|
||||
|
||||
if (param != PIN_CONFIG_INPUT_DEBOUNCE)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/*
|
||||
* Debounce only works if interrupts are enabled. The manual
|
||||
* states that if PCLK is 66 MHz, and this is set to 0x7D0, then
|
||||
* PCLK is divided down to 33 kHz for the debounce timer. 0x7D0 is
|
||||
* 2000 decimal, so what they mean is simply that the PCLK is
|
||||
* divided by this value.
|
||||
*
|
||||
* As we get a debounce setting in microseconds, we calculate the
|
||||
* desired period time and see if we can get a suitable debounce
|
||||
* time.
|
||||
*/
|
||||
pclk_freq = clk_get_rate(g->clk);
|
||||
deb_div = DIV_ROUND_CLOSEST(pclk_freq, arg);
|
||||
|
||||
/* This register is only 24 bits wide */
|
||||
if (deb_div > (1 << 24))
|
||||
return -ENOTSUPP;
|
||||
|
||||
dev_dbg(g->dev, "prescale divisor: %08x, resulting frequency %lu Hz\n",
|
||||
deb_div, (pclk_freq/deb_div));
|
||||
|
||||
val = readl(g->base + GPIO_DEBOUNCE_PRESCALE);
|
||||
if (val == deb_div) {
|
||||
/*
|
||||
* The debounce timer happens to already be set to the
|
||||
* desireable value, what a coincidence! We can just enable
|
||||
* debounce on this GPIO line and return. This happens more
|
||||
* often than you think, for example when all GPIO keys
|
||||
* on a system are requesting the same debounce interval.
|
||||
*/
|
||||
val = readl(g->base + GPIO_DEBOUNCE_EN);
|
||||
val |= BIT(offset);
|
||||
writel(val, g->base + GPIO_DEBOUNCE_EN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = readl(g->base + GPIO_DEBOUNCE_EN);
|
||||
if (val) {
|
||||
/*
|
||||
* Oh no! Someone is already using the debounce with
|
||||
* another setting than what we need. Bummer.
|
||||
*/
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/* First come, first serve */
|
||||
writel(deb_div, g->base + GPIO_DEBOUNCE_PRESCALE);
|
||||
/* Enable debounce */
|
||||
val |= BIT(offset);
|
||||
writel(val, g->base + GPIO_DEBOUNCE_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftgpio_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -180,6 +251,19 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
|
|||
if (irq <= 0)
|
||||
return irq ? irq : -EINVAL;
|
||||
|
||||
g->clk = devm_clk_get(dev, NULL);
|
||||
if (!IS_ERR(g->clk)) {
|
||||
ret = clk_prepare_enable(g->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (PTR_ERR(g->clk) == -EPROBE_DEFER) {
|
||||
/*
|
||||
* Percolate deferrals, for anything else,
|
||||
* just live without the clocking.
|
||||
*/
|
||||
return PTR_ERR(g->clk);
|
||||
}
|
||||
|
||||
ret = bgpio_init(&g->gc, dev, 4,
|
||||
g->base + GPIO_DATA_IN,
|
||||
g->base + GPIO_DATA_SET,
|
||||
|
@ -189,7 +273,7 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
|
|||
0);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to init generic GPIO\n");
|
||||
return ret;
|
||||
goto dis_clk;
|
||||
}
|
||||
g->gc.label = "FTGPIO010";
|
||||
g->gc.base = -1;
|
||||
|
@ -197,28 +281,50 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
|
|||
g->gc.owner = THIS_MODULE;
|
||||
/* ngpio is set by bgpio_init() */
|
||||
|
||||
/* We need a silicon clock to do debounce */
|
||||
if (!IS_ERR(g->clk))
|
||||
g->gc.set_config = ftgpio_gpio_set_config;
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, &g->gc, g);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto dis_clk;
|
||||
|
||||
/* Disable, unmask and clear all interrupts */
|
||||
writel(0x0, g->base + GPIO_INT_EN);
|
||||
writel(0x0, g->base + GPIO_INT_MASK);
|
||||
writel(~0x0, g->base + GPIO_INT_CLR);
|
||||
|
||||
/* Clear any use of debounce */
|
||||
writel(0x0, g->base + GPIO_DEBOUNCE_EN);
|
||||
|
||||
ret = gpiochip_irqchip_add(&g->gc, &ftgpio_gpio_irqchip,
|
||||
0, handle_bad_irq,
|
||||
IRQ_TYPE_NONE);
|
||||
if (ret) {
|
||||
dev_info(dev, "could not add irqchip\n");
|
||||
return ret;
|
||||
goto dis_clk;
|
||||
}
|
||||
gpiochip_set_chained_irqchip(&g->gc, &ftgpio_gpio_irqchip,
|
||||
irq, ftgpio_gpio_irq_handler);
|
||||
|
||||
platform_set_drvdata(pdev, g);
|
||||
dev_info(dev, "FTGPIO010 @%p registered\n", g->base);
|
||||
|
||||
return 0;
|
||||
|
||||
dis_clk:
|
||||
if (!IS_ERR(g->clk))
|
||||
clk_disable_unprepare(g->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ftgpio_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ftgpio_gpio *g = platform_get_drvdata(pdev);
|
||||
|
||||
if (!IS_ERR(g->clk))
|
||||
clk_disable_unprepare(g->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ftgpio_gpio_of_match[] = {
|
||||
|
@ -239,6 +345,7 @@ static struct platform_driver ftgpio_gpio_driver = {
|
|||
.name = "ftgpio010-gpio",
|
||||
.of_match_table = of_match_ptr(ftgpio_gpio_of_match),
|
||||
},
|
||||
.probe = ftgpio_gpio_probe,
|
||||
.probe = ftgpio_gpio_probe,
|
||||
.remove = ftgpio_gpio_remove,
|
||||
};
|
||||
builtin_platform_driver(ftgpio_gpio_driver);
|
||||
|
|
|
@ -189,7 +189,6 @@ static void egpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
|||
unsigned long flag;
|
||||
struct egpio_chip *egpio;
|
||||
struct egpio_info *ei;
|
||||
unsigned bit;
|
||||
int pos;
|
||||
int reg;
|
||||
int shift;
|
||||
|
@ -199,7 +198,6 @@ static void egpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
|||
|
||||
egpio = gpiochip_get_data(chip);
|
||||
ei = dev_get_drvdata(egpio->dev);
|
||||
bit = egpio_bit(ei, offset);
|
||||
pos = egpio_pos(ei, offset);
|
||||
reg = egpio->reg_start + pos;
|
||||
shift = pos << ei->reg_shift;
|
||||
|
@ -334,7 +332,13 @@ static int __init egpio_probe(struct platform_device *pdev)
|
|||
ei->chip[i].is_out = pdata->chip[i].direction;
|
||||
ei->chip[i].dev = &(pdev->dev);
|
||||
chip = &(ei->chip[i].chip);
|
||||
chip->label = "htc-egpio";
|
||||
chip->label = devm_kasprintf(&pdev->dev, GFP_KERNEL,
|
||||
"htc-egpio-%d",
|
||||
i);
|
||||
if (!chip->label) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
chip->parent = &pdev->dev;
|
||||
chip->owner = THIS_MODULE;
|
||||
chip->get = egpio_get;
|
||||
|
|
|
@ -313,18 +313,21 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset,
|
|||
|
||||
static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
|
||||
struct gpio_desc **desc,
|
||||
struct gpio_array *info,
|
||||
int value)
|
||||
{
|
||||
int i, *values;
|
||||
unsigned long *values;
|
||||
|
||||
values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
|
||||
values = bitmap_alloc(ndescs, GFP_KERNEL);
|
||||
if (!values)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ndescs; i++)
|
||||
values[i] = value;
|
||||
if (value)
|
||||
bitmap_fill(values, ndescs);
|
||||
else
|
||||
bitmap_zero(values, ndescs);
|
||||
|
||||
gpiod_set_array_value_cansleep(ndescs, desc, values);
|
||||
gpiod_set_array_value_cansleep(ndescs, desc, info, values);
|
||||
kfree(values);
|
||||
}
|
||||
|
||||
|
@ -397,7 +400,8 @@ static int max3191x_probe(struct spi_device *spi)
|
|||
if (max3191x->modesel_pins)
|
||||
gpiod_set_array_single_value_cansleep(
|
||||
max3191x->modesel_pins->ndescs,
|
||||
max3191x->modesel_pins->desc, max3191x->mode);
|
||||
max3191x->modesel_pins->desc,
|
||||
max3191x->modesel_pins->info, max3191x->mode);
|
||||
|
||||
max3191x->ignore_uv = device_property_read_bool(dev,
|
||||
"maxim,ignore-undervoltage");
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Generic driver for memory-mapped GPIO controllers.
|
||||
*
|
||||
* Copyright 2008 MontaVista Software, Inc.
|
||||
* Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`.......
|
||||
* ...`` ```````..
|
||||
* ..The simplest form of a GPIO controller that the driver supports is``
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/irq_sim.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
|
||||
|
@ -28,6 +29,8 @@
|
|||
* of GPIO lines.
|
||||
*/
|
||||
#define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2)
|
||||
/* Maximum of three properties + the sentinel. */
|
||||
#define GPIO_MOCKUP_MAX_PROP 4
|
||||
|
||||
#define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__)
|
||||
|
||||
|
@ -59,13 +62,6 @@ struct gpio_mockup_dbgfs_private {
|
|||
int offset;
|
||||
};
|
||||
|
||||
struct gpio_mockup_platform_data {
|
||||
int base;
|
||||
int ngpio;
|
||||
int index;
|
||||
bool named_lines;
|
||||
};
|
||||
|
||||
static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES];
|
||||
static int gpio_mockup_num_ranges;
|
||||
module_param_array(gpio_mockup_ranges, int, &gpio_mockup_num_ranges, 0400);
|
||||
|
@ -255,26 +251,37 @@ static int gpio_mockup_name_lines(struct device *dev,
|
|||
|
||||
static int gpio_mockup_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_mockup_platform_data *pdata;
|
||||
struct gpio_mockup_chip *chip;
|
||||
struct gpio_chip *gc;
|
||||
int rv, base, ngpio;
|
||||
struct device *dev;
|
||||
char *name;
|
||||
const char *name;
|
||||
int rv, base;
|
||||
u16 ngpio;
|
||||
|
||||
dev = &pdev->dev;
|
||||
pdata = dev_get_platdata(dev);
|
||||
base = pdata->base;
|
||||
ngpio = pdata->ngpio;
|
||||
|
||||
rv = device_property_read_u32(dev, "gpio-base", &base);
|
||||
if (rv)
|
||||
base = -1;
|
||||
|
||||
rv = device_property_read_u16(dev, "nr-gpios", &ngpio);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv = device_property_read_string(dev, "chip-name", &name);
|
||||
if (rv)
|
||||
name = NULL;
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
name = devm_kasprintf(dev, GFP_KERNEL, "%s-%c",
|
||||
pdev->name, pdata->index);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (!name) {
|
||||
name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"%s-%c", pdev->name, pdev->id + 'A');
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
gc = &chip->gc;
|
||||
gc->base = base;
|
||||
|
@ -295,7 +302,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)
|
|||
if (!chip->lines)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pdata->named_lines) {
|
||||
if (device_property_read_bool(dev, "named-gpio-lines")) {
|
||||
rv = gpio_mockup_name_lines(dev, chip);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
@ -339,9 +346,11 @@ static void gpio_mockup_unregister_pdevs(void)
|
|||
|
||||
static int __init gpio_mockup_init(void)
|
||||
{
|
||||
int i, num_chips, err = 0, index = 'A';
|
||||
struct gpio_mockup_platform_data pdata;
|
||||
struct property_entry properties[GPIO_MOCKUP_MAX_PROP];
|
||||
int i, prop, num_chips, err = 0, base;
|
||||
struct platform_device_info pdevinfo;
|
||||
struct platform_device *pdev;
|
||||
u16 ngpio;
|
||||
|
||||
if ((gpio_mockup_num_ranges < 2) ||
|
||||
(gpio_mockup_num_ranges % 2) ||
|
||||
|
@ -371,17 +380,28 @@ static int __init gpio_mockup_init(void)
|
|||
}
|
||||
|
||||
for (i = 0; i < num_chips; i++) {
|
||||
pdata.index = index++;
|
||||
pdata.base = gpio_mockup_range_base(i);
|
||||
pdata.ngpio = pdata.base < 0
|
||||
? gpio_mockup_range_ngpio(i)
|
||||
: gpio_mockup_range_ngpio(i) - pdata.base;
|
||||
pdata.named_lines = gpio_mockup_named_lines;
|
||||
memset(properties, 0, sizeof(properties));
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
prop = 0;
|
||||
|
||||
pdev = platform_device_register_resndata(NULL,
|
||||
GPIO_MOCKUP_NAME,
|
||||
i, NULL, 0, &pdata,
|
||||
sizeof(pdata));
|
||||
base = gpio_mockup_range_base(i);
|
||||
if (base >= 0)
|
||||
properties[prop++] = PROPERTY_ENTRY_U32("gpio-base",
|
||||
base);
|
||||
|
||||
ngpio = base < 0 ? gpio_mockup_range_ngpio(i)
|
||||
: gpio_mockup_range_ngpio(i) - base;
|
||||
properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);
|
||||
|
||||
if (gpio_mockup_named_lines)
|
||||
properties[prop++] = PROPERTY_ENTRY_BOOL(
|
||||
"named-gpio-lines");
|
||||
|
||||
pdevinfo.name = GPIO_MOCKUP_NAME;
|
||||
pdevinfo.id = i;
|
||||
pdevinfo.properties = properties;
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
gpio_mockup_err("error registering device");
|
||||
platform_driver_unregister(&gpio_mockup_driver);
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
/* FIXME: for gpio_get_value(), replace this by direct register read */
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define MXS_SET 0x4
|
||||
|
@ -86,7 +84,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
|
|||
port->both_edges &= ~pin_mask;
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
val = gpio_get_value(port->gc.base + d->hwirq);
|
||||
val = port->gc.get(&port->gc, d->hwirq);
|
||||
if (val)
|
||||
edge = GPIO_INT_FALL_EDGE;
|
||||
else
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm.h>
|
||||
|
@ -28,10 +29,10 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/platform_data/gpio-omap.h>
|
||||
|
||||
#define OFF_MODE 1
|
||||
#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
|
||||
|
||||
static LIST_HEAD(omap_gpio_list);
|
||||
#define OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER BIT(2)
|
||||
#define OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN BIT(1)
|
||||
|
||||
struct gpio_regs {
|
||||
u32 irqenable1;
|
||||
|
@ -48,6 +49,13 @@ struct gpio_regs {
|
|||
u32 debounce_en;
|
||||
};
|
||||
|
||||
struct gpio_bank;
|
||||
|
||||
struct gpio_omap_funcs {
|
||||
void (*idle_enable_level_quirk)(struct gpio_bank *bank);
|
||||
void (*idle_disable_level_quirk)(struct gpio_bank *bank);
|
||||
};
|
||||
|
||||
struct gpio_bank {
|
||||
struct list_head node;
|
||||
void __iomem *base;
|
||||
|
@ -55,6 +63,7 @@ struct gpio_bank {
|
|||
u32 non_wakeup_gpios;
|
||||
u32 enabled_non_wakeup_gpios;
|
||||
struct gpio_regs context;
|
||||
struct gpio_omap_funcs funcs;
|
||||
u32 saved_datain;
|
||||
u32 level_mask;
|
||||
u32 toggle_mask;
|
||||
|
@ -62,6 +71,8 @@ struct gpio_bank {
|
|||
raw_spinlock_t wa_lock;
|
||||
struct gpio_chip chip;
|
||||
struct clk *dbck;
|
||||
struct notifier_block nb;
|
||||
unsigned int is_suspended:1;
|
||||
u32 mod_usage;
|
||||
u32 irq_usage;
|
||||
u32 dbck_enable_mask;
|
||||
|
@ -73,8 +84,8 @@ struct gpio_bank {
|
|||
int stride;
|
||||
u32 width;
|
||||
int context_loss_count;
|
||||
int power_mode;
|
||||
bool workaround_enabled;
|
||||
u32 quirks;
|
||||
|
||||
void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
|
||||
void (*set_dataout_multiple)(struct gpio_bank *bank,
|
||||
|
@ -368,9 +379,18 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
|
|||
readl_relaxed(bank->base + bank->regs->fallingdetect);
|
||||
|
||||
if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
|
||||
omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit, trigger != 0);
|
||||
bank->context.wake_en =
|
||||
readl_relaxed(bank->base + bank->regs->wkup_en);
|
||||
/* Defer wkup_en register update until we idle? */
|
||||
if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN) {
|
||||
if (trigger)
|
||||
bank->context.wake_en |= gpio_bit;
|
||||
else
|
||||
bank->context.wake_en &= ~gpio_bit;
|
||||
} else {
|
||||
omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit,
|
||||
trigger != 0);
|
||||
bank->context.wake_en =
|
||||
readl_relaxed(bank->base + bank->regs->wkup_en);
|
||||
}
|
||||
}
|
||||
|
||||
/* This part needs to be executed always for OMAP{34xx, 44xx} */
|
||||
|
@ -682,12 +702,7 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
|
|||
struct gpio_bank *bank = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* If this is the first gpio_request for the bank,
|
||||
* enable the bank module.
|
||||
*/
|
||||
if (!BANK_USED(bank))
|
||||
pm_runtime_get_sync(chip->parent);
|
||||
pm_runtime_get_sync(chip->parent);
|
||||
|
||||
raw_spin_lock_irqsave(&bank->lock, flags);
|
||||
omap_enable_gpio_module(bank, offset);
|
||||
|
@ -711,12 +726,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
|
|||
omap_disable_gpio_module(bank, offset);
|
||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
|
||||
/*
|
||||
* If this is the last gpio to be freed in the bank,
|
||||
* disable the bank module.
|
||||
*/
|
||||
if (!BANK_USED(bank))
|
||||
pm_runtime_put(chip->parent);
|
||||
pm_runtime_put(chip->parent);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -741,7 +751,9 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
|
|||
if (WARN_ON(!isr_reg))
|
||||
goto exit;
|
||||
|
||||
pm_runtime_get_sync(bank->chip.parent);
|
||||
if (WARN_ONCE(!pm_runtime_active(bank->chip.parent),
|
||||
"gpio irq%i while runtime suspended?\n", irq))
|
||||
return IRQ_NONE;
|
||||
|
||||
while (1) {
|
||||
raw_spin_lock_irqsave(&bank->lock, lock_flags);
|
||||
|
@ -792,7 +804,6 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
|
|||
}
|
||||
}
|
||||
exit:
|
||||
pm_runtime_put(bank->chip.parent);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -841,20 +852,14 @@ static void omap_gpio_irq_bus_lock(struct irq_data *data)
|
|||
{
|
||||
struct gpio_bank *bank = omap_irq_data_get_bank(data);
|
||||
|
||||
if (!BANK_USED(bank))
|
||||
pm_runtime_get_sync(bank->chip.parent);
|
||||
pm_runtime_get_sync(bank->chip.parent);
|
||||
}
|
||||
|
||||
static void gpio_irq_bus_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct gpio_bank *bank = omap_irq_data_get_bank(data);
|
||||
|
||||
/*
|
||||
* If this is the last IRQ to be freed in the bank,
|
||||
* disable the bank module.
|
||||
*/
|
||||
if (!BANK_USED(bank))
|
||||
pm_runtime_put(bank->chip.parent);
|
||||
pm_runtime_put(bank->chip.parent);
|
||||
}
|
||||
|
||||
static void omap_gpio_ack_irq(struct irq_data *d)
|
||||
|
@ -899,6 +904,82 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
|
|||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only edges can generate a wakeup event to the PRCM.
|
||||
*
|
||||
* Therefore, ensure any wake-up capable GPIOs have
|
||||
* edge-detection enabled before going idle to ensure a wakeup
|
||||
* to the PRCM is generated on a GPIO transition. (c.f. 34xx
|
||||
* NDA TRM 25.5.3.1)
|
||||
*
|
||||
* The normal values will be restored upon ->runtime_resume()
|
||||
* by writing back the values saved in bank->context.
|
||||
*/
|
||||
static void __maybe_unused
|
||||
omap2_gpio_enable_level_quirk(struct gpio_bank *bank)
|
||||
{
|
||||
u32 wake_low, wake_hi;
|
||||
|
||||
/* Enable additional edge detection for level gpios for idle */
|
||||
wake_low = bank->context.leveldetect0 & bank->context.wake_en;
|
||||
if (wake_low)
|
||||
writel_relaxed(wake_low | bank->context.fallingdetect,
|
||||
bank->base + bank->regs->fallingdetect);
|
||||
|
||||
wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
|
||||
if (wake_hi)
|
||||
writel_relaxed(wake_hi | bank->context.risingdetect,
|
||||
bank->base + bank->regs->risingdetect);
|
||||
}
|
||||
|
||||
static void __maybe_unused
|
||||
omap2_gpio_disable_level_quirk(struct gpio_bank *bank)
|
||||
{
|
||||
/* Disable edge detection for level gpios after idle */
|
||||
writel_relaxed(bank->context.fallingdetect,
|
||||
bank->base + bank->regs->fallingdetect);
|
||||
writel_relaxed(bank->context.risingdetect,
|
||||
bank->base + bank->regs->risingdetect);
|
||||
}
|
||||
|
||||
/*
|
||||
* On omap4 and later SoC variants a level interrupt with wkup_en
|
||||
* enabled blocks the GPIO functional clock from idling until the GPIO
|
||||
* instance has been reset. To avoid that, we must set wkup_en only for
|
||||
* idle for level interrupts, and clear level registers for the duration
|
||||
* of idle. The level interrupts will be still there on wakeup by their
|
||||
* nature.
|
||||
*/
|
||||
static void __maybe_unused
|
||||
omap4_gpio_enable_level_quirk(struct gpio_bank *bank)
|
||||
{
|
||||
/* Update wake register for idle, edge bits might be already set */
|
||||
writel_relaxed(bank->context.wake_en,
|
||||
bank->base + bank->regs->wkup_en);
|
||||
|
||||
/* Clear level registers for idle */
|
||||
writel_relaxed(0, bank->base + bank->regs->leveldetect0);
|
||||
writel_relaxed(0, bank->base + bank->regs->leveldetect1);
|
||||
}
|
||||
|
||||
static void __maybe_unused
|
||||
omap4_gpio_disable_level_quirk(struct gpio_bank *bank)
|
||||
{
|
||||
/* Restore level registers after idle */
|
||||
writel_relaxed(bank->context.leveldetect0,
|
||||
bank->base + bank->regs->leveldetect0);
|
||||
writel_relaxed(bank->context.leveldetect1,
|
||||
bank->base + bank->regs->leveldetect1);
|
||||
|
||||
/* Clear saved wkup_en for level, it will be set for next idle again */
|
||||
bank->context.wake_en &= ~(bank->context.leveldetect0 |
|
||||
bank->context.leveldetect1);
|
||||
|
||||
/* Update wake with only edge configuration */
|
||||
writel_relaxed(bank->context.wake_en,
|
||||
bank->base + bank->regs->wkup_en);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static int omap_mpuio_suspend_noirq(struct device *dev)
|
||||
|
@ -1218,6 +1299,36 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context);
|
||||
static void omap_gpio_unidle(struct gpio_bank *bank);
|
||||
|
||||
static int gpio_omap_cpu_notifier(struct notifier_block *nb,
|
||||
unsigned long cmd, void *v)
|
||||
{
|
||||
struct gpio_bank *bank;
|
||||
unsigned long flags;
|
||||
|
||||
bank = container_of(nb, struct gpio_bank, nb);
|
||||
|
||||
raw_spin_lock_irqsave(&bank->lock, flags);
|
||||
switch (cmd) {
|
||||
case CPU_CLUSTER_PM_ENTER:
|
||||
if (bank->is_suspended)
|
||||
break;
|
||||
omap_gpio_idle(bank, true);
|
||||
break;
|
||||
case CPU_CLUSTER_PM_ENTER_FAILED:
|
||||
case CPU_CLUSTER_PM_EXIT:
|
||||
if (bank->is_suspended)
|
||||
break;
|
||||
omap_gpio_unidle(bank);
|
||||
break;
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static const struct of_device_id omap_gpio_match[];
|
||||
|
||||
static int omap_gpio_probe(struct platform_device *pdev)
|
||||
|
@ -1256,6 +1367,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||
irqc->irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
|
||||
irqc->name = dev_name(&pdev->dev);
|
||||
irqc->flags = IRQCHIP_MASK_ON_SUSPEND;
|
||||
irqc->parent_device = dev;
|
||||
|
||||
bank->irq = platform_get_irq(pdev, 0);
|
||||
if (bank->irq <= 0) {
|
||||
|
@ -1270,6 +1382,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||
bank->chip.parent = dev;
|
||||
bank->chip.owner = THIS_MODULE;
|
||||
bank->dbck_flag = pdata->dbck_flag;
|
||||
bank->quirks = pdata->quirks;
|
||||
bank->stride = pdata->bank_stride;
|
||||
bank->width = pdata->bank_width;
|
||||
bank->is_mpuio = pdata->is_mpuio;
|
||||
|
@ -1278,6 +1391,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||
#ifdef CONFIG_OF_GPIO
|
||||
bank->chip.of_node = of_node_get(node);
|
||||
#endif
|
||||
|
||||
if (node) {
|
||||
if (!of_property_read_bool(node, "ti,gpio-always-on"))
|
||||
bank->loses_context = true;
|
||||
|
@ -1298,6 +1412,18 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||
omap_set_gpio_dataout_mask_multiple;
|
||||
}
|
||||
|
||||
if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN) {
|
||||
bank->funcs.idle_enable_level_quirk =
|
||||
omap4_gpio_enable_level_quirk;
|
||||
bank->funcs.idle_disable_level_quirk =
|
||||
omap4_gpio_disable_level_quirk;
|
||||
} else if (bank->quirks & OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER) {
|
||||
bank->funcs.idle_enable_level_quirk =
|
||||
omap2_gpio_enable_level_quirk;
|
||||
bank->funcs.idle_disable_level_quirk =
|
||||
omap2_gpio_disable_level_quirk;
|
||||
}
|
||||
|
||||
raw_spin_lock_init(&bank->lock);
|
||||
raw_spin_lock_init(&bank->wa_lock);
|
||||
|
||||
|
@ -1322,7 +1448,6 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, bank);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_irq_safe(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
if (bank->is_mpuio)
|
||||
|
@ -1341,9 +1466,13 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
omap_gpio_show_rev(bank);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
if (bank->funcs.idle_enable_level_quirk &&
|
||||
bank->funcs.idle_disable_level_quirk) {
|
||||
bank->nb.notifier_call = gpio_omap_cpu_notifier;
|
||||
cpu_pm_register_notifier(&bank->nb);
|
||||
}
|
||||
|
||||
list_add_tail(&bank->node, &omap_gpio_list);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1352,6 +1481,8 @@ static int omap_gpio_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
|
||||
if (bank->nb.notifier_call)
|
||||
cpu_pm_unregister_notifier(&bank->nb);
|
||||
list_del(&bank->node);
|
||||
gpiochip_remove(&bank->chip);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
@ -1361,48 +1492,22 @@ static int omap_gpio_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP2PLUS
|
||||
|
||||
#if defined(CONFIG_PM)
|
||||
static void omap_gpio_restore_context(struct gpio_bank *bank);
|
||||
|
||||
static int omap_gpio_runtime_suspend(struct device *dev)
|
||||
static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
struct device *dev = bank->chip.parent;
|
||||
u32 l1 = 0, l2 = 0;
|
||||
unsigned long flags;
|
||||
u32 wake_low, wake_hi;
|
||||
|
||||
raw_spin_lock_irqsave(&bank->lock, flags);
|
||||
|
||||
/*
|
||||
* Only edges can generate a wakeup event to the PRCM.
|
||||
*
|
||||
* Therefore, ensure any wake-up capable GPIOs have
|
||||
* edge-detection enabled before going idle to ensure a wakeup
|
||||
* to the PRCM is generated on a GPIO transition. (c.f. 34xx
|
||||
* NDA TRM 25.5.3.1)
|
||||
*
|
||||
* The normal values will be restored upon ->runtime_resume()
|
||||
* by writing back the values saved in bank->context.
|
||||
*/
|
||||
wake_low = bank->context.leveldetect0 & bank->context.wake_en;
|
||||
if (wake_low)
|
||||
writel_relaxed(wake_low | bank->context.fallingdetect,
|
||||
bank->base + bank->regs->fallingdetect);
|
||||
wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
|
||||
if (wake_hi)
|
||||
writel_relaxed(wake_hi | bank->context.risingdetect,
|
||||
bank->base + bank->regs->risingdetect);
|
||||
if (bank->funcs.idle_enable_level_quirk)
|
||||
bank->funcs.idle_enable_level_quirk(bank);
|
||||
|
||||
if (!bank->enabled_non_wakeup_gpios)
|
||||
goto update_gpio_context_count;
|
||||
|
||||
if (bank->power_mode != OFF_MODE) {
|
||||
bank->power_mode = 0;
|
||||
if (!may_lose_context)
|
||||
goto update_gpio_context_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* If going to OFF, remove triggering for all
|
||||
* non-wakeup GPIOs. Otherwise spurious IRQs will be
|
||||
|
@ -1427,23 +1532,16 @@ update_gpio_context_count:
|
|||
bank->get_context_loss_count(dev);
|
||||
|
||||
omap_gpio_dbck_disable(bank);
|
||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_gpio_init_context(struct gpio_bank *p);
|
||||
|
||||
static int omap_gpio_runtime_resume(struct device *dev)
|
||||
static void omap_gpio_unidle(struct gpio_bank *bank)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
struct device *dev = bank->chip.parent;
|
||||
u32 l = 0, gen, gen0, gen1;
|
||||
unsigned long flags;
|
||||
int c;
|
||||
|
||||
raw_spin_lock_irqsave(&bank->lock, flags);
|
||||
|
||||
/*
|
||||
* On the first resume during the probe, the context has not
|
||||
* been initialised and so initialise it now. Also initialise
|
||||
|
@ -1459,16 +1557,8 @@ static int omap_gpio_runtime_resume(struct device *dev)
|
|||
|
||||
omap_gpio_dbck_enable(bank);
|
||||
|
||||
/*
|
||||
* In ->runtime_suspend(), level-triggered, wakeup-enabled
|
||||
* GPIOs were set to edge trigger also in order to be able to
|
||||
* generate a PRCM wakeup. Here we restore the
|
||||
* pre-runtime_suspend() values for edge triggering.
|
||||
*/
|
||||
writel_relaxed(bank->context.fallingdetect,
|
||||
bank->base + bank->regs->fallingdetect);
|
||||
writel_relaxed(bank->context.risingdetect,
|
||||
bank->base + bank->regs->risingdetect);
|
||||
if (bank->funcs.idle_disable_level_quirk)
|
||||
bank->funcs.idle_disable_level_quirk(bank);
|
||||
|
||||
if (bank->loses_context) {
|
||||
if (!bank->get_context_loss_count) {
|
||||
|
@ -1478,16 +1568,13 @@ static int omap_gpio_runtime_resume(struct device *dev)
|
|||
if (c != bank->context_loss_count) {
|
||||
omap_gpio_restore_context(bank);
|
||||
} else {
|
||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bank->workaround_enabled) {
|
||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
if (!bank->workaround_enabled)
|
||||
return;
|
||||
|
||||
l = readl_relaxed(bank->base + bank->regs->datain);
|
||||
|
||||
|
@ -1540,41 +1627,8 @@ static int omap_gpio_runtime_resume(struct device *dev)
|
|||
}
|
||||
|
||||
bank->workaround_enabled = false;
|
||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#if IS_BUILTIN(CONFIG_GPIO_OMAP)
|
||||
void omap2_gpio_prepare_for_idle(int pwr_mode)
|
||||
{
|
||||
struct gpio_bank *bank;
|
||||
|
||||
list_for_each_entry(bank, &omap_gpio_list, node) {
|
||||
if (!BANK_USED(bank) || !bank->loses_context)
|
||||
continue;
|
||||
|
||||
bank->power_mode = pwr_mode;
|
||||
|
||||
pm_runtime_put_sync_suspend(bank->chip.parent);
|
||||
}
|
||||
}
|
||||
|
||||
void omap2_gpio_resume_after_idle(void)
|
||||
{
|
||||
struct gpio_bank *bank;
|
||||
|
||||
list_for_each_entry(bank, &omap_gpio_list, node) {
|
||||
if (!BANK_USED(bank) || !bank->loses_context)
|
||||
continue;
|
||||
|
||||
pm_runtime_get_sync(bank->chip.parent);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PM)
|
||||
static void omap_gpio_init_context(struct gpio_bank *p)
|
||||
{
|
||||
struct omap_gpio_reg_offs *regs = p->regs;
|
||||
|
@ -1631,17 +1685,57 @@ static void omap_gpio_restore_context(struct gpio_bank *bank)
|
|||
writel_relaxed(bank->context.irqenable2,
|
||||
bank->base + bank->regs->irqenable2);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
#else
|
||||
#define omap_gpio_runtime_suspend NULL
|
||||
#define omap_gpio_runtime_resume NULL
|
||||
static inline void omap_gpio_init_context(struct gpio_bank *p) {}
|
||||
#endif
|
||||
|
||||
static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
raw_spin_lock_irqsave(&bank->lock, flags);
|
||||
/* Must be idled only by CPU_CLUSTER_PM_ENTER? */
|
||||
if (bank->irq_usage) {
|
||||
error = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
omap_gpio_idle(bank, true);
|
||||
bank->is_suspended = true;
|
||||
unlock:
|
||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
raw_spin_lock_irqsave(&bank->lock, flags);
|
||||
/* Must be unidled only by CPU_CLUSTER_PM_ENTER? */
|
||||
if (bank->irq_usage) {
|
||||
error = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
omap_gpio_unidle(bank);
|
||||
bank->is_suspended = false;
|
||||
unlock:
|
||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP2PLUS
|
||||
static const struct dev_pm_ops gpio_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
#else
|
||||
static const struct dev_pm_ops gpio_pm_ops;
|
||||
#endif /* CONFIG_ARCH_OMAP2PLUS */
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static struct omap_gpio_reg_offs omap2_gpio_regs = {
|
||||
|
@ -1690,6 +1784,11 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = {
|
|||
.fallingdetect = OMAP4_GPIO_FALLINGDETECT,
|
||||
};
|
||||
|
||||
/*
|
||||
* Note that omap2 does not currently support idle modes with context loss so
|
||||
* no need to add OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER quirk flag to save
|
||||
* and restore context.
|
||||
*/
|
||||
static const struct omap_gpio_platform_data omap2_pdata = {
|
||||
.regs = &omap2_gpio_regs,
|
||||
.bank_width = 32,
|
||||
|
@ -1700,12 +1799,15 @@ static const struct omap_gpio_platform_data omap3_pdata = {
|
|||
.regs = &omap2_gpio_regs,
|
||||
.bank_width = 32,
|
||||
.dbck_flag = true,
|
||||
.quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER,
|
||||
};
|
||||
|
||||
static const struct omap_gpio_platform_data omap4_pdata = {
|
||||
.regs = &omap4_gpio_regs,
|
||||
.bank_width = 32,
|
||||
.dbck_flag = true,
|
||||
.quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER |
|
||||
OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN,
|
||||
};
|
||||
|
||||
static const struct of_device_id omap_gpio_match[] = {
|
||||
|
|
|
@ -776,6 +776,9 @@ static int pxa_gpio_suspend(void)
|
|||
struct pxa_gpio_bank *c;
|
||||
int gpio;
|
||||
|
||||
if (!pchip)
|
||||
return 0;
|
||||
|
||||
for_each_gpio_bank(gpio, c, pchip) {
|
||||
c->saved_gplr = readl_relaxed(c->regbase + GPLR_OFFSET);
|
||||
c->saved_gpdr = readl_relaxed(c->regbase + GPDR_OFFSET);
|
||||
|
@ -794,6 +797,9 @@ static void pxa_gpio_resume(void)
|
|||
struct pxa_gpio_bank *c;
|
||||
int gpio;
|
||||
|
||||
if (!pchip)
|
||||
return;
|
||||
|
||||
for_each_gpio_bank(gpio, c, pchip) {
|
||||
/* restore level with set/clear */
|
||||
writel_relaxed(c->saved_gplr, c->regbase + GPSR_OFFSET);
|
||||
|
|
|
@ -321,6 +321,9 @@ static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask,
|
|||
u32 val, bankmask;
|
||||
|
||||
bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0);
|
||||
if (chip->valid_mask)
|
||||
bankmask &= chip->valid_mask[0];
|
||||
|
||||
if (!bankmask)
|
||||
return;
|
||||
|
||||
|
@ -558,6 +561,9 @@ static int gpio_rcar_resume(struct device *dev)
|
|||
u32 mask;
|
||||
|
||||
for (offset = 0; offset < p->gpio_chip.ngpio; offset++) {
|
||||
if (!gpiochip_line_is_valid(&p->gpio_chip, offset))
|
||||
continue;
|
||||
|
||||
mask = BIT(offset);
|
||||
/* I/O pin */
|
||||
if (!(p->bank_info.iointsel & mask)) {
|
||||
|
|
293
drivers/gpio/gpio-siox.c
Normal file
293
drivers/gpio/gpio-siox.c
Normal file
|
@ -0,0 +1,293 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/siox.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
struct gpio_siox_ddata {
|
||||
struct gpio_chip gchip;
|
||||
struct irq_chip ichip;
|
||||
struct mutex lock;
|
||||
u8 setdata[1];
|
||||
u8 getdata[3];
|
||||
|
||||
spinlock_t irqlock;
|
||||
u32 irq_enable;
|
||||
u32 irq_status;
|
||||
u32 irq_type[20];
|
||||
};
|
||||
|
||||
/*
|
||||
* Note that this callback only sets the value that is clocked out in the next
|
||||
* cycle.
|
||||
*/
|
||||
static int gpio_siox_set_data(struct siox_device *sdevice, u8 status, u8 buf[])
|
||||
{
|
||||
struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
|
||||
|
||||
mutex_lock(&ddata->lock);
|
||||
buf[0] = ddata->setdata[0];
|
||||
mutex_unlock(&ddata->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
|
||||
{
|
||||
struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
|
||||
size_t offset;
|
||||
u32 trigger;
|
||||
|
||||
mutex_lock(&ddata->lock);
|
||||
|
||||
spin_lock_irq(&ddata->irqlock);
|
||||
|
||||
for (offset = 0; offset < 12; ++offset) {
|
||||
unsigned int bitpos = 11 - offset;
|
||||
unsigned int gpiolevel = buf[bitpos / 8] & (1 << bitpos % 8);
|
||||
unsigned int prev_level =
|
||||
ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
|
||||
u32 irq_type = ddata->irq_type[offset];
|
||||
|
||||
if (gpiolevel) {
|
||||
if ((irq_type & IRQ_TYPE_LEVEL_HIGH) ||
|
||||
((irq_type & IRQ_TYPE_EDGE_RISING) && !prev_level))
|
||||
ddata->irq_status |= 1 << offset;
|
||||
} else {
|
||||
if ((irq_type & IRQ_TYPE_LEVEL_LOW) ||
|
||||
((irq_type & IRQ_TYPE_EDGE_FALLING) && prev_level))
|
||||
ddata->irq_status |= 1 << offset;
|
||||
}
|
||||
}
|
||||
|
||||
trigger = ddata->irq_status & ddata->irq_enable;
|
||||
|
||||
spin_unlock_irq(&ddata->irqlock);
|
||||
|
||||
ddata->getdata[0] = buf[0];
|
||||
ddata->getdata[1] = buf[1];
|
||||
ddata->getdata[2] = buf[2];
|
||||
|
||||
mutex_unlock(&ddata->lock);
|
||||
|
||||
for (offset = 0; offset < 12; ++offset) {
|
||||
if (trigger & (1 << offset)) {
|
||||
struct irq_domain *irqdomain = ddata->gchip.irq.domain;
|
||||
unsigned int irq = irq_find_mapping(irqdomain, offset);
|
||||
|
||||
/*
|
||||
* Conceptually handle_nested_irq should call the flow
|
||||
* handler of the irq chip. But it doesn't, so we have
|
||||
* to clean the irq_status here.
|
||||
*/
|
||||
spin_lock_irq(&ddata->irqlock);
|
||||
ddata->irq_status &= ~(1 << offset);
|
||||
spin_unlock_irq(&ddata->irqlock);
|
||||
|
||||
handle_nested_irq(irq);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpio_siox_irq_ack(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
||||
struct gpio_siox_ddata *ddata =
|
||||
container_of(ic, struct gpio_siox_ddata, ichip);
|
||||
|
||||
spin_lock_irq(&ddata->irqlock);
|
||||
ddata->irq_status &= ~(1 << d->hwirq);
|
||||
spin_unlock_irq(&ddata->irqlock);
|
||||
}
|
||||
|
||||
static void gpio_siox_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
||||
struct gpio_siox_ddata *ddata =
|
||||
container_of(ic, struct gpio_siox_ddata, ichip);
|
||||
|
||||
spin_lock_irq(&ddata->irqlock);
|
||||
ddata->irq_enable &= ~(1 << d->hwirq);
|
||||
spin_unlock_irq(&ddata->irqlock);
|
||||
}
|
||||
|
||||
static void gpio_siox_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
||||
struct gpio_siox_ddata *ddata =
|
||||
container_of(ic, struct gpio_siox_ddata, ichip);
|
||||
|
||||
spin_lock_irq(&ddata->irqlock);
|
||||
ddata->irq_enable |= 1 << d->hwirq;
|
||||
spin_unlock_irq(&ddata->irqlock);
|
||||
}
|
||||
|
||||
static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
|
||||
{
|
||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
||||
struct gpio_siox_ddata *ddata =
|
||||
container_of(ic, struct gpio_siox_ddata, ichip);
|
||||
|
||||
spin_lock_irq(&ddata->irqlock);
|
||||
ddata->irq_type[d->hwirq] = type;
|
||||
spin_unlock_irq(&ddata->irqlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct gpio_siox_ddata *ddata =
|
||||
container_of(chip, struct gpio_siox_ddata, gchip);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ddata->lock);
|
||||
|
||||
if (offset >= 12) {
|
||||
unsigned int bitpos = 19 - offset;
|
||||
|
||||
ret = ddata->setdata[0] & (1 << bitpos);
|
||||
} else {
|
||||
unsigned int bitpos = 11 - offset;
|
||||
|
||||
ret = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
|
||||
}
|
||||
|
||||
mutex_unlock(&ddata->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gpio_siox_set(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
struct gpio_siox_ddata *ddata =
|
||||
container_of(chip, struct gpio_siox_ddata, gchip);
|
||||
u8 mask = 1 << (19 - offset);
|
||||
|
||||
mutex_lock(&ddata->lock);
|
||||
|
||||
if (value)
|
||||
ddata->setdata[0] |= mask;
|
||||
else
|
||||
ddata->setdata[0] &= ~mask;
|
||||
|
||||
mutex_unlock(&ddata->lock);
|
||||
}
|
||||
|
||||
static int gpio_siox_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
if (offset >= 12)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_siox_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
if (offset < 12)
|
||||
return -EINVAL;
|
||||
|
||||
gpio_siox_set(chip, offset, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
if (offset < 12)
|
||||
return 1; /* input */
|
||||
else
|
||||
return 0; /* output */
|
||||
}
|
||||
|
||||
static int gpio_siox_probe(struct siox_device *sdevice)
|
||||
{
|
||||
struct gpio_siox_ddata *ddata;
|
||||
int ret;
|
||||
|
||||
ddata = devm_kzalloc(&sdevice->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&sdevice->dev, ddata);
|
||||
|
||||
mutex_init(&ddata->lock);
|
||||
spin_lock_init(&ddata->irqlock);
|
||||
|
||||
ddata->gchip.base = -1;
|
||||
ddata->gchip.can_sleep = 1;
|
||||
ddata->gchip.parent = &sdevice->dev;
|
||||
ddata->gchip.owner = THIS_MODULE;
|
||||
ddata->gchip.get = gpio_siox_get;
|
||||
ddata->gchip.set = gpio_siox_set;
|
||||
ddata->gchip.direction_input = gpio_siox_direction_input;
|
||||
ddata->gchip.direction_output = gpio_siox_direction_output;
|
||||
ddata->gchip.get_direction = gpio_siox_get_direction;
|
||||
ddata->gchip.ngpio = 20;
|
||||
|
||||
ddata->ichip.name = "siox-gpio";
|
||||
ddata->ichip.irq_ack = gpio_siox_irq_ack;
|
||||
ddata->ichip.irq_mask = gpio_siox_irq_mask;
|
||||
ddata->ichip.irq_unmask = gpio_siox_irq_unmask;
|
||||
ddata->ichip.irq_set_type = gpio_siox_irq_set_type;
|
||||
|
||||
ret = gpiochip_add(&ddata->gchip);
|
||||
if (ret) {
|
||||
dev_err(&sdevice->dev,
|
||||
"Failed to register gpio chip (%d)\n", ret);
|
||||
goto err_gpiochip;
|
||||
}
|
||||
|
||||
ret = gpiochip_irqchip_add(&ddata->gchip, &ddata->ichip,
|
||||
0, handle_level_irq, IRQ_TYPE_EDGE_RISING);
|
||||
if (ret) {
|
||||
dev_err(&sdevice->dev,
|
||||
"Failed to register irq chip (%d)\n", ret);
|
||||
err_gpiochip:
|
||||
gpiochip_remove(&ddata->gchip);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_siox_remove(struct siox_device *sdevice)
|
||||
{
|
||||
struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
|
||||
|
||||
gpiochip_remove(&ddata->gchip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct siox_driver gpio_siox_driver = {
|
||||
.probe = gpio_siox_probe,
|
||||
.remove = gpio_siox_remove,
|
||||
.set_data = gpio_siox_set_data,
|
||||
.get_data = gpio_siox_get_data,
|
||||
.driver = {
|
||||
.name = "gpio-siox",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init gpio_siox_init(void)
|
||||
{
|
||||
return siox_driver_register(&gpio_siox_driver);
|
||||
}
|
||||
module_init(gpio_siox_init);
|
||||
|
||||
static void __exit gpio_siox_exit(void)
|
||||
{
|
||||
siox_driver_unregister(&gpio_siox_driver);
|
||||
}
|
||||
module_exit(gpio_siox_exit);
|
||||
|
||||
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("SIOX gpio driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -122,7 +122,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
|
|||
BIT(offs % SYSCON_REG_BITS));
|
||||
}
|
||||
|
||||
priv->data->set(chip, offset, val);
|
||||
chip->set(chip, offset, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -45,14 +45,12 @@
|
|||
|
||||
|
||||
/**
|
||||
* @spinlock: used for atomic read/modify/write of registers
|
||||
* @base: register base address
|
||||
* @domain: IRQ domain of GPIO generated interrupts managed by this controller
|
||||
* @irq: Interrupt line of parent interrupt controller
|
||||
* @gc: gpio_chip structure associated to this GPIO controller
|
||||
*/
|
||||
struct tb10x_gpio {
|
||||
spinlock_t spinlock;
|
||||
void __iomem *base;
|
||||
struct irq_domain *domain;
|
||||
int irq;
|
||||
|
@ -76,60 +74,14 @@ static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
|
|||
u32 r;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpio->spinlock, flags);
|
||||
spin_lock_irqsave(&gpio->gc.bgpio_lock, flags);
|
||||
|
||||
r = tb10x_reg_read(gpio, offs);
|
||||
r = (r & ~mask) | (val & mask);
|
||||
|
||||
tb10x_reg_write(gpio, offs, r);
|
||||
|
||||
spin_unlock_irqrestore(&gpio->spinlock, flags);
|
||||
}
|
||||
|
||||
static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
|
||||
int mask = BIT(offset);
|
||||
int val = TB10X_GPIO_DIR_IN << offset;
|
||||
|
||||
tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
|
||||
int val;
|
||||
|
||||
val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA);
|
||||
|
||||
if (val & BIT(offset))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
|
||||
int mask = BIT(offset);
|
||||
int val = value << offset;
|
||||
|
||||
tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val);
|
||||
}
|
||||
|
||||
static int tb10x_gpio_direction_out(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
|
||||
int mask = BIT(offset);
|
||||
int val = TB10X_GPIO_DIR_OUT << offset;
|
||||
|
||||
tb10x_gpio_set(chip, offset, value);
|
||||
tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
|
||||
|
||||
return 0;
|
||||
spin_unlock_irqrestore(&gpio->gc.bgpio_lock, flags);
|
||||
}
|
||||
|
||||
static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
|
@ -169,72 +121,85 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct tb10x_gpio *tb10x_gpio;
|
||||
struct resource *mem;
|
||||
struct device_node *dn = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret = -EBUSY;
|
||||
u32 ngpio;
|
||||
|
||||
if (!dn)
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_u32(dn, "abilis,ngpio", &ngpio))
|
||||
if (of_property_read_u32(np, "abilis,ngpio", &ngpio))
|
||||
return -EINVAL;
|
||||
|
||||
tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL);
|
||||
tb10x_gpio = devm_kzalloc(dev, sizeof(*tb10x_gpio), GFP_KERNEL);
|
||||
if (tb10x_gpio == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&tb10x_gpio->spinlock);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
tb10x_gpio->base = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(tb10x_gpio->base))
|
||||
return PTR_ERR(tb10x_gpio->base);
|
||||
|
||||
tb10x_gpio->gc.label =
|
||||
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOF", pdev->dev.of_node);
|
||||
tb10x_gpio->gc.label =
|
||||
devm_kasprintf(dev, GFP_KERNEL, "%pOF", pdev->dev.of_node);
|
||||
if (!tb10x_gpio->gc.label)
|
||||
return -ENOMEM;
|
||||
|
||||
tb10x_gpio->gc.parent = &pdev->dev;
|
||||
tb10x_gpio->gc.owner = THIS_MODULE;
|
||||
tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
|
||||
tb10x_gpio->gc.get = tb10x_gpio_get;
|
||||
tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
|
||||
tb10x_gpio->gc.set = tb10x_gpio_set;
|
||||
tb10x_gpio->gc.request = gpiochip_generic_request;
|
||||
tb10x_gpio->gc.free = gpiochip_generic_free;
|
||||
tb10x_gpio->gc.base = -1;
|
||||
tb10x_gpio->gc.ngpio = ngpio;
|
||||
tb10x_gpio->gc.can_sleep = false;
|
||||
/*
|
||||
* Initialize generic GPIO with one single register for reading and setting
|
||||
* the lines, no special set or clear registers and a data direction register
|
||||
* wher 1 means "output".
|
||||
*/
|
||||
ret = bgpio_init(&tb10x_gpio->gc, dev, 4,
|
||||
tb10x_gpio->base + OFFSET_TO_REG_DATA,
|
||||
NULL,
|
||||
NULL,
|
||||
tb10x_gpio->base + OFFSET_TO_REG_DDR,
|
||||
NULL,
|
||||
0);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to init generic GPIO\n");
|
||||
return ret;
|
||||
}
|
||||
tb10x_gpio->gc.base = -1;
|
||||
tb10x_gpio->gc.parent = dev;
|
||||
tb10x_gpio->gc.owner = THIS_MODULE;
|
||||
/*
|
||||
* ngpio is set by bgpio_init() but we override it, this .request()
|
||||
* callback also overrides the one set up by generic GPIO.
|
||||
*/
|
||||
tb10x_gpio->gc.ngpio = ngpio;
|
||||
tb10x_gpio->gc.request = gpiochip_generic_request;
|
||||
tb10x_gpio->gc.free = gpiochip_generic_free;
|
||||
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &tb10x_gpio->gc, tb10x_gpio);
|
||||
ret = devm_gpiochip_add_data(dev, &tb10x_gpio->gc, tb10x_gpio);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not add gpiochip.\n");
|
||||
dev_err(dev, "Could not add gpiochip.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tb10x_gpio);
|
||||
|
||||
if (of_find_property(dn, "interrupt-controller", NULL)) {
|
||||
if (of_find_property(np, "interrupt-controller", NULL)) {
|
||||
struct irq_chip_generic *gc;
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "No interrupt specified.\n");
|
||||
dev_err(dev, "No interrupt specified.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
|
||||
tb10x_gpio->irq = ret;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
|
||||
ret = devm_request_irq(dev, ret, tb10x_gpio_irq_cascade,
|
||||
IRQF_TRIGGER_NONE | IRQF_SHARED,
|
||||
dev_name(&pdev->dev), tb10x_gpio);
|
||||
dev_name(dev), tb10x_gpio);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
tb10x_gpio->domain = irq_domain_add_linear(dn,
|
||||
tb10x_gpio->domain = irq_domain_add_linear(np,
|
||||
tb10x_gpio->gc.ngpio,
|
||||
&irq_generic_chip_ops, NULL);
|
||||
if (!tb10x_gpio->domain) {
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether expressed or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License version 2 for more details.
|
||||
*
|
||||
* Based on the TPS65912 driver
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* TI TPS6586x GPIO driver
|
||||
*
|
||||
|
@ -7,22 +8,10 @@
|
|||
* Based on tps6586x.c
|
||||
* Copyright (c) 2010 CompuLab Ltd.
|
||||
* Mike Rapoport <mike@compulab.co.il>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mfd/tps6586x.h>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* TI TPS6591x GPIO driver
|
||||
*
|
||||
|
@ -5,18 +6,12 @@
|
|||
*
|
||||
* Author: Graeme Gregory <gg@slimlogic.co.uk>
|
||||
* Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/tps65910.h>
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPIO driver for TI TPS65912x PMICs
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether expressed or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License version 2 for more details.
|
||||
*
|
||||
* Based on the Arizona GPIO driver and the previous TPS65912 driver by
|
||||
* Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
|
@ -40,9 +32,9 @@ static int tps65912_gpio_get_direction(struct gpio_chip *gc,
|
|||
return ret;
|
||||
|
||||
if (val & GPIO_CFG_MASK)
|
||||
return GPIOF_DIR_OUT;
|
||||
return 0;
|
||||
else
|
||||
return GPIOF_DIR_IN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tps65912_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Digital I/O driver for Technologic Systems TS-5500
|
||||
*
|
||||
|
@ -16,17 +17,12 @@
|
|||
* TS-5600:
|
||||
* Documentation: http://wiki.embeddedarm.com/wiki/TS-5600
|
||||
* Blocks: LCD port (identical to TS-5500 LCD).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/gpio-ts5500.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -318,7 +314,6 @@ static void ts5500_disable_irq(struct ts5500_priv *priv)
|
|||
static int ts5500_dio_probe(struct platform_device *pdev)
|
||||
{
|
||||
enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
|
||||
struct ts5500_dio_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
const char *name = dev_name(dev);
|
||||
struct ts5500_priv *priv;
|
||||
|
@ -349,10 +344,6 @@ static int ts5500_dio_probe(struct platform_device *pdev)
|
|||
priv->gpio_chip.set = ts5500_gpio_set;
|
||||
priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
|
||||
priv->gpio_chip.base = -1;
|
||||
if (pdata) {
|
||||
priv->gpio_chip.base = pdata->base;
|
||||
priv->strap = pdata->strap;
|
||||
}
|
||||
|
||||
switch (block) {
|
||||
case TS5500_DIO1:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Access to GPIOs on TWL4030/TPS659x0 chips
|
||||
*
|
||||
|
@ -9,20 +10,6 @@
|
|||
*
|
||||
* Initial Code:
|
||||
* Andy Lowe / Nishanth Menon
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -30,7 +17,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
@ -167,6 +154,23 @@ static int twl4030_set_gpio_direction(int gpio, int is_input)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int twl4030_get_gpio_direction(int gpio)
|
||||
{
|
||||
u8 d_bnk = gpio >> 3;
|
||||
u8 d_msk = BIT(gpio & 0x7);
|
||||
u8 base = REG_GPIODATADIR1 + d_bnk;
|
||||
int ret = 0;
|
||||
|
||||
ret = gpio_twl4030_read(base);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* 1 = output, but gpiolib semantics are inverse so invert */
|
||||
ret = !(ret & d_msk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int twl4030_set_gpio_dataout(int gpio, int enable)
|
||||
{
|
||||
u8 d_bnk = gpio >> 3;
|
||||
|
@ -372,6 +376,28 @@ static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int twl_get_direction(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct gpio_twl4030_priv *priv = gpiochip_get_data(chip);
|
||||
/*
|
||||
* Default 0 = output
|
||||
* LED GPIOs >= TWL4030_GPIO_MAX are always output
|
||||
*/
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
if (offset < TWL4030_GPIO_MAX) {
|
||||
ret = twl4030_get_gpio_direction(offset);
|
||||
if (ret) {
|
||||
mutex_unlock(&priv->mutex);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int twl_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct gpio_twl4030_priv *priv = gpiochip_get_data(chip);
|
||||
|
@ -387,8 +413,9 @@ static const struct gpio_chip template_chip = {
|
|||
.request = twl_request,
|
||||
.free = twl_free,
|
||||
.direction_input = twl_direction_in,
|
||||
.get = twl_get,
|
||||
.direction_output = twl_direction_out,
|
||||
.get_direction = twl_get_direction,
|
||||
.get = twl_get,
|
||||
.set = twl_set,
|
||||
.to_irq = twl_to_irq,
|
||||
.can_sleep = true,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Access to GPOs on TWL6040 chip
|
||||
*
|
||||
|
@ -6,28 +7,15 @@
|
|||
* Authors:
|
||||
* Sergio Aguirre <saaguirre@ti.com>
|
||||
* Peter Ujfalusi <peter.ujfalusi@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/mfd/twl6040.h>
|
||||
|
@ -41,7 +29,13 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (ret >> offset) & 1;
|
||||
return !!(ret & BIT(offset));
|
||||
}
|
||||
|
||||
static int twl6040gpo_get_direction(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
/* This means "out" */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
|
||||
|
@ -62,9 +56,9 @@ static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value)
|
|||
return;
|
||||
|
||||
if (value)
|
||||
gpoctl = ret | (1 << offset);
|
||||
gpoctl = ret | BIT(offset);
|
||||
else
|
||||
gpoctl = ret & ~(1 << offset);
|
||||
gpoctl = ret & ~BIT(offset);
|
||||
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
|
||||
}
|
||||
|
@ -74,6 +68,7 @@ static struct gpio_chip twl6040gpo_chip = {
|
|||
.owner = THIS_MODULE,
|
||||
.get = twl6040gpo_get,
|
||||
.direction_output = twl6040gpo_direction_out,
|
||||
.get_direction = twl6040gpo_get_direction,
|
||||
.set = twl6040gpo_set,
|
||||
.can_sleep = true,
|
||||
};
|
||||
|
|
|
@ -1,23 +1,14 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Freescale vf610 GPIO support through PORT and GPIO
|
||||
*
|
||||
* Copyright (c) 2014 Toradex AG.
|
||||
*
|
||||
* Author: Stefan Agner <stefan@agner.ch>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Nano River Technologies viperboard GPIO lib driver
|
||||
*
|
||||
* (C) 2012 by Lemonage GmbH
|
||||
* Author: Lars Poeschel <poeschel@lemonage.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -19,9 +14,8 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
#include <linux/mfd/viperboard.h>
|
||||
|
||||
|
|
|
@ -1,27 +1,14 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Driver for NEC VR4100 series General-purpose I/O Unit.
|
||||
*
|
||||
* Copyright (C) 2002 MontaVista Software Inc.
|
||||
* Author: Yoichi Yuasa <source@mvista.com>
|
||||
* Copyright (C) 2003-2009 Yoichi Yuasa <yuasa@linux-mips.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -384,44 +371,6 @@ static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull)
|
||||
{
|
||||
u16 reg, mask;
|
||||
unsigned long flags;
|
||||
|
||||
if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO)
|
||||
return -EPERM;
|
||||
|
||||
if (pin >= 15)
|
||||
return -EINVAL;
|
||||
|
||||
mask = 1 << pin;
|
||||
|
||||
spin_lock_irqsave(&giu_lock, flags);
|
||||
|
||||
if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) {
|
||||
reg = giu_read(GIUTERMUPDN);
|
||||
if (pull == GPIO_PULL_UP)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
giu_write(GIUTERMUPDN, reg);
|
||||
|
||||
reg = giu_read(GIUUSEUPDN);
|
||||
reg |= mask;
|
||||
giu_write(GIUUSEUPDN, reg);
|
||||
} else {
|
||||
reg = giu_read(GIUUSEUPDN);
|
||||
reg &= ~mask;
|
||||
giu_write(GIUUSEUPDN, reg);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&giu_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown);
|
||||
|
||||
static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin)
|
||||
{
|
||||
u16 reg, mask;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO
|
||||
*
|
||||
|
@ -5,27 +6,10 @@
|
|||
* Copyright (C) 2010 One Laptop per Child
|
||||
* Author: Harald Welte <HaraldWelte@viatech.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* gpiolib support for Wolfson WM831x PMICs
|
||||
*
|
||||
|
@ -5,17 +6,12 @@
|
|||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* gpiolib support for Wolfson WM835x PMICs
|
||||
*
|
||||
|
@ -5,17 +6,12 @@
|
|||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* gpiolib support for Wolfson WM8994
|
||||
*
|
||||
|
@ -5,17 +6,12 @@
|
|||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2003-2015 Broadcom Corporation
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2013 TangoTec Ltd.
|
||||
* Author: Baruch Siach <baruch@tkos.co.il>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Driver for the Xtensa LX4 GPIO32 Option
|
||||
*
|
||||
* Documentation: Xtensa LX4 Microprocessor Data Book, Section 2.22
|
||||
|
@ -30,7 +27,7 @@
|
|||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
/*
|
||||
* Memory layout:
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ACPI helpers for GPIO API
|
||||
*
|
||||
* Copyright (C) 2012, Intel Corporation
|
||||
* Authors: Mathias Nyman <mathias.nyman@linux.intel.com>
|
||||
* Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Device property helpers for GPIO chips.
|
||||
*
|
||||
* Copyright (C) 2016, Intel Corporation
|
||||
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/property.h>
|
||||
|
@ -32,32 +29,29 @@ void devprop_gpiochip_set_names(struct gpio_chip *chip,
|
|||
struct gpio_device *gdev = chip->gpiodev;
|
||||
const char **names;
|
||||
int ret, i;
|
||||
int count;
|
||||
|
||||
ret = fwnode_property_read_string_array(fwnode, "gpio-line-names",
|
||||
NULL, 0);
|
||||
if (ret < 0)
|
||||
count = fwnode_property_read_string_array(fwnode, "gpio-line-names",
|
||||
NULL, 0);
|
||||
if (count < 0)
|
||||
return;
|
||||
|
||||
if (ret != gdev->ngpio) {
|
||||
dev_warn(&gdev->dev,
|
||||
"names %d do not match number of GPIOs %d\n", ret,
|
||||
gdev->ngpio);
|
||||
return;
|
||||
}
|
||||
if (count > gdev->ngpio)
|
||||
count = gdev->ngpio;
|
||||
|
||||
names = kcalloc(gdev->ngpio, sizeof(*names), GFP_KERNEL);
|
||||
names = kcalloc(count, sizeof(*names), GFP_KERNEL);
|
||||
if (!names)
|
||||
return;
|
||||
|
||||
ret = fwnode_property_read_string_array(fwnode, "gpio-line-names",
|
||||
names, gdev->ngpio);
|
||||
names, count);
|
||||
if (ret < 0) {
|
||||
dev_warn(&gdev->dev, "failed to read GPIO line names\n");
|
||||
kfree(names);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < gdev->ngpio; i++)
|
||||
for (i = 0; i < count; i++)
|
||||
gdev->descs[i].name = names[i];
|
||||
|
||||
kfree(names);
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* drivers/gpio/devres.c - managed gpio resources
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* devres.c - managed gpio resources
|
||||
* This file is based on kernel/irq/devres.c
|
||||
*
|
||||
* Copyright (c) 2011 John Crispin <john@phrozen.org>
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* OF helpers for the GPIO API
|
||||
*
|
||||
* Copyright (c) 2007-2008 MontaVista Software, Inc.
|
||||
*
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
|
@ -58,7 +54,8 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
|
|||
}
|
||||
|
||||
static void of_gpio_flags_quirks(struct device_node *np,
|
||||
enum of_gpio_flags *flags)
|
||||
enum of_gpio_flags *flags,
|
||||
int index)
|
||||
{
|
||||
/*
|
||||
* Some GPIO fixed regulator quirks.
|
||||
|
@ -92,6 +89,51 @@ static void of_gpio_flags_quirks(struct device_node *np,
|
|||
pr_info("%s uses legacy open drain flag - update the DTS if you can\n",
|
||||
of_node_full_name(np));
|
||||
}
|
||||
|
||||
/*
|
||||
* Legacy handling of SPI active high chip select. If we have a
|
||||
* property named "cs-gpios" we need to inspect the child node
|
||||
* to determine if the flags should have inverted semantics.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_SPI_MASTER) &&
|
||||
of_property_read_bool(np, "cs-gpios")) {
|
||||
struct device_node *child;
|
||||
u32 cs;
|
||||
int ret;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
ret = of_property_read_u32(child, "reg", &cs);
|
||||
if (!ret)
|
||||
continue;
|
||||
if (cs == index) {
|
||||
/*
|
||||
* SPI children have active low chip selects
|
||||
* by default. This can be specified negatively
|
||||
* by just omitting "spi-cs-high" in the
|
||||
* device node, or actively by tagging on
|
||||
* GPIO_ACTIVE_LOW as flag in the device
|
||||
* tree. If the line is simultaneously
|
||||
* tagged as active low in the device tree
|
||||
* and has the "spi-cs-high" set, we get a
|
||||
* conflict and the "spi-cs-high" flag will
|
||||
* take precedence.
|
||||
*/
|
||||
if (of_property_read_bool(np, "spi-cs-high")) {
|
||||
if (*flags & OF_GPIO_ACTIVE_LOW) {
|
||||
pr_warn("%s GPIO handle specifies active low - ignored\n",
|
||||
of_node_full_name(np));
|
||||
*flags &= ~OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
} else {
|
||||
if (!(*flags & OF_GPIO_ACTIVE_LOW))
|
||||
pr_info("%s enforce active low on chipselect handle\n",
|
||||
of_node_full_name(np));
|
||||
*flags |= OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,7 +174,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
|||
goto out;
|
||||
|
||||
if (flags)
|
||||
of_gpio_flags_quirks(np, flags);
|
||||
of_gpio_flags_quirks(np, flags, index);
|
||||
|
||||
pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
|
||||
__func__, propname, np, index,
|
||||
|
@ -349,8 +391,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
|||
else if (of_property_read_bool(np, "output-high"))
|
||||
*dflags |= GPIOD_OUT_HIGH;
|
||||
else {
|
||||
pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n",
|
||||
desc_to_gpio(desc), np->name);
|
||||
pr_warn("GPIO line %d (%pOFn): no hogging state specified, bailing out\n",
|
||||
desc_to_gpio(desc), np);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/idr.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -444,11 +444,6 @@ static struct attribute *gpiochip_attrs[] = {
|
|||
};
|
||||
ATTRIBUTE_GROUPS(gpiochip);
|
||||
|
||||
static struct gpio_desc *gpio_to_valid_desc(int gpio)
|
||||
{
|
||||
return gpio_is_valid(gpio) ? gpio_to_desc(gpio) : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* /sys/class/gpio/export ... write-only
|
||||
* integer N ... number of GPIO to export (full access)
|
||||
|
@ -467,7 +462,7 @@ static ssize_t export_store(struct class *class,
|
|||
if (status < 0)
|
||||
goto done;
|
||||
|
||||
desc = gpio_to_valid_desc(gpio);
|
||||
desc = gpio_to_desc(gpio);
|
||||
/* reject invalid GPIOs */
|
||||
if (!desc) {
|
||||
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
|
||||
|
@ -514,7 +509,7 @@ static ssize_t unexport_store(struct class *class,
|
|||
if (status < 0)
|
||||
goto done;
|
||||
|
||||
desc = gpio_to_valid_desc(gpio);
|
||||
desc = gpio_to_desc(gpio);
|
||||
/* reject bogus commands (gpio_unexport ignores them) */
|
||||
if (!desc) {
|
||||
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Internal GPIO functions.
|
||||
*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef GPIOLIB_H
|
||||
|
@ -183,15 +180,26 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
|
|||
}
|
||||
#endif
|
||||
|
||||
struct gpio_array {
|
||||
struct gpio_desc **desc;
|
||||
unsigned int size;
|
||||
struct gpio_chip *chip;
|
||||
unsigned long *get_mask;
|
||||
unsigned long *set_mask;
|
||||
unsigned long invert_mask[];
|
||||
};
|
||||
|
||||
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
|
||||
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
||||
unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
|
||||
unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
|
||||
/* This is just passed between gpiolib and devres */
|
||||
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
|
||||
|
@ -214,6 +222,7 @@ struct gpio_desc {
|
|||
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
|
||||
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
|
||||
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
|
||||
#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
|
||||
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
|
||||
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
|
||||
|
||||
|
|
|
@ -22,18 +22,16 @@ struct gpiomux {
|
|||
struct i2c_mux_gpio_platform_data data;
|
||||
unsigned gpio_base;
|
||||
struct gpio_desc **gpios;
|
||||
int *values;
|
||||
};
|
||||
|
||||
static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
|
||||
{
|
||||
int i;
|
||||
DECLARE_BITMAP(values, BITS_PER_TYPE(val));
|
||||
|
||||
for (i = 0; i < mux->data.n_gpios; i++)
|
||||
mux->values[i] = (val >> i) & 1;
|
||||
values[0] = val;
|
||||
|
||||
gpiod_set_array_value_cansleep(mux->data.n_gpios,
|
||||
mux->gpios, mux->values);
|
||||
gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, NULL,
|
||||
values);
|
||||
}
|
||||
|
||||
static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
|
||||
|
@ -182,15 +180,13 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||
return -EPROBE_DEFER;
|
||||
|
||||
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
|
||||
mux->data.n_gpios * sizeof(*mux->gpios) +
|
||||
mux->data.n_gpios * sizeof(*mux->values), 0,
|
||||
mux->data.n_gpios * sizeof(*mux->gpios), 0,
|
||||
i2c_mux_gpio_select, NULL);
|
||||
if (!muxc) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_failed;
|
||||
}
|
||||
mux->gpios = muxc->priv;
|
||||
mux->values = (int *)(mux->gpios + mux->data.n_gpios);
|
||||
muxc->priv = mux;
|
||||
|
||||
platform_set_drvdata(pdev, muxc);
|
||||
|
|
|
@ -40,17 +40,21 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
|
|||
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
|
||||
|
||||
if (!IS_ERR(reset_gpios)) {
|
||||
int i, *values;
|
||||
unsigned long *values;
|
||||
int nvalues = reset_gpios->ndescs;
|
||||
|
||||
values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
|
||||
values = bitmap_alloc(nvalues, GFP_KERNEL);
|
||||
if (!values)
|
||||
return;
|
||||
|
||||
for (i = 0; i < nvalues; i++)
|
||||
values[i] = value;
|
||||
if (value)
|
||||
bitmap_fill(values, nvalues);
|
||||
else
|
||||
bitmap_zero(values, nvalues);
|
||||
|
||||
gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
|
||||
reset_gpios->info, values);
|
||||
|
||||
gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
|
||||
kfree(values);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,20 +17,18 @@
|
|||
|
||||
struct mux_gpio {
|
||||
struct gpio_descs *gpios;
|
||||
int *val;
|
||||
};
|
||||
|
||||
static int mux_gpio_set(struct mux_control *mux, int state)
|
||||
{
|
||||
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
|
||||
int i;
|
||||
DECLARE_BITMAP(values, BITS_PER_TYPE(state));
|
||||
|
||||
for (i = 0; i < mux_gpio->gpios->ndescs; i++)
|
||||
mux_gpio->val[i] = (state >> i) & 1;
|
||||
values[0] = state;
|
||||
|
||||
gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
|
||||
mux_gpio->gpios->desc,
|
||||
mux_gpio->val);
|
||||
mux_gpio->gpios->info, values);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -58,13 +56,11 @@ static int mux_gpio_probe(struct platform_device *pdev)
|
|||
if (pins < 0)
|
||||
return pins;
|
||||
|
||||
mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
|
||||
pins * sizeof(*mux_gpio->val));
|
||||
mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio));
|
||||
if (IS_ERR(mux_chip))
|
||||
return PTR_ERR(mux_chip);
|
||||
|
||||
mux_gpio = mux_chip_priv(mux_chip);
|
||||
mux_gpio->val = (int *)(mux_gpio + 1);
|
||||
mux_chip->ops = &mux_gpio_ops;
|
||||
|
||||
mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
|
||||
|
|
|
@ -20,23 +20,21 @@
|
|||
struct mdio_mux_gpio_state {
|
||||
struct gpio_descs *gpios;
|
||||
void *mux_handle;
|
||||
int values[];
|
||||
};
|
||||
|
||||
static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
|
||||
void *data)
|
||||
{
|
||||
struct mdio_mux_gpio_state *s = data;
|
||||
unsigned int n;
|
||||
DECLARE_BITMAP(values, BITS_PER_TYPE(desired_child));
|
||||
|
||||
if (current_child == desired_child)
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < s->gpios->ndescs; n++)
|
||||
s->values[n] = (desired_child >> n) & 1;
|
||||
values[0] = desired_child;
|
||||
|
||||
gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
|
||||
s->values);
|
||||
s->gpios->info, values);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,8 +49,7 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(gpios))
|
||||
return PTR_ERR(gpios);
|
||||
|
||||
s = devm_kzalloc(&pdev->dev, struct_size(s, values, gpios->ndescs),
|
||||
GFP_KERNEL);
|
||||
s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
|
||||
if (!s) {
|
||||
gpiod_put_array(gpios);
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -351,19 +351,20 @@ static int soc_common_pcmcia_config_skt(
|
|||
|
||||
if (ret == 0) {
|
||||
struct gpio_desc *descs[2];
|
||||
int values[2], n = 0;
|
||||
DECLARE_BITMAP(values, 2);
|
||||
int n = 0;
|
||||
|
||||
if (skt->gpio_reset) {
|
||||
descs[n] = skt->gpio_reset;
|
||||
values[n++] = !!(state->flags & SS_RESET);
|
||||
__assign_bit(n++, values, state->flags & SS_RESET);
|
||||
}
|
||||
if (skt->gpio_bus_enable) {
|
||||
descs[n] = skt->gpio_bus_enable;
|
||||
values[n++] = !!(state->flags & SS_OUTPUT_ENA);
|
||||
__assign_bit(n++, values, state->flags & SS_OUTPUT_ENA);
|
||||
}
|
||||
|
||||
if (n)
|
||||
gpiod_set_array_value_cansleep(n, descs, values);
|
||||
gpiod_set_array_value_cansleep(n, descs, NULL, values);
|
||||
|
||||
/*
|
||||
* This really needs a better solution. The IRQ
|
||||
|
|
|
@ -157,15 +157,13 @@ static const struct phy_ops gpio_usb_ops = {
|
|||
*/
|
||||
static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
|
||||
{
|
||||
int values[PHY_MDM6600_NR_CMD_LINES];
|
||||
int i;
|
||||
DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);
|
||||
|
||||
val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
|
||||
for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
|
||||
values[i] = (val & BIT(i)) >> i;
|
||||
values[0] = val;
|
||||
|
||||
gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
|
||||
ddata->cmd_gpios->desc, values);
|
||||
ddata->cmd_gpios->desc,
|
||||
ddata->cmd_gpios->info, values);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,7 +174,7 @@ static void phy_mdm6600_status(struct work_struct *work)
|
|||
{
|
||||
struct phy_mdm6600 *ddata;
|
||||
struct device *dev;
|
||||
int values[PHY_MDM6600_NR_STATUS_LINES];
|
||||
DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES);
|
||||
int error, i, val = 0;
|
||||
|
||||
ddata = container_of(work, struct phy_mdm6600, status_work.work);
|
||||
|
@ -184,16 +182,17 @@ static void phy_mdm6600_status(struct work_struct *work)
|
|||
|
||||
error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
|
||||
ddata->status_gpios->desc,
|
||||
ddata->status_gpios->info,
|
||||
values);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
|
||||
val |= values[i] << i;
|
||||
val |= test_bit(i, values) << i;
|
||||
dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
|
||||
__func__, i, values[i], val);
|
||||
__func__, i, test_bit(i, values), val);
|
||||
}
|
||||
ddata->status = val;
|
||||
ddata->status = values[0];
|
||||
|
||||
dev_info(dev, "modem status: %i %s\n",
|
||||
ddata->status,
|
||||
|
|
|
@ -601,6 +601,42 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
#define msm_gpio_dbg_show NULL
|
||||
#endif
|
||||
|
||||
static int msm_gpio_init_valid_mask(struct gpio_chip *chip)
|
||||
{
|
||||
struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
|
||||
int ret;
|
||||
unsigned int len, i;
|
||||
unsigned int max_gpios = pctrl->soc->ngpios;
|
||||
u16 *tmp;
|
||||
|
||||
/* The number of GPIOs in the ACPI tables */
|
||||
len = ret = device_property_read_u16_array(pctrl->dev, "gpios", NULL,
|
||||
0);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
if (ret > max_gpios)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = device_property_read_u16_array(pctrl->dev, "gpios", tmp, len);
|
||||
if (ret < 0) {
|
||||
dev_err(pctrl->dev, "could not read list of GPIOs\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
bitmap_zero(chip->valid_mask, max_gpios);
|
||||
for (i = 0; i < len; i++)
|
||||
set_bit(tmp[i], chip->valid_mask);
|
||||
|
||||
out:
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct gpio_chip msm_gpio_template = {
|
||||
.direction_input = msm_gpio_direction_input,
|
||||
.direction_output = msm_gpio_direction_output,
|
||||
|
@ -610,6 +646,7 @@ static const struct gpio_chip msm_gpio_template = {
|
|||
.request = gpiochip_generic_request,
|
||||
.free = gpiochip_generic_free,
|
||||
.dbg_show = msm_gpio_dbg_show,
|
||||
.init_valid_mask = msm_gpio_init_valid_mask,
|
||||
};
|
||||
|
||||
/* For dual-edge interrupts in software, since some hardware has no
|
||||
|
@ -925,41 +962,6 @@ static void msm_gpio_irq_handler(struct irq_desc *desc)
|
|||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static int msm_gpio_init_valid_mask(struct gpio_chip *chip,
|
||||
struct msm_pinctrl *pctrl)
|
||||
{
|
||||
int ret;
|
||||
unsigned int len, i;
|
||||
unsigned int max_gpios = pctrl->soc->ngpios;
|
||||
u16 *tmp;
|
||||
|
||||
/* The number of GPIOs in the ACPI tables */
|
||||
len = ret = device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
if (ret > max_gpios)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = device_property_read_u16_array(pctrl->dev, "gpios", tmp, len);
|
||||
if (ret < 0) {
|
||||
dev_err(pctrl->dev, "could not read list of GPIOs\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
bitmap_zero(chip->valid_mask, max_gpios);
|
||||
for (i = 0; i < len; i++)
|
||||
set_bit(tmp[i], chip->valid_mask);
|
||||
|
||||
out:
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
|
||||
{
|
||||
return device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0) > 0;
|
||||
|
@ -998,13 +1000,6 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = msm_gpio_init_valid_mask(chip, pctrl);
|
||||
if (ret) {
|
||||
dev_err(pctrl->dev, "Failed to setup irq valid bits\n");
|
||||
gpiochip_remove(&pctrl->chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* For DeviceTree-supported systems, the gpio core checks the
|
||||
* pinctrl's device node for the "gpio-ranges" property.
|
||||
|
|
|
@ -202,7 +202,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
long mask)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
int values[3];
|
||||
DECLARE_BITMAP(values, 3);
|
||||
int ret, i;
|
||||
|
||||
switch (mask) {
|
||||
|
@ -227,12 +227,10 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
values[0] = (ret >> 0) & 1;
|
||||
values[1] = (ret >> 1) & 1;
|
||||
values[2] = (ret >> 2) & 1;
|
||||
values[0] = ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
|
||||
gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
|
||||
values);
|
||||
st->oversampling = val;
|
||||
mutex_unlock(&st->lock);
|
||||
|
|
|
@ -40,7 +40,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
|
|||
{
|
||||
enum mctrl_gpio_idx i;
|
||||
struct gpio_desc *desc_array[UART_GPIO_MAX];
|
||||
int value_array[UART_GPIO_MAX];
|
||||
DECLARE_BITMAP(values, UART_GPIO_MAX);
|
||||
unsigned int count = 0;
|
||||
|
||||
if (gpios == NULL)
|
||||
|
@ -49,10 +49,11 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
|
|||
for (i = 0; i < UART_GPIO_MAX; i++)
|
||||
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
|
||||
desc_array[count] = gpios->gpio[i];
|
||||
value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
|
||||
__assign_bit(count, values,
|
||||
mctrl & mctrl_gpios_desc[i].mctrl);
|
||||
count++;
|
||||
}
|
||||
gpiod_set_array_value(count, desc_array, value_array);
|
||||
gpiod_set_array_value(count, desc_array, NULL, values);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctrl_gpio_set);
|
||||
|
||||
|
|
|
@ -17,11 +17,20 @@ struct device;
|
|||
*/
|
||||
struct gpio_desc;
|
||||
|
||||
/**
|
||||
* Opaque descriptor for a structure of GPIO array attributes. This structure
|
||||
* is attached to struct gpiod_descs obtained from gpiod_get_array() and can be
|
||||
* passed back to get/set array functions in order to activate fast processing
|
||||
* path if applicable.
|
||||
*/
|
||||
struct gpio_array;
|
||||
|
||||
/**
|
||||
* Struct containing an array of descriptors that can be obtained using
|
||||
* gpiod_get_array().
|
||||
*/
|
||||
struct gpio_descs {
|
||||
struct gpio_array *info;
|
||||
unsigned int ndescs;
|
||||
struct gpio_desc *desc[];
|
||||
};
|
||||
|
@ -105,36 +114,46 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
|
|||
/* Value get/set from non-sleeping context */
|
||||
int gpiod_get_value(const struct gpio_desc *desc);
|
||||
int gpiod_get_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array, int *value_array);
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
void gpiod_set_value(struct gpio_desc *desc, int value);
|
||||
void gpiod_set_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array, int *value_array);
|
||||
int gpiod_set_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
int gpiod_get_raw_value(const struct gpio_desc *desc);
|
||||
int gpiod_get_raw_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
|
||||
int gpiod_set_raw_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
|
||||
/* Value get/set from sleeping context */
|
||||
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
|
||||
int gpiod_get_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
|
||||
void gpiod_set_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
int gpiod_set_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
|
||||
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
|
||||
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
|
||||
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
|
||||
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
|
||||
|
@ -331,7 +350,8 @@ static inline int gpiod_get_value(const struct gpio_desc *desc)
|
|||
}
|
||||
static inline int gpiod_get_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
|
@ -342,12 +362,14 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value)
|
|||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
}
|
||||
static inline void gpiod_set_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
static inline int gpiod_set_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
static inline int gpiod_get_raw_value(const struct gpio_desc *desc)
|
||||
{
|
||||
|
@ -357,7 +379,8 @@ static inline int gpiod_get_raw_value(const struct gpio_desc *desc)
|
|||
}
|
||||
static inline int gpiod_get_raw_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
|
@ -369,8 +392,9 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value)
|
|||
WARN_ON(1);
|
||||
}
|
||||
static inline int gpiod_set_raw_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
struct gpio_desc **desc_array,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
|
@ -385,7 +409,8 @@ static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc)
|
|||
}
|
||||
static inline int gpiod_get_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
|
@ -396,12 +421,14 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
|||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
}
|
||||
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
|
||||
static inline int gpiod_set_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
|
||||
{
|
||||
|
@ -411,7 +438,8 @@ static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
|
|||
}
|
||||
static inline int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
|
@ -425,7 +453,8 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc,
|
|||
}
|
||||
static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
|
|
|
@ -66,9 +66,15 @@ struct gpio_irq_chip {
|
|||
/**
|
||||
* @lock_key:
|
||||
*
|
||||
* Per GPIO IRQ chip lockdep classes.
|
||||
* Per GPIO IRQ chip lockdep class for IRQ lock.
|
||||
*/
|
||||
struct lock_class_key *lock_key;
|
||||
|
||||
/**
|
||||
* @request_key:
|
||||
*
|
||||
* Per GPIO IRQ chip lockdep class for IRQ request.
|
||||
*/
|
||||
struct lock_class_key *request_key;
|
||||
|
||||
/**
|
||||
|
@ -145,6 +151,20 @@ struct gpio_irq_chip {
|
|||
* will allocate and map all IRQs during initialization.
|
||||
*/
|
||||
unsigned int first;
|
||||
|
||||
/**
|
||||
* @irq_enable:
|
||||
*
|
||||
* Store old irq_chip irq_enable callback
|
||||
*/
|
||||
void (*irq_enable)(struct irq_data *data);
|
||||
|
||||
/**
|
||||
* @irq_disable:
|
||||
*
|
||||
* Store old irq_chip irq_disable callback
|
||||
*/
|
||||
void (*irq_disable)(struct irq_data *data);
|
||||
};
|
||||
|
||||
static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip)
|
||||
|
@ -165,9 +185,13 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip)
|
|||
* @free: optional hook for chip-specific deactivation, such as
|
||||
* disabling module power and clock; may sleep
|
||||
* @get_direction: returns direction for signal "offset", 0=out, 1=in,
|
||||
* (same as GPIOF_DIR_XXX), or negative error
|
||||
* (same as GPIOF_DIR_XXX), or negative error.
|
||||
* It is recommended to always implement this function, even on
|
||||
* input-only or output-only gpio chips.
|
||||
* @direction_input: configures signal "offset" as input, or returns error
|
||||
* This can be omitted on input-only or output-only gpio chips.
|
||||
* @direction_output: configures signal "offset" as output, or returns error
|
||||
* This can be omitted on input-only or output-only gpio chips.
|
||||
* @get: returns value for signal "offset", 0=low, 1=high, or negative error
|
||||
* @get_multiple: reads values for multiple signals defined by "mask" and
|
||||
* stores them in "bits", returns 0 on success or negative error
|
||||
|
@ -263,6 +287,9 @@ struct gpio_chip {
|
|||
|
||||
void (*dbg_show)(struct seq_file *s,
|
||||
struct gpio_chip *chip);
|
||||
|
||||
int (*init_valid_mask)(struct gpio_chip *chip);
|
||||
|
||||
int base;
|
||||
u16 ngpio;
|
||||
const char *const *names;
|
||||
|
@ -301,7 +328,9 @@ struct gpio_chip {
|
|||
/**
|
||||
* @need_valid_mask:
|
||||
*
|
||||
* If set core allocates @valid_mask with all bits set to one.
|
||||
* If set core allocates @valid_mask with all its values initialized
|
||||
* with init_valid_mask() or set to one if init_valid_mask() is not
|
||||
* defined
|
||||
*/
|
||||
bool need_valid_mask;
|
||||
|
||||
|
@ -402,6 +431,10 @@ extern struct gpio_chip *gpiochip_find(void *data,
|
|||
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset);
|
||||
void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset);
|
||||
bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset);
|
||||
int gpiochip_reqres_irq(struct gpio_chip *chip, unsigned int offset);
|
||||
void gpiochip_relres_irq(struct gpio_chip *chip, unsigned int offset);
|
||||
void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset);
|
||||
void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset);
|
||||
|
||||
/* Line status inquiry for drivers */
|
||||
bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset);
|
||||
|
|
|
@ -16,46 +16,12 @@
|
|||
#ifndef __DAVINCI_GPIO_PLATFORM_H
|
||||
#define __DAVINCI_GPIO_PLATFORM_H
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm-generic/gpio.h>
|
||||
|
||||
#define MAX_REGS_BANKS 5
|
||||
#define MAX_INT_PER_BANK 32
|
||||
|
||||
struct davinci_gpio_platform_data {
|
||||
u32 ngpio;
|
||||
u32 gpio_unbanked;
|
||||
};
|
||||
|
||||
struct davinci_gpio_irq_data {
|
||||
void __iomem *regs;
|
||||
struct davinci_gpio_controller *chip;
|
||||
int bank_num;
|
||||
};
|
||||
|
||||
struct davinci_gpio_controller {
|
||||
struct gpio_chip chip;
|
||||
struct irq_domain *irq_domain;
|
||||
/* Serialize access to GPIO registers */
|
||||
spinlock_t lock;
|
||||
void __iomem *regs[MAX_REGS_BANKS];
|
||||
int gpio_unbanked;
|
||||
int irqs[MAX_INT_PER_BANK];
|
||||
unsigned int base;
|
||||
};
|
||||
|
||||
/*
|
||||
* basic gpio routines
|
||||
*/
|
||||
#define GPIO(X) (X) /* 0 <= X <= (DAVINCI_N_GPIO - 1) */
|
||||
|
||||
/* Convert GPIO signal to GPIO pin number */
|
||||
#define GPIO_TO_PIN(bank, gpio) (16 * (bank) + (gpio))
|
||||
|
||||
static inline u32 __gpio_mask(unsigned gpio)
|
||||
{
|
||||
return 1 << (gpio % 32);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -197,23 +197,12 @@ struct omap_gpio_platform_data {
|
|||
bool is_mpuio; /* whether the bank is of type MPUIO */
|
||||
u32 non_wakeup_gpios;
|
||||
|
||||
u32 quirks; /* Version specific quirks mask */
|
||||
|
||||
struct omap_gpio_reg_offs *regs;
|
||||
|
||||
/* Return context loss count due to PM states changing */
|
||||
int (*get_context_loss_count)(struct device *dev);
|
||||
};
|
||||
|
||||
#if IS_BUILTIN(CONFIG_GPIO_OMAP)
|
||||
extern void omap2_gpio_prepare_for_idle(int off_mode);
|
||||
extern void omap2_gpio_resume_after_idle(void);
|
||||
#else
|
||||
static inline void omap2_gpio_prepare_for_idle(int off_mode)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void omap2_gpio_resume_after_idle(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* GPIO (DIO) header for Technologic Systems TS-5500
|
||||
*
|
||||
* Copyright (c) 2012 Savoir-faire Linux Inc.
|
||||
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _PDATA_GPIO_TS5500_H
|
||||
#define _PDATA_GPIO_TS5500_H
|
||||
|
||||
/**
|
||||
* struct ts5500_dio_platform_data - TS-5500 pin block configuration
|
||||
* @base: The GPIO base number to use.
|
||||
* @strap: The only pin connected to an interrupt in a block is input-only.
|
||||
* If you need a bidirectional line which can trigger an IRQ, you
|
||||
* may strap it with an in/out pin. This flag indicates this case.
|
||||
*/
|
||||
struct ts5500_dio_platform_data {
|
||||
int base;
|
||||
bool strap;
|
||||
};
|
||||
|
||||
#endif /* _PDATA_GPIO_TS5500_H */
|
|
@ -65,7 +65,7 @@ struct gpioline_info {
|
|||
|
||||
/**
|
||||
* struct gpiohandle_request - Information about a GPIO handle request
|
||||
* @lineoffsets: an array desired lines, specified by offset index for the
|
||||
* @lineoffsets: an array of desired lines, specified by offset index for the
|
||||
* associated GPIO device
|
||||
* @flags: desired flags for the desired GPIO lines, such as
|
||||
* GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed
|
||||
|
|
Loading…
Reference in a new issue