Merge tag 'gpio-updates-for-v6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio updates from Bartosz Golaszewski:
 "There are two new drivers and support for more models in existing
  ones.

  The generic GPIO API has been reworked and all users converted
  which allowed us to move the fields specific to the generic GPIO
  implementation out of the high-level struct gpio_chip into its own
  structure that wraps the gpio_chip.

  Other than that, there's nothing too exciting. Mostly minor tweaks and
  fixes all over the place, some refactoring and some small new features
  in helper modules.

  GPIO core:
   - add support for sparse pin ranges to the glue between GPIO and
     pinctrl
   - use a common prefix across all GPIO descriptor flags for improved
     namespacing

  New drivers:
   - add new GPIO driver for the Nuvoton NCT6694
   - add new GPIO driver for MAX7360

  Driver improvements:
   - add support for Tegra 256 to the gpio-tegra186 driver
   - add support for Loongson-2K0300 to the gpio-loongson-64bit driver
   - refactor the gpio-aggregator module to expose its GPIO forwarder
     API to other in-kernel users (to enable merging of a new pinctrl
     driver that uses it)
   - convert all remaining drivers to using the modernized generic GPIO
     chip API and remove the old interface
   - stop displaying global GPIO numbers in debugfs output of controller
     drivers
   - extend the gpio-regmap helper with a new config option and improve
     its support for GPIO interrupts
   - remove redundant fast_io parameter from regmap configs in GPIO
     drivers that already use MMIO regmaps which imply it
   - add support for a new model in gpio-mmio: ixp4xx expansion bus
   - order includes alphabetically in a few drivers for better
     readability
   - use generic device properties where applicable
   - use devm_mutex_init() where applicable
   - extend build coverage of drivers by enabling more to be compiled
     with COMPILE_TEST enabled
   - allow building gpio-stmpe as a module
   - use dev_err_probe() where it makes sense in drivers

  Late driver fixes:
   - fix setting GPIO direction to output in gpio-mpfs

  Documentation:
   - document the usage of software nodes with GPIO chips

  Device-tree bindings:
   - Add DT bindings documents for new hardware: Tegra256, MAX7360
   - Document a new model in Loongson bindings: LS2K0300
   - Document a new model using the generic GPIO binding: IXP4xx
   - Convert the DT binding for fsl,mxs-pinctrl to YAML
   - fix the schema ID in the "trivial" GPIO schema
   - describe GPIO hogs in the generic GPIO binding"

* tag 'gpio-updates-for-v6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (122 commits)
  gpio: mpfs: fix setting gpio direction to output
  gpio: generic: move GPIO_GENERIC_ flags to the correct header
  gpio: generic: rename BGPIOF_ flags to GPIO_GENERIC_
  gpio: nomadik: fix the debugfs helper stub
  MAINTAINERS: Add entry on MAX7360 driver
  input: misc: Add support for MAX7360 rotary
  input: keyboard: Add support for MAX7360 keypad
  gpio: max7360: Add MAX7360 gpio support
  gpio: regmap: Allow to provide init_valid_mask callback
  gpio: regmap: Allow to allocate regmap-irq device
  pwm: max7360: Add MAX7360 PWM support
  pinctrl: Add MAX7360 pinctrl driver
  mfd: Add max7360 support
  dt-bindings: mfd: gpio: Add MAX7360
  rtc: Add Nuvoton NCT6694 RTC support
  hwmon: Add Nuvoton NCT6694 HWMON support
  watchdog: Add Nuvoton NCT6694 WDT support
  can: Add Nuvoton NCT6694 CANFD support
  i2c: Add Nuvoton NCT6694 I2C support
  gpio: Add Nuvoton NCT6694 GPIO support
  ...
This commit is contained in:
Linus Torvalds
2025-10-01 11:34:12 -07:00
124 changed files with 8579 additions and 1783 deletions
@@ -22,6 +22,7 @@ properties:
- brcm,bcm6345-gpio
- ni,169445-nand-gpio
- wd,mbl-gpio # Western Digital MyBook Live memory-mapped GPIO controller
- intel,ixp4xx-expansion-bus-mmio-gpio
big-endian: true
@@ -89,6 +90,20 @@ properties:
description:
If this property is present, the controller cannot drive the GPIO lines.
if:
properties:
compatible:
contains:
const: intel,ixp4xx-expansion-bus-mmio-gpio
then:
$ref: /schemas/memory-controllers/intel,ixp4xx-expansion-peripheral-props.yaml#
patternProperties:
"^.+-hog(-[0-9]+)?$":
type: object
required:
- gpio-hog
required:
- compatible
- reg
@@ -96,7 +111,7 @@ required:
- '#gpio-cells'
- gpio-controller
additionalProperties: false
unevaluatedProperties: false
examples:
- |
@@ -126,3 +141,22 @@ examples:
gpio-controller;
#gpio-cells = <2>;
};
bus@c4000000 {
compatible = "intel,ixp42x-expansion-bus-controller", "syscon";
reg = <0xc4000000 0x30>;
native-endian;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0x0 0x50000000 0x01000000>;
dma-ranges = <0 0x0 0x50000000 0x01000000>;
gpio@1,0 {
compatible = "intel,ixp4xx-expansion-bus-mmio-gpio";
gpio-controller;
#gpio-cells = <2>;
big-endian;
reg = <1 0x00000000 0x2>;
reg-names = "dat";
intel,ixp4xx-eb-write-enable = <1>;
};
};
@@ -18,9 +18,13 @@ description: |
properties:
compatible:
enum:
- fsl,imx23-pinctrl
- fsl,imx28-pinctrl
items:
- enum:
- fsl,imx23-pinctrl
- fsl,imx28-pinctrl
# Over 10 years old devices, driver use simple-bus to probe child gpio
# Devices. Keep it as it to be compatible existed dts files.
- const: simple-bus
'#address-cells':
const: 1
@@ -31,7 +35,65 @@ properties:
maxItems: 1
patternProperties:
"gpio@[0-9]+$":
"^(?!gpio@)[^@]+@[0-9]+$":
type: object
properties:
fsl,pinmux-ids:
$ref: /schemas/types.yaml#/definitions/uint32-array
description: |
An integer array. Each integer in the array specify a pin
with given mux function, with bank, pin and mux packed as below.
[15..12] : bank number
[11..4] : pin number
[3..0] : mux selection
This integer with mux selection packed is used as an entity by both group
and config nodes to identify a pin. The mux selection in the integer takes
effects only on group node, and will get ignored by driver with config node,
since config node is only meant to set up pin configurations.
Valid values for these integers are listed below.
reg:
items:
- description: |
pin group index. NOTE: it is supposed wrong use reg property
here. But it is over 10 years devices. Just keep it as it.
fsl,drive-strength:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
description: |
0: MXS_DRIVE_4mA
1: MXS_DRIVE_8mA
2: MXS_DRIVE_12mA
3: MXS_DRIVE_16mA
fsl,voltage:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
description: |
0: MXS_VOLTAGE_LOW - 1.8 V
1: MXS_VOLTAGE_HIGH - 3.3 V
fsl,pull-up:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
description: |
0: MXS_PULL_DISABLE - Disable the internal pull-up
1: MXS_PULL_ENABLE - Enable the internal pull-up
Note that when enabling the pull-up, the internal pad keeper gets disabled.
Also, some pins doesn't have a pull up, in that case, setting the fsl,pull-up
will only disable the internal pad keeper.
required:
- fsl,pinmux-ids
additionalProperties: false
"^gpio@[0-9]+$":
type: object
properties:
compatible:
@@ -80,7 +142,7 @@ examples:
pinctrl@80018000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-pinctrl";
compatible = "fsl,imx28-pinctrl", "simple-bus";
reg = <0x80018000 0x2000>;
gpio@0 {
@@ -132,4 +194,12 @@ examples:
interrupt-controller;
#interrupt-cells = <2>;
};
lcdif-apx4@5 {
reg = <5>;
fsl,pinmux-ids = <0x1181 0x1191>;
fsl,drive-strength = <0>;
fsl,voltage = <0>;
fsl,pull-up = <0>;
};
};
@@ -14,6 +14,7 @@ properties:
oneOf:
- enum:
- loongson,ls2k-gpio
- loongson,ls2k0300-gpio
- loongson,ls2k0500-gpio0
- loongson,ls2k0500-gpio1
- loongson,ls2k2000-gpio0
@@ -36,7 +37,7 @@ properties:
ngpios:
minimum: 1
maximum: 64
maximum: 128
"#gpio-cells":
const: 2
@@ -49,6 +50,14 @@ properties:
minItems: 1
maxItems: 64
"#interrupt-cells":
const: 2
interrupt-controller: true
resets:
maxItems: 1
required:
- compatible
- reg
@@ -58,6 +67,23 @@ required:
- gpio-ranges
- interrupts
allOf:
- if:
properties:
compatible:
contains:
const: loongson,ls2k0300-gpio
then:
required:
- "#interrupt-cells"
- interrupt-controller
- resets
else:
properties:
"#interrupts-cells": false
interrupt-controller: false
resets: false
additionalProperties: false
examples:
@@ -95,9 +95,9 @@ examples:
#gpio-cells = <2>;
maxim,modesel-gpios = <&gpio2 23>;
maxim,fault-gpios = <&gpio2 24 GPIO_ACTIVE_LOW>;
maxim,db0-gpios = <&gpio2 25>;
maxim,db1-gpios = <&gpio2 26>;
maxim,fault-gpios = <&gpio2 24 GPIO_ACTIVE_LOW>;
maxim,db0-gpios = <&gpio2 25>;
maxim,db1-gpios = <&gpio2 26>;
spi-max-frequency = <25000000>;
};
@@ -0,0 +1,83 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/maxim,max7360-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim MAX7360 GPIO controller
maintainers:
- Kamel Bouhara <kamel.bouhara@bootlin.com>
- Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
description: |
Maxim MAX7360 GPIO controller, in MAX7360 chipset
https://www.analog.com/en/products/max7360.html
The device provides two series of GPIOs, referred here as GPIOs and GPOs.
PORT0 to PORT7 pins can be used as GPIOs, with support for interrupts and
constant-current mode. These pins will also be used by the rotary encoder and
PWM functionalities.
COL2 to COL7 pins can be used as GPOs, there is no input capability. COL pins
will be partitioned, with the first pins being affected to the keypad
functionality and the last ones as GPOs.
properties:
compatible:
enum:
- maxim,max7360-gpio
- maxim,max7360-gpo
gpio-controller: true
"#gpio-cells":
const: 2
interrupt-controller: true
"#interrupt-cells":
const: 2
maxim,constant-current-disable:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Bit field, each bit disables constant-current output of the associated
GPIO, starting from the least significant bit for the first GPIO.
maximum: 0xff
required:
- compatible
- gpio-controller
allOf:
- if:
properties:
compatible:
contains:
enum:
- maxim,max7360-gpio
ngpios: false
then:
required:
- interrupt-controller
else:
properties:
interrupt-controller: false
maxim,constant-current-disable: false
additionalProperties: false
examples:
- |
gpio {
compatible = "maxim,max7360-gpio";
gpio-controller;
#gpio-cells = <2>;
maxim,constant-current-disable = <0x06>;
interrupt-controller;
#interrupt-cells = <2>;
};
@@ -85,6 +85,7 @@ properties:
- nvidia,tegra194-gpio-aon
- nvidia,tegra234-gpio
- nvidia,tegra234-gpio-aon
- nvidia,tegra256-gpio
reg-names:
items:
@@ -155,6 +156,7 @@ allOf:
- nvidia,tegra186-gpio
- nvidia,tegra194-gpio
- nvidia,tegra234-gpio
- nvidia,tegra256-gpio
then:
properties:
interrupts:
@@ -80,7 +80,7 @@ examples:
gpio@d4019000 {
compatible = "spacemit,k1-gpio";
reg = <0xd4019000 0x800>;
clocks =<&ccu 9>, <&ccu 61>;
clocks = <&ccu 9>, <&ccu 61>;
clock-names = "core", "bus";
gpio-controller;
#gpio-cells = <3>;
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/trivial-gpio.yaml#
$id: http://devicetree.org/schemas/gpio/trivial-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Trivial 2-cell GPIO controllers
@@ -0,0 +1,191 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/maxim,max7360.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim MAX7360 Keypad, Rotary encoder, PWM and GPIO controller
maintainers:
- Kamel Bouhara <kamel.bouhara@bootlin.com>
- Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
description: |
Maxim MAX7360 device, with following functions:
- keypad controller
- rotary controller
- GPIO and GPO controller
- PWM controller
https://www.analog.com/en/products/max7360.html
allOf:
- $ref: /schemas/input/matrix-keymap.yaml#
- $ref: /schemas/input/input.yaml#
properties:
compatible:
enum:
- maxim,max7360
reg:
maxItems: 1
interrupts:
maxItems: 2
interrupt-names:
items:
- const: inti
- const: intk
keypad-debounce-delay-ms:
description: Keypad debounce delay in ms
minimum: 9
maximum: 40
default: 9
rotary-debounce-delay-ms:
description: Rotary encoder debounce delay in ms
minimum: 0
maximum: 15
default: 0
linux,axis:
$ref: /schemas/input/rotary-encoder.yaml#/properties/linux,axis
rotary-encoder,relative-axis:
$ref: /schemas/types.yaml#/definitions/flag
description:
Register a relative axis rather than an absolute one.
rotary-encoder,steps:
$ref: /schemas/types.yaml#/definitions/uint32
default: 24
description:
Number of steps in a full turnaround of the
encoder. Only relevant for absolute axis. Defaults to 24 which is a
typical value for such devices.
rotary-encoder,rollover:
$ref: /schemas/types.yaml#/definitions/flag
description:
Automatic rollover when the rotary value becomes
greater than the specified steps or smaller than 0. For absolute axis only.
"#pwm-cells":
const: 3
gpio:
$ref: /schemas/gpio/maxim,max7360-gpio.yaml#
description:
PORT0 to PORT7 general purpose input/output pins configuration.
gpo:
$ref: /schemas/gpio/maxim,max7360-gpio.yaml#
description: >
COL2 to COL7 general purpose output pins configuration. Allows to use
unused keypad columns as outputs.
The MAX7360 has 8 column lines and 6 of them can be used as GPOs. GPIOs
numbers used for this gpio-controller node do correspond to the column
numbers: values 0 and 1 are never valid, values from 2 to 7 might be valid
depending on the value of the keypad,num-column property.
patternProperties:
'-pins$':
type: object
description:
Pinctrl node's client devices use subnodes for desired pin configuration.
Client device subnodes use below standard properties.
$ref: /schemas/pinctrl/pincfg-node.yaml
properties:
pins:
description:
List of gpio pins affected by the properties specified in this
subnode.
items:
pattern: '^(PORT[0-7]|ROTARY)$'
minItems: 1
maxItems: 8
function:
description:
Specify the alternative function to be configured for the specified
pins.
enum: [gpio, pwm, rotary]
additionalProperties: false
required:
- compatible
- reg
- interrupts
- interrupt-names
- linux,keymap
- linux,axis
- "#pwm-cells"
- gpio
- gpo
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
io-expander@38 {
compatible = "maxim,max7360";
reg = <0x38>;
interrupt-parent = <&gpio1>;
interrupts = <23 IRQ_TYPE_LEVEL_LOW>,
<24 IRQ_TYPE_LEVEL_LOW>;
interrupt-names = "inti", "intk";
keypad,num-rows = <8>;
keypad,num-columns = <4>;
linux,keymap = <
MATRIX_KEY(0x00, 0x00, KEY_F5)
MATRIX_KEY(0x01, 0x00, KEY_F4)
MATRIX_KEY(0x02, 0x01, KEY_F6)
>;
keypad-debounce-delay-ms = <10>;
autorepeat;
rotary-debounce-delay-ms = <2>;
linux,axis = <0>; /* REL_X */
rotary-encoder,relative-axis;
#pwm-cells = <3>;
max7360_gpio: gpio {
compatible = "maxim,max7360-gpio";
gpio-controller;
#gpio-cells = <2>;
maxim,constant-current-disable = <0x06>;
interrupt-controller;
#interrupt-cells = <0x2>;
};
max7360_gpo: gpo {
compatible = "maxim,max7360-gpo";
gpio-controller;
#gpio-cells = <2>;
};
backlight_pins: backlight-pins {
pins = "PORT2";
function = "pwm";
};
};
};
@@ -1,127 +0,0 @@
* Freescale MXS Pin Controller
The pins controlled by mxs pin controller are organized in banks, each bank
has 32 pins. Each pin has 4 multiplexing functions, and generally, the 4th
function is GPIO. The configuration on the pins includes drive strength,
voltage and pull-up.
Required properties:
- compatible: "fsl,imx23-pinctrl" or "fsl,imx28-pinctrl"
- reg: Should contain the register physical address and length for the
pin controller.
Please refer to pinctrl-bindings.txt in this directory for details of the
common pinctrl bindings used by client devices.
The node of mxs pin controller acts as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for
a group of pins, and only affects those parameters that are explicitly listed.
In other words, a subnode that describes a drive strength parameter implies no
information about pull-up. For this reason, even seemingly boolean values are
actually tristates in this binding: unspecified, off, or on. Unspecified is
represented as an absent property, and off/on are represented as integer
values 0 and 1.
Those subnodes under mxs pin controller node will fall into two categories.
One is to set up a group of pins for a function, both mux selection and pin
configurations, and it's called group node in the binding document. The other
one is to adjust the pin configuration for some particular pins that need a
different configuration than what is defined in group node. The binding
document calls this type of node config node.
On mxs, there is no hardware pin group. The pin group in this binding only
means a group of pins put together for particular peripheral to work in
particular function, like SSP0 functioning as mmc0-8bit. That said, the
group node should include all the pins needed for one function rather than
having these pins defined in several group nodes. It also means each of
"pinctrl-*" phandle in client device node should only have one group node
pointed in there, while the phandle can have multiple config node referenced
there to adjust configurations for some pins in the group.
Required subnode-properties:
- fsl,pinmux-ids: An integer array. Each integer in the array specify a pin
with given mux function, with bank, pin and mux packed as below.
[15..12] : bank number
[11..4] : pin number
[3..0] : mux selection
This integer with mux selection packed is used as an entity by both group
and config nodes to identify a pin. The mux selection in the integer takes
effects only on group node, and will get ignored by driver with config node,
since config node is only meant to set up pin configurations.
Valid values for these integers are listed below.
- reg: Should be the index of the group nodes for same function. This property
is required only for group nodes, and should not be present in any config
nodes.
Optional subnode-properties:
- fsl,drive-strength: Integer.
0: MXS_DRIVE_4mA
1: MXS_DRIVE_8mA
2: MXS_DRIVE_12mA
3: MXS_DRIVE_16mA
- fsl,voltage: Integer.
0: MXS_VOLTAGE_LOW - 1.8 V
1: MXS_VOLTAGE_HIGH - 3.3 V
- fsl,pull-up: Integer.
0: MXS_PULL_DISABLE - Disable the internal pull-up
1: MXS_PULL_ENABLE - Enable the internal pull-up
Note that when enabling the pull-up, the internal pad keeper gets disabled.
Also, some pins doesn't have a pull up, in that case, setting the fsl,pull-up
will only disable the internal pad keeper.
Examples:
pinctrl@80018000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-pinctrl";
reg = <0x80018000 2000>;
mmc0_8bit_pins_a: mmc0-8bit@0 {
reg = <0>;
fsl,pinmux-ids = <
MX28_PAD_SSP0_DATA0__SSP0_D0
MX28_PAD_SSP0_DATA1__SSP0_D1
MX28_PAD_SSP0_DATA2__SSP0_D2
MX28_PAD_SSP0_DATA3__SSP0_D3
MX28_PAD_SSP0_DATA4__SSP0_D4
MX28_PAD_SSP0_DATA5__SSP0_D5
MX28_PAD_SSP0_DATA6__SSP0_D6
MX28_PAD_SSP0_DATA7__SSP0_D7
MX28_PAD_SSP0_CMD__SSP0_CMD
MX28_PAD_SSP0_DETECT__SSP0_CARD_DETECT
MX28_PAD_SSP0_SCK__SSP0_SCK
>;
fsl,drive-strength = <MXS_DRIVE_4mA>;
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_ENABLE>;
};
mmc_cd_cfg: mmc-cd-cfg {
fsl,pinmux-ids = <MX28_PAD_SSP0_DETECT__SSP0_CARD_DETECT>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
mmc_sck_cfg: mmc-sck-cfg {
fsl,pinmux-ids = <MX28_PAD_SSP0_SCK__SSP0_SCK>;
fsl,drive-strength = <MXS_DRIVE_12mA>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
};
In this example, group node mmc0-8bit defines a group of pins for mxs SSP0
to function as a 8-bit mmc device, with 8mA, 3.3V and pull-up configurations
applied on all these pins. And config nodes mmc-cd-cfg and mmc-sck-cfg are
adjusting the configuration for pins card-detection and clock from what group
node mmc0-8bit defines. Only the configuration properties to be adjusted need
to be listed in the config nodes.
Valid values for i.MX28/i.MX23 pinmux-id are defined in
arch/arm/boot/dts/imx28-pinfunc.h and arch/arm/boot/dts/imx23-pinfunc.h.
The definitions for the padconfig properties can be found in
arch/arm/boot/dts/mxs-pinfunc.h.
+65
View File
@@ -94,6 +94,71 @@ with the help of _DSD (Device Specific Data), introduced in ACPI 5.1::
For more information about the ACPI GPIO bindings see
Documentation/firmware-guide/acpi/gpio-properties.rst.
Software Nodes
--------------
Software nodes allow board-specific code to construct an in-memory,
device-tree-like structure using struct software_node and struct
property_entry. This structure can then be associated with a platform device,
allowing drivers to use the standard device properties API to query
configuration, just as they would on an ACPI or device tree system.
Software-node-backed GPIOs are described using the ``PROPERTY_ENTRY_GPIO()``
macro, which ties a software node representing the GPIO controller with
consumer device. It allows consumers to use regular gpiolib APIs, such as
gpiod_get(), gpiod_get_optional().
The software node representing a GPIO controller need not be attached to the
GPIO controller device. The only requirement is that the node must be
registered and its name must match the GPIO controller's label.
For example, here is how to describe a single GPIO-connected LED. This is an
alternative to using platform_data on legacy systems.
.. code-block:: c
#include <linux/property.h>
#include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
/*
* 1. Define a node for the GPIO controller. Its .name must match the
* controller's label.
*/
static const struct software_node gpio_controller_node = {
.name = "gpio-foo",
};
/* 2. Define the properties for the LED device. */
static const struct property_entry led_device_props[] = {
PROPERTY_ENTRY_STRING("label", "myboard:green:status"),
PROPERTY_ENTRY_STRING("linux,default-trigger", "heartbeat"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_controller_node, 42, GPIO_ACTIVE_HIGH),
{ }
};
/* 3. Define the software node for the LED device. */
static const struct software_node led_device_swnode = {
.name = "status-led",
.properties = led_device_props,
};
/*
* 4. Register the software nodes and the platform device.
*/
const struct software_node *swnodes[] = {
&gpio_controller_node,
&led_device_swnode,
NULL
};
software_node_register_node_group(swnodes);
// Then register a platform_device for "leds-gpio" and associate
// it with &led_device_swnode via .fwnode.
For a complete guide on converting board files to use software nodes, see
Documentation/driver-api/gpio/legacy-boards.rst.
Platform Data
-------------
Finally, GPIOs can be bound to devices and functions using platform data. Board
+1
View File
@@ -12,6 +12,7 @@ Contents:
driver
consumer
board
legacy-boards
drivers-on-gpio
bt8xxgpio
@@ -0,0 +1,298 @@
Supporting Legacy Boards
========================
Many drivers in the kernel, such as ``leds-gpio`` and ``gpio-keys``, are
migrating away from using board-specific ``platform_data`` to a unified device
properties interface. This interface allows drivers to be simpler and more
generic, as they can query properties in a standardized way.
On modern systems, these properties are provided via device tree. However, some
older platforms have not been converted to device tree and instead rely on
board files to describe their hardware configuration. To bridge this gap and
allow these legacy boards to work with modern, generic drivers, the kernel
provides a mechanism called **software nodes**.
This document provides a guide on how to convert a legacy board file from using
``platform_data`` and ``gpiod_lookup_table`` to the modern software node
approach for describing GPIO-connected devices.
The Core Idea: Software Nodes
-----------------------------
Software nodes allow board-specific code to construct an in-memory,
device-tree-like structure using struct software_node and struct
property_entry. This structure can then be associated with a platform device,
allowing drivers to use the standard device properties API (e.g.,
device_property_read_u32(), device_property_read_string()) to query
configuration, just as they would on an ACPI or device tree system.
The gpiolib code has support for handling software nodes, so that if GPIO is
described properly, as detailed in the section below, then regular gpiolib APIs,
such as gpiod_get(), gpiod_get_optional(), and others will work.
Requirements for GPIO Properties
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using software nodes to describe GPIO connections, the following
requirements must be met for the GPIO core to correctly resolve the reference:
1. **The GPIO controller's software node "name" must match the controller's
"label".** The gpiolib core uses this name to find the corresponding
struct gpio_chip at runtime.
This software node has to be registered, but need not be attached to the
device representing the GPIO controller that is providing the GPIO in
question. It may be left as a "free floating" node.
2. **The GPIO property must be a reference.** The ``PROPERTY_ENTRY_GPIO()``
macro handles this as it is an alias for ``PROPERTY_ENTRY_REF()``.
3. **The reference must have exactly two arguments:**
- The first argument is the GPIO offset within the controller.
- The second argument is the flags for the GPIO line (e.g.,
GPIO_ACTIVE_HIGH, GPIO_ACTIVE_LOW).
The ``PROPERTY_ENTRY_GPIO()`` macro is the preferred way of defining GPIO
properties in software nodes.
Conversion Example
------------------
Let's walk through an example of converting a board file that defines a GPIO-
connected LED and a button.
Before: Using Platform Data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A typical legacy board file might look like this:
.. code-block:: c
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio_keys.h>
#include <linux/gpio/machine.h>
#define MYBOARD_GPIO_CONTROLLER "gpio-foo"
/* LED setup */
static const struct gpio_led myboard_leds[] = {
{
.name = "myboard:green:status",
.default_trigger = "heartbeat",
},
};
static const struct gpio_led_platform_data myboard_leds_pdata = {
.num_leds = ARRAY_SIZE(myboard_leds),
.leds = myboard_leds,
};
static struct gpiod_lookup_table myboard_leds_gpios = {
.dev_id = "leds-gpio",
.table = {
GPIO_LOOKUP_IDX(MYBOARD_GPIO_CONTROLLER, 42, NULL, 0, GPIO_ACTIVE_HIGH),
{ },
},
};
/* Button setup */
static struct gpio_keys_button myboard_buttons[] = {
{
.code = KEY_WPS_BUTTON,
.desc = "WPS Button",
.active_low = 1,
},
};
static const struct gpio_keys_platform_data myboard_buttons_pdata = {
.buttons = myboard_buttons,
.nbuttons = ARRAY_SIZE(myboard_buttons),
};
static struct gpiod_lookup_table myboard_buttons_gpios = {
.dev_id = "gpio-keys",
.table = {
GPIO_LOOKUP_IDX(MYBOARD_GPIO_CONTROLLER, 15, NULL, 0, GPIO_ACTIVE_LOW),
{ },
},
};
/* Device registration */
static int __init myboard_init(void)
{
gpiod_add_lookup_table(&myboard_leds_gpios);
gpiod_add_lookup_table(&myboard_buttons_gpios);
platform_device_register_data(NULL, "leds-gpio", -1,
&myboard_leds_pdata, sizeof(myboard_leds_pdata));
platform_device_register_data(NULL, "gpio-keys", -1,
&myboard_buttons_pdata, sizeof(myboard_buttons_pdata));
return 0;
}
After: Using Software Nodes
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is how the same configuration can be expressed using software nodes.
Step 1: Define the GPIO Controller Node
***************************************
First, define a software node that represents the GPIO controller that the
LEDs and buttons are connected to. The ``name`` of this node must match the
name of the driver for the GPIO controller (e.g., "gpio-foo").
.. code-block:: c
#include <linux/property.h>
#include <linux/gpio/property.h>
#define MYBOARD_GPIO_CONTROLLER "gpio-foo"
static const struct software_node myboard_gpio_controller_node = {
.name = MYBOARD_GPIO_CONTROLLER,
};
Step 2: Define Consumer Device Nodes and Properties
***************************************************
Next, define the software nodes for the consumer devices (the LEDs and buttons).
This involves creating a parent node for each device type and child nodes for
each individual LED or button.
.. code-block:: c
/* LED setup */
static const struct software_node myboard_leds_node = {
.name = "myboard-leds",
};
static const struct property_entry myboard_status_led_props[] = {
PROPERTY_ENTRY_STRING("label", "myboard:green:status"),
PROPERTY_ENTRY_STRING("linux,default-trigger", "heartbeat"),
PROPERTY_ENTRY_GPIO("gpios", &myboard_gpio_controller_node, 42, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node myboard_status_led_swnode = {
.name = "status-led",
.parent = &myboard_leds_node,
.properties = myboard_status_led_props,
};
/* Button setup */
static const struct software_node myboard_keys_node = {
.name = "myboard-keys",
};
static const struct property_entry myboard_wps_button_props[] = {
PROPERTY_ENTRY_STRING("label", "WPS Button"),
PROPERTY_ENTRY_U32("linux,code", KEY_WPS_BUTTON),
PROPERTY_ENTRY_GPIO("gpios", &myboard_gpio_controller_node, 15, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node myboard_wps_button_swnode = {
.name = "wps-button",
.parent = &myboard_keys_node,
.properties = myboard_wps_button_props,
};
Step 3: Group and Register the Nodes
************************************
For maintainability, it is often beneficial to group all software nodes into a
single array and register them with one call.
.. code-block:: c
static const struct software_node * const myboard_swnodes[] = {
&myboard_gpio_controller_node,
&myboard_leds_node,
&myboard_status_led_swnode,
&myboard_keys_node,
&myboard_wps_button_swnode,
NULL
};
static int __init myboard_init(void)
{
int error;
error = software_node_register_node_group(myboard_swnodes);
if (error) {
pr_err("Failed to register software nodes: %d\n", error);
return error;
}
// ... platform device registration follows
}
.. note::
When splitting registration of nodes by devices that they represent, it is
essential that the software node representing the GPIO controller itself
is registered first, before any of the nodes that reference it.
Step 4: Register Platform Devices with Software Nodes
*****************************************************
Finally, register the platform devices and associate them with their respective
software nodes using the ``fwnode`` field in struct platform_device_info.
.. code-block:: c
static struct platform_device *leds_pdev;
static struct platform_device *keys_pdev;
static int __init myboard_init(void)
{
struct platform_device_info pdev_info;
int error;
error = software_node_register_node_group(myboard_swnodes);
if (error)
return error;
memset(&pdev_info, 0, sizeof(pdev_info));
pdev_info.name = "leds-gpio";
pdev_info.id = PLATFORM_DEVID_NONE;
pdev_info.fwnode = software_node_fwnode(&myboard_leds_node);
leds_pdev = platform_device_register_full(&pdev_info);
if (IS_ERR(leds_pdev)) {
error = PTR_ERR(leds_pdev);
goto err_unregister_nodes;
}
memset(&pdev_info, 0, sizeof(pdev_info));
pdev_info.name = "gpio-keys";
pdev_info.id = PLATFORM_DEVID_NONE;
pdev_info.fwnode = software_node_fwnode(&myboard_keys_node);
keys_pdev = platform_device_register_full(&pdev_info);
if (IS_ERR(keys_pdev)) {
error = PTR_ERR(keys_pdev);
platform_device_unregister(leds_pdev);
goto err_unregister_nodes;
}
return 0;
err_unregister_nodes:
software_node_unregister_node_group(myboard_swnodes);
return error;
}
static void __exit myboard_exit(void)
{
platform_device_unregister(keys_pdev);
platform_device_unregister(leds_pdev);
software_node_unregister_node_group(myboard_swnodes);
}
With these changes, the generic ``leds-gpio`` and ``gpio-keys`` drivers will
be able to probe successfully and get their configuration from the properties
defined in the software nodes, removing the need for board-specific platform
data.
+25
View File
@@ -15065,6 +15065,19 @@ L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/temperature/max30208.c
MAXIM MAX7360 KEYPAD LED MFD DRIVER
M: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
S: Maintained
F: Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
F: Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
F: drivers/gpio/gpio-max7360.c
F: drivers/input/keyboard/max7360-keypad.c
F: drivers/input/misc/max7360-rotary.c
F: drivers/mfd/max7360.c
F: drivers/pinctrl/pinctrl-max7360.c
F: drivers/pwm/pwm-max7360.c
F: include/linux/mfd/max7360.h
MAXIM MAX77650 PMIC MFD DRIVER
M: Bartosz Golaszewski <brgl@bgdev.pl>
L: linux-kernel@vger.kernel.org
@@ -18162,6 +18175,18 @@ F: drivers/nubus/
F: include/linux/nubus.h
F: include/uapi/linux/nubus.h
NUVOTON NCT6694 MFD DRIVER
M: Ming Yu <tmyu0@nuvoton.com>
S: Supported
F: drivers/gpio/gpio-nct6694.c
F: drivers/hwmon/nct6694-hwmon.c
F: drivers/i2c/busses/i2c-nct6694.c
F: drivers/mfd/nct6694.c
F: drivers/net/can/usb/nct6694_canfd.c
F: drivers/rtc/rtc-nct6694.c
F: drivers/watchdog/nct6694_wdt.c
F: include/linux/mfd/nct6694.h
NUVOTON NCT7201 IIO DRIVER
M: Eason Yang <j2anfernee@gmail.com>
L: linux-iio@vger.kernel.org
+39 -10
View File
@@ -303,7 +303,7 @@ config GPIO_EN7523
config GPIO_EP93XX
def_bool y
depends on ARCH_EP93XX
depends on ARCH_EP93XX || COMPILE_TEST
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
@@ -408,8 +408,7 @@ config GPIO_IMX_SCU
config GPIO_IXP4XX
bool "Intel IXP4xx GPIO"
depends on ARCH_IXP4XX
depends on OF
depends on (ARCH_IXP4XX && OF) || COMPILE_TEST
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
select IRQ_DOMAIN_HIERARCHY
@@ -437,6 +436,7 @@ config GPIO_LOONGSON_64BIT
depends on LOONGARCH || COMPILE_TEST
depends on OF_GPIO
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
Say yes here to support the GPIO functionality of a number of
Loongson series of chips. The Loongson GPIO controller supports
@@ -734,7 +734,8 @@ config GPIO_TANGIER
If built as a module its name will be gpio-tangier.
config GPIO_TB10X
bool
bool "Abilis Systems TB10x GPIO controller"
depends on ARC_PLAT_TB10X || COMPILE_TEST
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
select OF_GPIO
@@ -883,7 +884,7 @@ config GPIO_ZYNQMP_MODEPIN
config GPIO_LOONGSON1
tristate "Loongson1 GPIO support"
depends on MACH_LOONGSON32
depends on MACH_LOONGSON32 || COMPILE_TEST
select GPIO_GENERIC
help
Say Y or M here to support GPIO on Loongson1 SoCs.
@@ -1193,14 +1194,18 @@ config GPIO_PCA953X
4 bits: pca9536, pca9537
8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554,
pca9556, pca9557, pca9574, tca6408, tca9554, xra1202
pca9556, pca9557, pca9574, tca6408, tca9554, xra1202,
pcal6408, pcal9554b, tca9538
16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575,
tca6416
tca6416, pca6416, pcal6416, pcal9535, pcal9555a, max7318,
tca9539
24 bits: tca6424
18 bits: tca6418
40 bits: pca9505, pca9698
24 bits: tca6424, pcal6524
40 bits: pca9505, pca9698, pca9506
config GPIO_PCA953X_IRQ
bool "Interrupt controller support for PCA953x"
@@ -1491,6 +1496,18 @@ config GPIO_MADERA
help
Support for GPIOs on Cirrus Logic Madera class codecs.
config GPIO_MAX7360
tristate "MAX7360 GPIO support"
depends on MFD_MAX7360
select GPIO_REGMAP
select REGMAP_IRQ
help
Allows to use MAX7360 I/O Expander PWM lines as GPIO and keypad COL
lines as GPO.
This driver can also be built as a module. If so, the module will be
called gpio-max7360.
config GPIO_MAX77620
tristate "GPIO support for PMIC MAX77620 and MAX20024"
depends on MFD_MAX77620
@@ -1521,6 +1538,18 @@ config GPIO_MAX77759
This driver can also be built as a module. If so, the module will be
called gpio-max77759.
config GPIO_NCT6694
tristate "Nuvoton NCT6694 GPIO controller support"
depends on MFD_NCT6694
select GENERIC_IRQ_CHIP
select GPIOLIB_IRQCHIP
help
This driver supports 8 GPIO pins per bank that can all be interrupt
sources.
This driver can also be built as a module. If so, the module will be
called gpio-nct6694.
config GPIO_PALMAS
tristate "TI PALMAS series PMICs GPIO"
depends on MFD_PALMAS
@@ -1558,7 +1587,7 @@ config GPIO_SL28CPLD
called gpio-sl28cpld.
config GPIO_STMPE
bool "STMPE GPIOs"
tristate "STMPE GPIOs"
depends on MFD_STMPE
depends on OF_GPIO
select GPIOLIB_IRQCHIP
+2
View File
@@ -106,6 +106,7 @@ obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o
obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o
obj-$(CONFIG_GPIO_MAX7360) += gpio-max7360.o
obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o
obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o
obj-$(CONFIG_GPIO_MAX77759) += gpio-max77759.o
@@ -128,6 +129,7 @@ obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
obj-$(CONFIG_GPIO_NCT6694) += gpio-nct6694.o
obj-$(CONFIG_GPIO_NOMADIK) += gpio-nomadik.o
obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o
obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
-17
View File
@@ -131,11 +131,6 @@ Work items:
helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use
this with dry-coding and sending to maintainers to test
- Move the MMIO GPIO specific fields out of struct gpio_chip into a
dedicated structure. Currently every GPIO chip has them if gpio-mmio is
enabled in Kconfig even if it itself doesn't register with the helper
library.
-------------------------------------------------------------------------------
Generic regmap GPIO
@@ -176,18 +171,6 @@ cannot be converted yet, but watch this space!
-------------------------------------------------------------------------------
Convert all GPIO chips to using the new, value returning line setters
struct gpio_chip's set() and set_multiple() callbacks are now deprecated. They
return void and thus do not allow drivers to indicate failure to set the line
value back to the caller.
We've now added new variants - set_rv() and set_multiple_rv() that return an
integer. Let's convert all GPIO drivers treewide to use the new callbacks,
remove the old ones and finally rename the new ones back to the old names.
-------------------------------------------------------------------------------
Remove legacy sysfs features
We have two parallel per-chip class devices and per-exported-line attribute
+353 -57
View File
@@ -12,6 +12,7 @@
#include <linux/configfs.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -28,6 +29,7 @@
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/forwarder.h>
#include <linux/gpio/machine.h>
#include "dev-sync-probe.h"
@@ -244,18 +246,34 @@ struct gpiochip_fwd {
spinlock_t slock; /* protects tmp[] if !can_sleep */
};
struct gpiochip_fwd_timing *delay_timings;
void *data;
unsigned long *valid_mask;
unsigned long tmp[]; /* values and descs for multiple ops */
};
#define fwd_tmp_values(fwd) &(fwd)->tmp[0]
#define fwd_tmp_descs(fwd) (void *)&(fwd)->tmp[BITS_TO_LONGS((fwd)->chip.ngpio)]
#define fwd_tmp_values(fwd) (&(fwd)->tmp[0])
#define fwd_tmp_descs(fwd) ((void *)&(fwd)->tmp[BITS_TO_LONGS((fwd)->chip.ngpio)])
#define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios))
static int gpio_fwd_request(struct gpio_chip *chip, unsigned int offset)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
return test_bit(offset, fwd->valid_mask) ? 0 : -ENODEV;
}
static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
/*
* get_direction() is called during gpiochip registration, return
* -ENODEV if there is no GPIO desc for the line.
*/
if (!test_bit(offset, fwd->valid_mask))
return -ENODEV;
return gpiod_get_direction(fwd->descs[offset]);
}
@@ -453,10 +471,11 @@ static int gpiochip_fwd_delay_of_xlate(struct gpio_chip *chip,
return line;
}
static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip,
struct gpiochip_fwd *fwd)
static int gpiochip_fwd_setup_delay_line(struct gpiochip_fwd *fwd)
{
fwd->delay_timings = devm_kcalloc(dev, chip->ngpio,
struct gpio_chip *chip = &fwd->chip;
fwd->delay_timings = devm_kcalloc(chip->parent, chip->ngpio,
sizeof(*fwd->delay_timings),
GFP_KERNEL);
if (!fwd->delay_timings)
@@ -468,20 +487,333 @@ static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *c
return 0;
}
#else
static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip,
struct gpiochip_fwd *fwd)
static int gpiochip_fwd_setup_delay_line(struct gpiochip_fwd *fwd)
{
return 0;
}
#endif /* !CONFIG_OF_GPIO */
/**
* gpiochip_fwd_get_gpiochip - Get the GPIO chip for the GPIO forwarder
* @fwd: GPIO forwarder
*
* Returns: The GPIO chip for the GPIO forwarder
*/
struct gpio_chip *gpiochip_fwd_get_gpiochip(struct gpiochip_fwd *fwd)
{
return &fwd->chip;
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_get_gpiochip, "GPIO_FORWARDER");
/**
* gpiochip_fwd_get_data - Get driver-private data for the GPIO forwarder
* @fwd: GPIO forwarder
*
* Returns: The driver-private data for the GPIO forwarder
*/
void *gpiochip_fwd_get_data(struct gpiochip_fwd *fwd)
{
return fwd->data;
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_get_data, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_request - Request a line of the GPIO forwarder
* @fwd: GPIO forwarder
* @offset: the offset of the line to request
*
* Returns: 0 on success, or negative errno on failure.
*/
int gpiochip_fwd_gpio_request(struct gpiochip_fwd *fwd, unsigned int offset)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_request(gc, offset);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_request, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_get_direction - Return the current direction of a GPIO forwarder line
* @fwd: GPIO forwarder
* @offset: the offset of the line
*
* Returns: 0 for output, 1 for input, or an error code in case of error.
*/
int gpiochip_fwd_gpio_get_direction(struct gpiochip_fwd *fwd, unsigned int offset)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_get_direction(gc, offset);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_get_direction, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_direction_output - Set a GPIO forwarder line direction to
* output
* @fwd: GPIO forwarder
* @offset: the offset of the line
* @value: value to set
*
* Returns: 0 on success, or negative errno on failure.
*/
int gpiochip_fwd_gpio_direction_output(struct gpiochip_fwd *fwd, unsigned int offset,
int value)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_direction_output(gc, offset, value);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_direction_output, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_direction_input - Set a GPIO forwarder line direction to input
* @fwd: GPIO forwarder
* @offset: the offset of the line
*
* Returns: 0 on success, or negative errno on failure.
*/
int gpiochip_fwd_gpio_direction_input(struct gpiochip_fwd *fwd, unsigned int offset)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_direction_input(gc, offset);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_direction_input, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_get - Return a GPIO forwarder line's value
* @fwd: GPIO forwarder
* @offset: the offset of the line
*
* Returns: The GPIO's logical value, i.e. taking the ACTIVE_LOW status into
* account, or negative errno on failure.
*/
int gpiochip_fwd_gpio_get(struct gpiochip_fwd *fwd, unsigned int offset)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_get(gc, offset);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_get, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_get_multiple - Get values for multiple GPIO forwarder lines
* @fwd: GPIO forwarder
* @mask: bit mask array; one bit per line; BITS_PER_LONG bits per word defines
* which lines are to be read
* @bits: bit value array; one bit per line; BITS_PER_LONG bits per word will
* contains the read values for the lines specified by mask
*
* Returns: 0 on success, or negative errno on failure.
*/
int gpiochip_fwd_gpio_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
unsigned long *bits)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_get_multiple_locked(gc, mask, bits);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_get_multiple, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_set - Assign value to a GPIO forwarder line.
* @fwd: GPIO forwarder
* @offset: the offset of the line
* @value: value to set
*
* Returns: 0 on success, or negative errno on failure.
*/
int gpiochip_fwd_gpio_set(struct gpiochip_fwd *fwd, unsigned int offset, int value)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_set(gc, offset, value);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_set, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_set_multiple - Assign values to multiple GPIO forwarder lines
* @fwd: GPIO forwarder
* @mask: bit mask array; one bit per output; BITS_PER_LONG bits per word
* defines which outputs are to be changed
* @bits: bit value array; one bit per output; BITS_PER_LONG bits per word
* defines the values the outputs specified by mask are to be set to
*
* Returns: 0 on success, or negative errno on failure.
*/
int gpiochip_fwd_gpio_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
unsigned long *bits)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_set_multiple_locked(gc, mask, bits);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_set_multiple, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_set_config - Set @config for a GPIO forwarder line
* @fwd: GPIO forwarder
* @offset: the offset of the line
* @config: Same packed config format as generic pinconf
*
* Returns: 0 on success, %-ENOTSUPP if the controller doesn't support setting
* the configuration.
*/
int gpiochip_fwd_gpio_set_config(struct gpiochip_fwd *fwd, unsigned int offset,
unsigned long config)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_set_config(gc, offset, config);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_set_config, "GPIO_FORWARDER");
/**
* gpiochip_fwd_gpio_to_irq - Return the IRQ corresponding to a GPIO forwarder line
* @fwd: GPIO forwarder
* @offset: the offset of the line
*
* Returns: The Linux IRQ corresponding to the passed line, or an error code in
* case of error.
*/
int gpiochip_fwd_gpio_to_irq(struct gpiochip_fwd *fwd, unsigned int offset)
{
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
return gpio_fwd_to_irq(gc, offset);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_to_irq, "GPIO_FORWARDER");
/**
* devm_gpiochip_fwd_alloc - Allocate and initialize a new GPIO forwarder
* @dev: Parent device pointer
* @ngpios: Number of GPIOs in the forwarder
*
* Returns: An opaque object pointer, or an ERR_PTR()-encoded negative error
* code on failure.
*/
struct gpiochip_fwd *devm_gpiochip_fwd_alloc(struct device *dev,
unsigned int ngpios)
{
struct gpiochip_fwd *fwd;
struct gpio_chip *chip;
fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)), GFP_KERNEL);
if (!fwd)
return ERR_PTR(-ENOMEM);
fwd->descs = devm_kcalloc(dev, ngpios, sizeof(*fwd->descs), GFP_KERNEL);
if (!fwd->descs)
return ERR_PTR(-ENOMEM);
fwd->valid_mask = devm_bitmap_zalloc(dev, ngpios, GFP_KERNEL);
if (!fwd->valid_mask)
return ERR_PTR(-ENOMEM);
chip = &fwd->chip;
chip->label = dev_name(dev);
chip->parent = dev;
chip->owner = THIS_MODULE;
chip->request = gpio_fwd_request;
chip->get_direction = gpio_fwd_get_direction;
chip->direction_input = gpio_fwd_direction_input;
chip->direction_output = gpio_fwd_direction_output;
chip->get = gpio_fwd_get;
chip->get_multiple = gpio_fwd_get_multiple_locked;
chip->set = gpio_fwd_set;
chip->set_multiple = gpio_fwd_set_multiple_locked;
chip->to_irq = gpio_fwd_to_irq;
chip->base = -1;
chip->ngpio = ngpios;
return fwd;
}
EXPORT_SYMBOL_NS_GPL(devm_gpiochip_fwd_alloc, "GPIO_FORWARDER");
/**
* gpiochip_fwd_desc_add - Add a GPIO desc in the forwarder
* @fwd: GPIO forwarder
* @desc: GPIO descriptor to register
* @offset: offset for the GPIO in the forwarder
*
* Returns: 0 on success, or negative errno on failure.
*/
int gpiochip_fwd_desc_add(struct gpiochip_fwd *fwd, struct gpio_desc *desc,
unsigned int offset)
{
struct gpio_chip *chip = &fwd->chip;
if (offset >= chip->ngpio)
return -EINVAL;
if (test_and_set_bit(offset, fwd->valid_mask))
return -EEXIST;
/*
* If any of the GPIO lines are sleeping, then the entire forwarder
* will be sleeping.
*/
if (gpiod_cansleep(desc))
chip->can_sleep = true;
fwd->descs[offset] = desc;
dev_dbg(chip->parent, "%u => gpio %d irq %d\n", offset,
desc_to_gpio(desc), gpiod_to_irq(desc));
return 0;
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_add, "GPIO_FORWARDER");
/**
* gpiochip_fwd_desc_free - Remove a GPIO desc from the forwarder
* @fwd: GPIO forwarder
* @offset: offset of GPIO desc to remove
*/
void gpiochip_fwd_desc_free(struct gpiochip_fwd *fwd, unsigned int offset)
{
if (test_and_clear_bit(offset, fwd->valid_mask))
gpiod_put(fwd->descs[offset]);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_free, "GPIO_FORWARDER");
/**
* gpiochip_fwd_register - Register a GPIO forwarder
* @fwd: GPIO forwarder
* @data: driver-private data associated with this forwarder
*
* Returns: 0 on success, or negative errno on failure.
*/
int gpiochip_fwd_register(struct gpiochip_fwd *fwd, void *data)
{
struct gpio_chip *chip = &fwd->chip;
/*
* Some gpio_desc were not registered. They will be registered at runtime
* but we have to suppose they can sleep.
*/
if (!bitmap_full(fwd->valid_mask, chip->ngpio))
chip->can_sleep = true;
if (chip->can_sleep)
mutex_init(&fwd->mlock);
else
spin_lock_init(&fwd->slock);
fwd->data = data;
return devm_gpiochip_add_data(chip->parent, chip, fwd);
}
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_register, "GPIO_FORWARDER");
/**
* gpiochip_fwd_create() - Create a new GPIO forwarder
* @dev: Parent device pointer
* @ngpios: Number of GPIOs in the forwarder.
* @descs: Array containing the GPIO descriptors to forward to.
* This array must contain @ngpios entries, and must not be deallocated
* before the forwarder has been destroyed again.
* This array must contain @ngpios entries, and can be deallocated
* as the forwarder has its own array.
* @features: Bitwise ORed features as defined with FWD_FEATURE_*.
*
* This function creates a new gpiochip, which forwards all GPIO operations to
@@ -495,64 +827,27 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
struct gpio_desc *descs[],
unsigned long features)
{
const char *label = dev_name(dev);
struct gpiochip_fwd *fwd;
struct gpio_chip *chip;
unsigned int i;
int error;
fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)),
GFP_KERNEL);
if (!fwd)
return ERR_PTR(-ENOMEM);
fwd = devm_gpiochip_fwd_alloc(dev, ngpios);
if (IS_ERR(fwd))
return fwd;
chip = &fwd->chip;
/*
* If any of the GPIO lines are sleeping, then the entire forwarder
* will be sleeping.
* If any of the chips support .set_config(), then the forwarder will
* support setting configs.
*/
for (i = 0; i < ngpios; i++) {
struct gpio_chip *parent = gpiod_to_chip(descs[i]);
dev_dbg(dev, "%u => gpio %d irq %d\n", i,
desc_to_gpio(descs[i]), gpiod_to_irq(descs[i]));
if (gpiod_cansleep(descs[i]))
chip->can_sleep = true;
if (parent && parent->set_config)
chip->set_config = gpio_fwd_set_config;
}
chip->label = label;
chip->parent = dev;
chip->owner = THIS_MODULE;
chip->get_direction = gpio_fwd_get_direction;
chip->direction_input = gpio_fwd_direction_input;
chip->direction_output = gpio_fwd_direction_output;
chip->get = gpio_fwd_get;
chip->get_multiple = gpio_fwd_get_multiple_locked;
chip->set = gpio_fwd_set;
chip->set_multiple = gpio_fwd_set_multiple_locked;
chip->to_irq = gpio_fwd_to_irq;
chip->base = -1;
chip->ngpio = ngpios;
fwd->descs = descs;
if (chip->can_sleep)
mutex_init(&fwd->mlock);
else
spin_lock_init(&fwd->slock);
if (features & FWD_FEATURE_DELAY) {
error = gpiochip_fwd_setup_delay_line(dev, chip, fwd);
error = gpiochip_fwd_desc_add(fwd, descs[i], i);
if (error)
return ERR_PTR(error);
}
error = devm_gpiochip_add_data(dev, chip, fwd);
if (features & FWD_FEATURE_DELAY) {
error = gpiochip_fwd_setup_delay_line(fwd);
if (error)
return ERR_PTR(error);
}
error = gpiochip_fwd_register(fwd, NULL);
if (error)
return ERR_PTR(error);
@@ -1334,6 +1629,7 @@ static int gpio_aggregator_probe(struct platform_device *pdev)
return PTR_ERR(fwd);
platform_set_drvdata(pdev, fwd);
devm_kfree(dev, descs);
return 0;
}
+23 -21
View File
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/spinlock.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
@@ -24,54 +25,50 @@
#define PT_SYNC_REG 0x28
struct pt_gpio_chip {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *reg_base;
};
static int pt_gpio_request(struct gpio_chip *gc, unsigned offset)
{
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc);
unsigned long flags;
u32 using_pins;
dev_dbg(gc->parent, "pt_gpio_request offset=%x\n", offset);
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(gen_gc);
using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
if (using_pins & BIT(offset)) {
dev_warn(gc->parent, "PT GPIO pin %x reconfigured\n",
offset);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return -EINVAL;
}
writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
static void pt_gpio_free(struct gpio_chip *gc, unsigned offset)
{
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc);
unsigned long flags;
u32 using_pins;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(gen_gc);
using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
using_pins &= ~BIT(offset);
writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
dev_dbg(gc->parent, "pt_gpio_free offset=%x\n", offset);
}
static int pt_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct pt_gpio_chip *pt_gpio;
int ret = 0;
@@ -91,22 +88,27 @@ static int pt_gpio_probe(struct platform_device *pdev)
return PTR_ERR(pt_gpio->reg_base);
}
ret = bgpio_init(&pt_gpio->gc, dev, 4,
pt_gpio->reg_base + PT_INPUTDATA_REG,
pt_gpio->reg_base + PT_OUTPUTDATA_REG, NULL,
pt_gpio->reg_base + PT_DIRECTION_REG, NULL,
BGPIOF_READ_OUTPUT_REG_SET);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = pt_gpio->reg_base + PT_INPUTDATA_REG,
.set = pt_gpio->reg_base + PT_OUTPUTDATA_REG,
.dirout = pt_gpio->reg_base + PT_DIRECTION_REG,
.flags = GPIO_GENERIC_READ_OUTPUT_REG_SET,
};
ret = gpio_generic_chip_init(&pt_gpio->chip, &config);
if (ret) {
dev_err(dev, "bgpio_init failed\n");
dev_err(dev, "failed to initialize the generic GPIO chip\n");
return ret;
}
pt_gpio->gc.owner = THIS_MODULE;
pt_gpio->gc.request = pt_gpio_request;
pt_gpio->gc.free = pt_gpio_free;
pt_gpio->gc.ngpio = (uintptr_t)device_get_match_data(dev);
pt_gpio->chip.gc.owner = THIS_MODULE;
pt_gpio->chip.gc.request = pt_gpio_request;
pt_gpio->chip.gc.free = pt_gpio_free;
pt_gpio->chip.gc.ngpio = (uintptr_t)device_get_match_data(dev);
ret = devm_gpiochip_add_data(dev, &pt_gpio->gc, pt_gpio);
ret = devm_gpiochip_add_data(dev, &pt_gpio->chip.gc, pt_gpio);
if (ret) {
dev_err(dev, "Failed to register GPIO lib\n");
return ret;
+42 -46
View File
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mod_devicetable.h>
@@ -28,17 +29,17 @@
#define AR71XX_GPIO_REG_INT_MASK 0x24
struct ath79_gpio_ctrl {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *base;
raw_spinlock_t lock;
unsigned long both_edges;
};
static struct ath79_gpio_ctrl *irq_data_to_ath79_gpio(struct irq_data *data)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
return container_of(gc, struct ath79_gpio_ctrl, gc);
return container_of(gen_gc, struct ath79_gpio_ctrl, chip);
}
static u32 ath79_gpio_read(struct ath79_gpio_ctrl *ctrl, unsigned reg)
@@ -70,48 +71,43 @@ static void ath79_gpio_irq_unmask(struct irq_data *data)
{
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
u32 mask = BIT(irqd_to_hwirq(data));
unsigned long flags;
gpiochip_enable_irq(&ctrl->gc, irqd_to_hwirq(data));
raw_spin_lock_irqsave(&ctrl->lock, flags);
gpiochip_enable_irq(&ctrl->chip.gc, irqd_to_hwirq(data));
guard(gpio_generic_lock_irqsave)(&ctrl->chip);
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask);
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
}
static void ath79_gpio_irq_mask(struct irq_data *data)
{
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
u32 mask = BIT(irqd_to_hwirq(data));
unsigned long flags;
raw_spin_lock_irqsave(&ctrl->lock, flags);
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
gpiochip_disable_irq(&ctrl->gc, irqd_to_hwirq(data));
scoped_guard(gpio_generic_lock_irqsave, &ctrl->chip)
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
gpiochip_disable_irq(&ctrl->chip.gc, irqd_to_hwirq(data));
}
static void ath79_gpio_irq_enable(struct irq_data *data)
{
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
u32 mask = BIT(irqd_to_hwirq(data));
unsigned long flags;
raw_spin_lock_irqsave(&ctrl->lock, flags);
guard(gpio_generic_lock_irqsave)(&ctrl->chip);
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask);
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask);
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
}
static void ath79_gpio_irq_disable(struct irq_data *data)
{
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
u32 mask = BIT(irqd_to_hwirq(data));
unsigned long flags;
raw_spin_lock_irqsave(&ctrl->lock, flags);
guard(gpio_generic_lock_irqsave)(&ctrl->chip);
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0);
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
}
static int ath79_gpio_irq_set_type(struct irq_data *data,
@@ -120,7 +116,6 @@ static int ath79_gpio_irq_set_type(struct irq_data *data,
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
u32 mask = BIT(irqd_to_hwirq(data));
u32 type = 0, polarity = 0;
unsigned long flags;
bool disabled;
switch (flow_type) {
@@ -142,7 +137,7 @@ static int ath79_gpio_irq_set_type(struct irq_data *data,
return -EINVAL;
}
raw_spin_lock_irqsave(&ctrl->lock, flags);
guard(gpio_generic_lock_irqsave)(&ctrl->chip);
if (flow_type == IRQ_TYPE_EDGE_BOTH) {
ctrl->both_edges |= mask;
@@ -167,8 +162,6 @@ static int ath79_gpio_irq_set_type(struct irq_data *data,
ath79_gpio_update_bits(
ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask);
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
return 0;
}
@@ -187,28 +180,27 @@ static void ath79_gpio_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct ath79_gpio_ctrl *ctrl =
container_of(gc, struct ath79_gpio_ctrl, gc);
unsigned long flags, pending;
container_of(gen_gc, struct ath79_gpio_ctrl, chip);
unsigned long pending;
u32 both_edges, state;
int irq;
chained_irq_enter(irqchip, desc);
raw_spin_lock_irqsave(&ctrl->lock, flags);
scoped_guard(gpio_generic_lock_irqsave, &ctrl->chip) {
pending = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_INT_PENDING);
pending = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_INT_PENDING);
/* Update the polarity of the both edges irqs */
both_edges = ctrl->both_edges & pending;
if (both_edges) {
state = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN);
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_POLARITY,
both_edges, ~state);
/* Update the polarity of the both edges irqs */
both_edges = ctrl->both_edges & pending;
if (both_edges) {
state = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN);
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_POLARITY,
both_edges, ~state);
}
}
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
for_each_set_bit(irq, &pending, gc->ngpio)
generic_handle_domain_irq(gc->irq.domain, irq);
@@ -224,6 +216,7 @@ MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);
static int ath79_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct ath79_gpio_ctrl *ctrl;
struct gpio_irq_chip *girq;
@@ -252,22 +245,25 @@ static int ath79_gpio_probe(struct platform_device *pdev)
if (IS_ERR(ctrl->base))
return PTR_ERR(ctrl->base);
raw_spin_lock_init(&ctrl->lock);
err = bgpio_init(&ctrl->gc, dev, 4,
ctrl->base + AR71XX_GPIO_REG_IN,
ctrl->base + AR71XX_GPIO_REG_SET,
ctrl->base + AR71XX_GPIO_REG_CLEAR,
oe_inverted ? NULL : ctrl->base + AR71XX_GPIO_REG_OE,
oe_inverted ? ctrl->base + AR71XX_GPIO_REG_OE : NULL,
0);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = ctrl->base + AR71XX_GPIO_REG_IN,
.set = ctrl->base + AR71XX_GPIO_REG_SET,
.clr = ctrl->base + AR71XX_GPIO_REG_CLEAR,
.dirout = oe_inverted ? NULL : ctrl->base + AR71XX_GPIO_REG_OE,
.dirin = oe_inverted ? ctrl->base + AR71XX_GPIO_REG_OE : NULL,
};
err = gpio_generic_chip_init(&ctrl->chip, &config);
if (err) {
dev_err(dev, "bgpio_init failed\n");
dev_err(dev, "failed to initialize generic GPIO chip\n");
return err;
}
/* Optional interrupt setup */
if (device_property_read_bool(dev, "interrupt-controller")) {
girq = &ctrl->gc.irq;
girq = &ctrl->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &ath79_gpio_irqchip);
girq->parent_handler = ath79_gpio_irq_handler;
girq->num_parents = 1;
@@ -280,7 +276,7 @@ static int ath79_gpio_probe(struct platform_device *pdev)
girq->handler = handle_simple_irq;
}
return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
return devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl);
}
static struct platform_driver ath79_gpio_driver = {

Some files were not shown because too many files have changed in this diff Show More