gpio updates for v6.18-rc1

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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmjaTpAACgkQEacuoBRx
 13Iqwg//bcAxAcYGsEzMqcid9ugu+1KFiULWhgIYi4PBeX72UR7ZtSbzQ+hwTY68
 Vs1W7eoeaglHwd46K/oXEBsnOvot/gQBjuw4QmYbO6viDWY9OuU81xppgAyw4IEx
 wBmPiRVHHRNhcAA9/lu5OkFSWeeBNqcUkWlGgyUzsIIpQnadBRYXga+qFW9mq4m5
 FMSCagsNP0tnRG4ae1Ek8SAbi3BiKaC/bpxukLVvv6Ma6CUktN2d49gW24h9NbKi
 clflMVWgWgZBgYVk7Nmz5IQwYtvQz2M3y4Mb4VsuSWXWKRjX+gJb0knmq1fa8SEn
 lx1MyhSgkpdtUx/bzLjZrPvesbiS1JnyLRAQVa+vtOU8ct2GSWjbKK9vY9w6guVT
 KlptBNxxyQQi3WVtC+lvYz303gAlm5R2veehTMkmR6PIAE+zQ3EBNstiEEQsoMb2
 npAqr/maodIo+FvUpFghSTfG2dSNRnXpRXmGzn49Y3LSjZu/7gC0B5mZYuGghK/g
 UIvo3m40G/ajTUSyWOWPViUnJPkNMiQTHiH8MrXFUO/sbCavZCC2xp37D0ezWfIx
 KRq3KPLHEL39dDvLFkpTQccOfSXch/bVFNX0X6EtfyAx4rSCphbX25IaWeuhJ9Ox
 N3bALHVs0hMvY5nq0sUDTgf8oQMbpqKdAhKnSae3SBWLhon/EaA=
 =TZ1G
 -----END PGP SIGNATURE-----

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
commit d5f7411411
124 changed files with 8579 additions and 1783 deletions

View File

@ -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>;
};
};

View File

@ -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>;
};
};

View File

@ -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:

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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:

View File

@ -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>;

View File

@ -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

View File

@ -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";
};
};
};

View File

@ -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.

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

View File

@ -12,6 +12,7 @@ Contents:
driver
consumer
board
legacy-boards
drivers-on-gpio
bt8xxgpio

View File

@ -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.

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

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

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

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

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;
}

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;

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 = {

View File

@ -6,6 +6,7 @@
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -36,7 +37,7 @@
struct blzp1600_gpio {
void __iomem *base;
struct gpio_chip gc;
struct gpio_generic_chip gen_gc;
int irq;
};
@ -76,7 +77,7 @@ static void blzp1600_gpio_irq_mask(struct irq_data *d)
{
struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d);
guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock);
guard(gpio_generic_lock_irqsave)(&chip->gen_gc);
blzp1600_gpio_rmw(chip->base + GPIO_IM_REG, BIT(d->hwirq), 1);
}
@ -84,7 +85,7 @@ static void blzp1600_gpio_irq_unmask(struct irq_data *d)
{
struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d);
guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock);
guard(gpio_generic_lock_irqsave)(&chip->gen_gc);
blzp1600_gpio_rmw(chip->base + GPIO_IM_REG, BIT(d->hwirq), 0);
}
@ -99,9 +100,9 @@ static void blzp1600_gpio_irq_enable(struct irq_data *d)
{
struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d);
gpiochip_enable_irq(&chip->gc, irqd_to_hwirq(d));
gpiochip_enable_irq(&chip->gen_gc.gc, irqd_to_hwirq(d));
guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock);
guard(gpio_generic_lock_irqsave)(&chip->gen_gc);
blzp1600_gpio_rmw(chip->base + GPIO_DIR_REG, BIT(d->hwirq), 0);
blzp1600_gpio_rmw(chip->base + GPIO_IEN_REG, BIT(d->hwirq), 1);
}
@ -110,9 +111,9 @@ static void blzp1600_gpio_irq_disable(struct irq_data *d)
{
struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d);
guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock);
guard(gpio_generic_lock_irqsave)(&chip->gen_gc);
blzp1600_gpio_rmw(chip->base + GPIO_IEN_REG, BIT(d->hwirq), 0);
gpiochip_disable_irq(&chip->gc, irqd_to_hwirq(d));
gpiochip_disable_irq(&chip->gen_gc.gc, irqd_to_hwirq(d));
}
static int blzp1600_gpio_irq_set_type(struct irq_data *d, u32 type)
@ -121,7 +122,7 @@ static int blzp1600_gpio_irq_set_type(struct irq_data *d, u32 type)
u32 edge_level, single_both, fall_rise;
int mask = BIT(d->hwirq);
guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock);
guard(gpio_generic_lock_irqsave)(&chip->gen_gc);
edge_level = blzp1600_gpio_read(chip, GPIO_IS_REG);
single_both = blzp1600_gpio_read(chip, GPIO_IBE_REG);
fall_rise = blzp1600_gpio_read(chip, GPIO_IEV_REG);
@ -186,8 +187,8 @@ static void blzp1600_gpio_irqhandler(struct irq_desc *desc)
chained_irq_enter(irqchip, desc);
irq_status = blzp1600_gpio_read(gpio, GPIO_RIS_REG);
for_each_set_bit(hwirq, &irq_status, gpio->gc.ngpio)
generic_handle_domain_irq(gpio->gc.irq.domain, hwirq);
for_each_set_bit(hwirq, &irq_status, gpio->gen_gc.gc.ngpio)
generic_handle_domain_irq(gpio->gen_gc.gc.irq.domain, hwirq);
chained_irq_exit(irqchip, desc);
}
@ -197,7 +198,7 @@ static int blzp1600_gpio_set_debounce(struct gpio_chip *gc, unsigned int offset,
{
struct blzp1600_gpio *chip = gpiochip_get_data(gc);
guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock);
guard(gpio_generic_lock_irqsave)(&chip->gen_gc);
blzp1600_gpio_rmw(chip->base + GPIO_DB_REG, BIT(offset), debounce);
return 0;
@ -216,6 +217,7 @@ static int blzp1600_gpio_set_config(struct gpio_chip *gc, unsigned int offset, u
static int blzp1600_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct blzp1600_gpio *chip;
struct gpio_chip *gc;
int ret;
@ -228,14 +230,21 @@ static int blzp1600_gpio_probe(struct platform_device *pdev)
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
ret = bgpio_init(&chip->gc, &pdev->dev, 4, chip->base + GPIO_IDATA_REG,
chip->base + GPIO_SET_REG, chip->base + GPIO_CLR_REG,
chip->base + GPIO_DIR_REG, NULL, 0);
config = (struct gpio_generic_chip_config) {
.dev = &pdev->dev,
.sz = 4,
.dat = chip->base + GPIO_IDATA_REG,
.set = chip->base + GPIO_SET_REG,
.clr = chip->base + GPIO_CLR_REG,
.dirout = chip->base + GPIO_DIR_REG,
};
ret = gpio_generic_chip_init(&chip->gen_gc, &config);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to register generic gpio\n");
/* configure the gpio chip */
gc = &chip->gc;
gc = &chip->gen_gc.gc;
gc->set_config = blzp1600_gpio_set_config;
if (device_property_present(&pdev->dev, "interrupt-controller")) {

View File

@ -3,6 +3,7 @@
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/irqdomain.h>
@ -37,7 +38,7 @@ enum gio_reg_index {
struct brcmstb_gpio_bank {
struct list_head node;
int id;
struct gpio_chip gc;
struct gpio_generic_chip chip;
struct brcmstb_gpio_priv *parent_priv;
u32 width;
u32 wake_active;
@ -72,19 +73,18 @@ __brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank)
{
void __iomem *reg_base = bank->parent_priv->reg_base;
return bank->gc.read_reg(reg_base + GIO_STAT(bank->id)) &
bank->gc.read_reg(reg_base + GIO_MASK(bank->id));
return gpio_generic_read_reg(&bank->chip, reg_base + GIO_STAT(bank->id)) &
gpio_generic_read_reg(&bank->chip, reg_base + GIO_MASK(bank->id));
}
static unsigned long
brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank)
{
unsigned long status;
unsigned long flags;
raw_spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&bank->chip);
status = __brcmstb_gpio_get_active_irqs(bank);
raw_spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
return status;
}
@ -92,26 +92,26 @@ brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank)
static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq,
struct brcmstb_gpio_bank *bank)
{
return hwirq - bank->gc.offset;
return hwirq - bank->chip.gc.offset;
}
static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
unsigned int hwirq, bool enable)
{
struct gpio_chip *gc = &bank->gc;
struct brcmstb_gpio_priv *priv = bank->parent_priv;
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank));
u32 imask;
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
imask = gc->read_reg(priv->reg_base + GIO_MASK(bank->id));
guard(gpio_generic_lock_irqsave)(&bank->chip);
imask = gpio_generic_read_reg(&bank->chip,
priv->reg_base + GIO_MASK(bank->id));
if (enable)
imask |= mask;
else
imask &= ~mask;
gc->write_reg(priv->reg_base + GIO_MASK(bank->id), imask);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
gpio_generic_write_reg(&bank->chip,
priv->reg_base + GIO_MASK(bank->id), imask);
}
static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
@ -150,7 +150,8 @@ static void brcmstb_gpio_irq_ack(struct irq_data *d)
struct brcmstb_gpio_priv *priv = bank->parent_priv;
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank));
gc->write_reg(priv->reg_base + GIO_STAT(bank->id), mask);
gpio_generic_write_reg(&bank->chip,
priv->reg_base + GIO_STAT(bank->id), mask);
}
static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type)
@ -162,7 +163,6 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type)
u32 edge_insensitive, iedge_insensitive;
u32 edge_config, iedge_config;
u32 level, ilevel;
unsigned long flags;
switch (type) {
case IRQ_TYPE_LEVEL_LOW:
@ -194,23 +194,25 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
raw_spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&bank->chip);
iedge_config = bank->gc.read_reg(priv->reg_base +
GIO_EC(bank->id)) & ~mask;
iedge_insensitive = bank->gc.read_reg(priv->reg_base +
GIO_EI(bank->id)) & ~mask;
ilevel = bank->gc.read_reg(priv->reg_base +
GIO_LEVEL(bank->id)) & ~mask;
iedge_config = gpio_generic_read_reg(&bank->chip,
priv->reg_base + GIO_EC(bank->id)) & ~mask;
iedge_insensitive = gpio_generic_read_reg(&bank->chip,
priv->reg_base + GIO_EI(bank->id)) & ~mask;
ilevel = gpio_generic_read_reg(&bank->chip,
priv->reg_base + GIO_LEVEL(bank->id)) & ~mask;
bank->gc.write_reg(priv->reg_base + GIO_EC(bank->id),
iedge_config | edge_config);
bank->gc.write_reg(priv->reg_base + GIO_EI(bank->id),
iedge_insensitive | edge_insensitive);
bank->gc.write_reg(priv->reg_base + GIO_LEVEL(bank->id),
ilevel | level);
gpio_generic_write_reg(&bank->chip,
priv->reg_base + GIO_EC(bank->id),
iedge_config | edge_config);
gpio_generic_write_reg(&bank->chip,
priv->reg_base + GIO_EI(bank->id),
iedge_insensitive | edge_insensitive);
gpio_generic_write_reg(&bank->chip,
priv->reg_base + GIO_LEVEL(bank->id),
ilevel | level);
raw_spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
return 0;
}
@ -263,7 +265,7 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank)
{
struct brcmstb_gpio_priv *priv = bank->parent_priv;
struct irq_domain *domain = priv->irq_domain;
int hwbase = bank->gc.offset;
int hwbase = bank->chip.gc.offset;
unsigned long status;
while ((status = brcmstb_gpio_get_active_irqs(bank))) {
@ -303,7 +305,7 @@ static struct brcmstb_gpio_bank *brcmstb_gpio_hwirq_to_bank(
/* banks are in descending order */
list_for_each_entry_reverse(bank, &priv->bank_list, node) {
i += bank->gc.ngpio;
i += bank->chip.gc.ngpio;
if (hwirq < i)
return bank;
}
@ -332,7 +334,7 @@ static int brcmstb_gpio_irq_map(struct irq_domain *d, unsigned int irq,
dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n",
irq, (int)hwirq, bank->id);
ret = irq_set_chip_data(irq, &bank->gc);
ret = irq_set_chip_data(irq, &bank->chip.gc);
if (ret < 0)
return ret;
irq_set_lockdep_class(irq, &brcmstb_gpio_irq_lock_class,
@ -394,7 +396,7 @@ static void brcmstb_gpio_remove(struct platform_device *pdev)
* more important to actually perform all of the steps.
*/
list_for_each_entry(bank, &priv->bank_list, node)
gpiochip_remove(&bank->gc);
gpiochip_remove(&bank->chip.gc);
}
static int brcmstb_gpio_of_xlate(struct gpio_chip *gc,
@ -412,7 +414,7 @@ static int brcmstb_gpio_of_xlate(struct gpio_chip *gc,
if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
return -EINVAL;
offset = gpiospec->args[0] - bank->gc.offset;
offset = gpiospec->args[0] - bank->chip.gc.offset;
if (offset >= gc->ngpio || offset < 0)
return -EINVAL;
@ -493,19 +495,17 @@ out_free_domain:
static void brcmstb_gpio_bank_save(struct brcmstb_gpio_priv *priv,
struct brcmstb_gpio_bank *bank)
{
struct gpio_chip *gc = &bank->gc;
unsigned int i;
for (i = 0; i < GIO_REG_STAT; i++)
bank->saved_regs[i] = gc->read_reg(priv->reg_base +
GIO_BANK_OFF(bank->id, i));
bank->saved_regs[i] = gpio_generic_read_reg(&bank->chip,
priv->reg_base + GIO_BANK_OFF(bank->id, i));
}
static void brcmstb_gpio_quiesce(struct device *dev, bool save)
{
struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
struct brcmstb_gpio_bank *bank;
struct gpio_chip *gc;
u32 imask;
/* disable non-wake interrupt */
@ -513,8 +513,6 @@ static void brcmstb_gpio_quiesce(struct device *dev, bool save)
disable_irq(priv->parent_irq);
list_for_each_entry(bank, &priv->bank_list, node) {
gc = &bank->gc;
if (save)
brcmstb_gpio_bank_save(priv, bank);
@ -523,8 +521,9 @@ static void brcmstb_gpio_quiesce(struct device *dev, bool save)
imask = bank->wake_active;
else
imask = 0;
gc->write_reg(priv->reg_base + GIO_MASK(bank->id),
imask);
gpio_generic_write_reg(&bank->chip,
priv->reg_base + GIO_MASK(bank->id),
imask);
}
}
@ -538,12 +537,12 @@ static void brcmstb_gpio_shutdown(struct platform_device *pdev)
static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv,
struct brcmstb_gpio_bank *bank)
{
struct gpio_chip *gc = &bank->gc;
unsigned int i;
for (i = 0; i < GIO_REG_STAT; i++)
gc->write_reg(priv->reg_base + GIO_BANK_OFF(bank->id, i),
bank->saved_regs[i]);
gpio_generic_write_reg(&bank->chip,
priv->reg_base + GIO_BANK_OFF(bank->id, i),
bank->saved_regs[i]);
}
static int brcmstb_gpio_suspend(struct device *dev)
@ -585,6 +584,7 @@ static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
static int brcmstb_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void __iomem *reg_base;
@ -630,7 +630,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
* else leave I/O in little endian mode.
*/
#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER;
#endif
of_property_for_each_u32(np, "brcm,gpio-bank-widths", bank_width) {
@ -665,17 +665,24 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
bank->width = bank_width;
}
gc = &bank->chip.gc;
/*
* Regs are 4 bytes wide, have data reg, no set/clear regs,
* and direction bits have 0 = output and 1 = input
*/
gc = &bank->gc;
err = bgpio_init(gc, dev, 4,
reg_base + GIO_DATA(bank->id),
NULL, NULL, NULL,
reg_base + GIO_IODIR(bank->id), flags);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = reg_base + GIO_DATA(bank->id),
.dirin = reg_base + GIO_IODIR(bank->id),
.flags = flags,
};
err = gpio_generic_chip_init(&bank->chip, &config);
if (err) {
dev_err(dev, "bgpio_init() failed\n");
dev_err(dev, "failed to initialize generic GPIO chip\n");
goto fail;
}
@ -700,7 +707,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
* be retained from S5 cold boot
*/
need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
gc->write_reg(reg_base + GIO_MASK(bank->id), 0);
gpio_generic_write_reg(&bank->chip,
reg_base + GIO_MASK(bank->id), 0);
err = gpiochip_add_data(gc, bank);
if (err) {

View File

@ -181,7 +181,7 @@ static int cdns_gpio_probe(struct platform_device *pdev)
config.dat = cgpio->regs + CDNS_GPIO_INPUT_VALUE;
config.set = cgpio->regs + CDNS_GPIO_OUTPUT_VALUE;
config.dirin = cgpio->regs + CDNS_GPIO_DIRECTION_MODE;
config.flags = BGPIOF_READ_OUTPUT_REG_SET;
config.flags = GPIO_GENERIC_READ_OUTPUT_REG_SET;
ret = gpio_generic_chip_init(&cgpio->gen_gc, &config);
if (ret) {

View File

@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -99,7 +100,7 @@ struct dwapb_gpio_port_irqchip {
};
struct dwapb_gpio_port {
struct gpio_chip gc;
struct gpio_generic_chip chip;
struct dwapb_gpio_port_irqchip *pirq;
struct dwapb_gpio *gpio;
#ifdef CONFIG_PM_SLEEP
@ -107,8 +108,12 @@ struct dwapb_gpio_port {
#endif
unsigned int idx;
};
#define to_dwapb_gpio(_gc) \
(container_of(_gc, struct dwapb_gpio_port, gc)->gpio)
static inline struct dwapb_gpio *to_dwapb_gpio(struct gpio_chip *gc)
{
return container_of(to_gpio_generic_chip(gc),
struct dwapb_gpio_port, chip)->gpio;
}
struct dwapb_gpio {
struct device *dev;
@ -148,19 +153,19 @@ static inline u32 gpio_reg_convert(struct dwapb_gpio *gpio, unsigned int offset)
static inline u32 dwapb_read(struct dwapb_gpio *gpio, unsigned int offset)
{
struct gpio_chip *gc = &gpio->ports[0].gc;
void __iomem *reg_base = gpio->regs;
struct gpio_generic_chip *chip = &gpio->ports[0].chip;
void __iomem *reg_base = gpio->regs;
return gc->read_reg(reg_base + gpio_reg_convert(gpio, offset));
return gpio_generic_read_reg(chip, reg_base + gpio_reg_convert(gpio, offset));
}
static inline void dwapb_write(struct dwapb_gpio *gpio, unsigned int offset,
u32 val)
{
struct gpio_chip *gc = &gpio->ports[0].gc;
void __iomem *reg_base = gpio->regs;
struct gpio_generic_chip *chip = &gpio->ports[0].chip;
void __iomem *reg_base = gpio->regs;
gc->write_reg(reg_base + gpio_reg_convert(gpio, offset), val);
gpio_generic_write_reg(chip, reg_base + gpio_reg_convert(gpio, offset), val);
}
static struct dwapb_gpio_port *dwapb_offs_to_port(struct dwapb_gpio *gpio, unsigned int offs)
@ -186,7 +191,7 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
if (!port)
return;
gc = &port->gc;
gc = &port->chip.gc;
pol = dwapb_read(gpio, GPIO_INT_POLARITY);
/* Just read the current value right out of the data register */
@ -201,13 +206,13 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
{
struct gpio_chip *gc = &gpio->ports[0].gc;
struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip;
unsigned long irq_status;
irq_hw_number_t hwirq;
irq_status = dwapb_read(gpio, GPIO_INTSTATUS);
for_each_set_bit(hwirq, &irq_status, DWAPB_MAX_GPIOS) {
int gpio_irq = irq_find_mapping(gc->irq.domain, hwirq);
int gpio_irq = irq_find_mapping(gen_gc->gc.irq.domain, hwirq);
u32 irq_type = irq_get_trigger_type(gpio_irq);
generic_handle_irq(gpio_irq);
@ -237,27 +242,27 @@ static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id)
static void dwapb_irq_ack(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
u32 val = BIT(irqd_to_hwirq(d));
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(gen_gc);
dwapb_write(gpio, GPIO_PORTA_EOI, val);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static void dwapb_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
val = dwapb_read(gpio, GPIO_INTMASK) | BIT(hwirq);
dwapb_write(gpio, GPIO_INTMASK, val);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
scoped_guard(gpio_generic_lock_irqsave, gen_gc) {
val = dwapb_read(gpio, GPIO_INTMASK) | BIT(hwirq);
dwapb_write(gpio, GPIO_INTMASK, val);
}
gpiochip_disable_irq(gc, hwirq);
}
@ -265,59 +270,61 @@ static void dwapb_irq_mask(struct irq_data *d)
static void dwapb_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
unsigned long flags;
u32 val;
gpiochip_enable_irq(gc, hwirq);
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(gen_gc);
val = dwapb_read(gpio, GPIO_INTMASK) & ~BIT(hwirq);
dwapb_write(gpio, GPIO_INTMASK, val);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static void dwapb_irq_enable(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(gen_gc);
val = dwapb_read(gpio, GPIO_INTEN) | BIT(hwirq);
dwapb_write(gpio, GPIO_INTEN, val);
val = dwapb_read(gpio, GPIO_INTMASK) & ~BIT(hwirq);
dwapb_write(gpio, GPIO_INTMASK, val);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static void dwapb_irq_disable(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(gen_gc);
val = dwapb_read(gpio, GPIO_INTMASK) | BIT(hwirq);
dwapb_write(gpio, GPIO_INTMASK, val);
val = dwapb_read(gpio, GPIO_INTEN) & ~BIT(hwirq);
dwapb_write(gpio, GPIO_INTEN, val);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static int dwapb_irq_set_type(struct irq_data *d, u32 type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
irq_hw_number_t bit = irqd_to_hwirq(d);
unsigned long level, polarity, flags;
unsigned long level, polarity;
guard(gpio_generic_lock_irqsave)(gen_gc);
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
level = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
polarity = dwapb_read(gpio, GPIO_INT_POLARITY);
@ -352,7 +359,6 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level);
if (type != IRQ_TYPE_EDGE_BOTH)
dwapb_write(gpio, GPIO_INT_POLARITY, polarity);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@ -393,11 +399,12 @@ static int dwapb_gpio_set_debounce(struct gpio_chip *gc,
unsigned offset, unsigned debounce)
{
struct dwapb_gpio_port *port = gpiochip_get_data(gc);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
struct dwapb_gpio *gpio = port->gpio;
unsigned long flags, val_deb;
unsigned long val_deb;
unsigned long mask = BIT(offset);
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(gen_gc);
val_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
if (debounce)
@ -406,8 +413,6 @@ static int dwapb_gpio_set_debounce(struct gpio_chip *gc,
val_deb &= ~mask;
dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@ -445,7 +450,7 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
struct dwapb_port_property *pp)
{
struct dwapb_gpio_port_irqchip *pirq;
struct gpio_chip *gc = &port->gc;
struct gpio_chip *gc = &port->chip.gc;
struct gpio_irq_chip *girq;
int err;
@ -501,6 +506,7 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
struct dwapb_port_property *pp,
unsigned int offs)
{
struct gpio_generic_chip_config config;
struct dwapb_gpio_port *port;
void __iomem *dat, *set, *dirout;
int err;
@ -519,32 +525,39 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
set = gpio->regs + GPIO_SWPORTA_DR + pp->idx * GPIO_SWPORT_DR_STRIDE;
dirout = gpio->regs + GPIO_SWPORTA_DDR + pp->idx * GPIO_SWPORT_DDR_STRIDE;
config = (struct gpio_generic_chip_config) {
.dev = gpio->dev,
.sz = 4,
.dat = dat,
.set = set,
.dirout = dirout,
};
/* This registers 32 GPIO lines per port */
err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
NULL, 0);
err = gpio_generic_chip_init(&port->chip, &config);
if (err) {
dev_err(gpio->dev, "failed to init gpio chip for port%d\n",
port->idx);
return err;
}
port->gc.fwnode = pp->fwnode;
port->gc.ngpio = pp->ngpio;
port->gc.base = pp->gpio_base;
port->gc.request = gpiochip_generic_request;
port->gc.free = gpiochip_generic_free;
port->chip.gc.fwnode = pp->fwnode;
port->chip.gc.ngpio = pp->ngpio;
port->chip.gc.base = pp->gpio_base;
port->chip.gc.request = gpiochip_generic_request;
port->chip.gc.free = gpiochip_generic_free;
/* Only port A support debounce */
if (pp->idx == 0)
port->gc.set_config = dwapb_gpio_set_config;
port->chip.gc.set_config = dwapb_gpio_set_config;
else
port->gc.set_config = gpiochip_generic_config;
port->chip.gc.set_config = gpiochip_generic_config;
/* Only port A can provide interrupts in all configurations of the IP */
if (pp->idx == 0)
dwapb_configure_irqs(gpio, port, pp);
err = devm_gpiochip_add_data(gpio->dev, &port->gc, port);
err = devm_gpiochip_add_data(gpio->dev, &port->chip.gc, port);
if (err) {
dev_err(gpio->dev, "failed to register gpiochip for port%d\n",
port->idx);
@ -750,38 +763,37 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
static int dwapb_gpio_suspend(struct device *dev)
{
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags;
struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip;
int i;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
for (i = 0; i < gpio->nr_ports; i++) {
unsigned int offset;
unsigned int idx = gpio->ports[i].idx;
struct dwapb_context *ctx = gpio->ports[i].ctx;
scoped_guard(gpio_generic_lock_irqsave, gen_gc) {
for (i = 0; i < gpio->nr_ports; i++) {
unsigned int offset;
unsigned int idx = gpio->ports[i].idx;
struct dwapb_context *ctx = gpio->ports[i].ctx;
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
ctx->dir = dwapb_read(gpio, offset);
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
ctx->dir = dwapb_read(gpio, offset);
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
ctx->data = dwapb_read(gpio, offset);
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
ctx->data = dwapb_read(gpio, offset);
offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_STRIDE;
ctx->ext = dwapb_read(gpio, offset);
offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_STRIDE;
ctx->ext = dwapb_read(gpio, offset);
/* Only port A can provide interrupts */
if (idx == 0) {
ctx->int_mask = dwapb_read(gpio, GPIO_INTMASK);
ctx->int_en = dwapb_read(gpio, GPIO_INTEN);
ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY);
ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
/* Only port A can provide interrupts */
if (idx == 0) {
ctx->int_mask = dwapb_read(gpio, GPIO_INTMASK);
ctx->int_en = dwapb_read(gpio, GPIO_INTEN);
ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY);
ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
/* Mask out interrupts */
dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en);
/* Mask out interrupts */
dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en);
}
}
}
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
@ -791,8 +803,8 @@ static int dwapb_gpio_suspend(struct device *dev)
static int dwapb_gpio_resume(struct device *dev)
{
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags;
struct gpio_chip *gc = &gpio->ports[0].chip.gc;
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
int i, err;
err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks);
@ -801,7 +813,8 @@ static int dwapb_gpio_resume(struct device *dev)
return err;
}
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(gen_gc);
for (i = 0; i < gpio->nr_ports; i++) {
unsigned int offset;
unsigned int idx = gpio->ports[i].idx;
@ -828,7 +841,6 @@ static int dwapb_gpio_resume(struct device *dev)
dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff);
}
}
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}

View File

@ -9,16 +9,17 @@
* linux/arch/arm/mach-ep93xx/core.c
*/
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/gpio/driver.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
struct ep93xx_gpio_irq_chip {
void __iomem *base;
@ -31,11 +32,14 @@ struct ep93xx_gpio_irq_chip {
struct ep93xx_gpio_chip {
void __iomem *base;
struct gpio_chip gc;
struct gpio_generic_chip chip;
struct ep93xx_gpio_irq_chip *eic;
};
#define to_ep93xx_gpio_chip(x) container_of(x, struct ep93xx_gpio_chip, gc)
static struct ep93xx_gpio_chip *to_ep93xx_gpio_chip(struct gpio_chip *gc)
{
return container_of(to_gpio_generic_chip(gc), struct ep93xx_gpio_chip, chip);
}
static struct ep93xx_gpio_irq_chip *to_ep93xx_gpio_irq_chip(struct gpio_chip *gc)
{
@ -267,7 +271,7 @@ static const struct irq_chip gpio_eic_irq_chip = {
static int ep93xx_setup_irqs(struct platform_device *pdev,
struct ep93xx_gpio_chip *egc)
{
struct gpio_chip *gc = &egc->gc;
struct gpio_chip *gc = &egc->chip.gc;
struct device *dev = &pdev->dev;
struct gpio_irq_chip *girq = &gc->irq;
int ret, irq, i;
@ -327,6 +331,7 @@ static int ep93xx_setup_irqs(struct platform_device *pdev,
static int ep93xx_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct ep93xx_gpio_chip *egc;
struct gpio_chip *gc;
void __iomem *data;
@ -345,8 +350,16 @@ static int ep93xx_gpio_probe(struct platform_device *pdev)
if (IS_ERR(dir))
return PTR_ERR(dir);
gc = &egc->gc;
ret = bgpio_init(gc, &pdev->dev, 1, data, NULL, NULL, dir, NULL, 0);
gc = &egc->chip.gc;
config = (struct gpio_generic_chip_config) {
.dev = &pdev->dev,
.sz = 1,
.dat = data,
.dirout = dir,
};
ret = gpio_generic_chip_init(&egc->chip, &config);
if (ret)
return dev_err_probe(&pdev->dev, ret, "unable to init generic GPIO\n");

View File

@ -10,12 +10,14 @@
* MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*/
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
/* GPIO registers definition */
#define GPIO_DATA_OUT 0x00
@ -40,13 +42,13 @@
/**
* struct ftgpio_gpio - Gemini GPIO state container
* @dev: containing device for this instance
* @gc: gpiochip for this instance
* @chip: generic GPIO chip for this instance
* @base: remapped I/O-memory base
* @clk: silicon clock
*/
struct ftgpio_gpio {
struct device *dev;
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *base;
struct clk *clk;
};
@ -233,6 +235,7 @@ static const struct irq_chip ftgpio_irq_chip = {
static int ftgpio_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct ftgpio_gpio *g;
struct gpio_irq_chip *girq;
@ -261,27 +264,30 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
*/
return PTR_ERR(g->clk);
ret = bgpio_init(&g->gc, dev, 4,
g->base + GPIO_DATA_IN,
g->base + GPIO_DATA_SET,
g->base + GPIO_DATA_CLR,
g->base + GPIO_DIR,
NULL,
0);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = g->base + GPIO_DATA_IN,
.set = g->base + GPIO_DATA_SET,
.clr = g->base + GPIO_DATA_CLR,
.dirout = g->base + GPIO_DIR,
};
ret = gpio_generic_chip_init(&g->chip, &config);
if (ret)
return dev_err_probe(dev, ret, "unable to init generic GPIO\n");
g->gc.label = dev_name(dev);
g->gc.base = -1;
g->gc.parent = dev;
g->gc.owner = THIS_MODULE;
/* ngpio is set by bgpio_init() */
g->chip.gc.label = dev_name(dev);
g->chip.gc.base = -1;
g->chip.gc.parent = dev;
g->chip.gc.owner = THIS_MODULE;
/* ngpio is set by gpio_generic_chip_init() */
/* We need a silicon clock to do debounce */
if (!IS_ERR(g->clk))
g->gc.set_config = ftgpio_gpio_set_config;
g->chip.gc.set_config = ftgpio_gpio_set_config;
girq = &g->gc.irq;
girq = &g->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &ftgpio_irq_chip);
girq->parent_handler = ftgpio_gpio_irq_handler;
girq->num_parents = 1;
@ -302,7 +308,7 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
/* Clear any use of debounce */
writel(0x0, g->base + GPIO_DEBOUNCE_EN);
return devm_gpiochip_add_data(dev, &g->gc, g);
return devm_gpiochip_add_data(dev, &g->chip.gc, g);
}
static const struct of_device_id ftgpio_gpio_of_match[] = {

View File

@ -16,6 +16,7 @@
*/
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
@ -51,24 +52,36 @@ MODULE_DEVICE_TABLE(of, gef_gpio_ids);
static int __init gef_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct gpio_generic_chip *chip;
struct gpio_chip *gc;
void __iomem *regs;
int ret;
gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
ret = bgpio_init(gc, dev, 4, regs + GEF_GPIO_IN, regs + GEF_GPIO_OUT,
NULL, NULL, regs + GEF_GPIO_DIRECT,
BGPIOF_BIG_ENDIAN_BYTE_ORDER);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = regs + GEF_GPIO_IN,
.set = regs + GEF_GPIO_OUT,
.dirin = regs + GEF_GPIO_DIRECT,
.flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER,
};
ret = gpio_generic_chip_init(chip, &config);
if (ret)
return dev_err_probe(dev, ret, "bgpio_init failed\n");
return dev_err_probe(dev, ret,
"failed to initialize the generic GPIO chip\n");
gc = &chip->gc;
/* Setup pointers to chip functions */
gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev));

View File

@ -19,6 +19,7 @@
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -59,7 +60,7 @@ struct grgpio_lirq {
};
struct grgpio_priv {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *regs;
struct device *dev;
@ -91,13 +92,12 @@ struct grgpio_priv {
static void grgpio_set_imask(struct grgpio_priv *priv, unsigned int offset,
int val)
{
struct gpio_chip *gc = &priv->gc;
if (val)
priv->imask |= BIT(offset);
else
priv->imask &= ~BIT(offset);
gc->write_reg(priv->regs + GRGPIO_IMASK, priv->imask);
gpio_generic_write_reg(&priv->chip, priv->regs + GRGPIO_IMASK, priv->imask);
}
static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset)
@ -118,7 +118,6 @@ static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset)
static int grgpio_irq_set_type(struct irq_data *d, unsigned int type)
{
struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
unsigned long flags;
u32 mask = BIT(d->hwirq);
u32 ipol;
u32 iedge;
@ -146,15 +145,13 @@ static int grgpio_irq_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&priv->chip);
ipol = priv->gc.read_reg(priv->regs + GRGPIO_IPOL) & ~mask;
iedge = priv->gc.read_reg(priv->regs + GRGPIO_IEDGE) & ~mask;
ipol = gpio_generic_read_reg(&priv->chip, priv->regs + GRGPIO_IPOL) & ~mask;
iedge = gpio_generic_read_reg(&priv->chip, priv->regs + GRGPIO_IEDGE) & ~mask;
priv->gc.write_reg(priv->regs + GRGPIO_IPOL, ipol | pol);
priv->gc.write_reg(priv->regs + GRGPIO_IEDGE, iedge | edge);
raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
gpio_generic_write_reg(&priv->chip, priv->regs + GRGPIO_IPOL, ipol | pol);
gpio_generic_write_reg(&priv->chip, priv->regs + GRGPIO_IEDGE, iedge | edge);
return 0;
}
@ -163,29 +160,23 @@ static void grgpio_irq_mask(struct irq_data *d)
{
struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
int offset = d->hwirq;
unsigned long flags;
raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
scoped_guard(gpio_generic_lock_irqsave, &priv->chip)
grgpio_set_imask(priv, offset, 0);
grgpio_set_imask(priv, offset, 0);
raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
gpiochip_disable_irq(&priv->gc, d->hwirq);
gpiochip_disable_irq(&priv->chip.gc, d->hwirq);
}
static void grgpio_irq_unmask(struct irq_data *d)
{
struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
int offset = d->hwirq;
unsigned long flags;
gpiochip_enable_irq(&priv->gc, d->hwirq);
raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
gpiochip_enable_irq(&priv->chip.gc, d->hwirq);
guard(gpio_generic_lock_irqsave)(&priv->chip);
grgpio_set_imask(priv, offset, 1);
raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
}
static const struct irq_chip grgpio_irq_chip = {
@ -200,12 +191,11 @@ static const struct irq_chip grgpio_irq_chip = {
static irqreturn_t grgpio_irq_handler(int irq, void *dev)
{
struct grgpio_priv *priv = dev;
int ngpio = priv->gc.ngpio;
unsigned long flags;
int ngpio = priv->chip.gc.ngpio;
int i;
int match = 0;
raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&priv->chip);
/*
* For each gpio line, call its interrupt handler if it its underlying
@ -221,8 +211,6 @@ static irqreturn_t grgpio_irq_handler(int irq, void *dev)
}
}
raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
if (!match)
dev_warn(priv->dev, "No gpio line matched irq %d\n", irq);
@ -253,13 +241,18 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
dev_dbg(priv->dev, "Mapping irq %d for gpio line %d\n",
irq, offset);
raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
gpio_generic_chip_lock_irqsave(&priv->chip, flags);
/* Request underlying irq if not already requested */
lirq->irq = irq;
uirq = &priv->uirqs[lirq->index];
if (uirq->refcnt == 0) {
raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
/*
* FIXME: This is not how locking works at all, you can't just
* release the lock for a moment to do something that can't
* sleep...
*/
gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
ret = request_irq(uirq->uirq, grgpio_irq_handler, 0,
dev_name(priv->dev), priv);
if (ret) {
@ -268,11 +261,11 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
uirq->uirq);
return ret;
}
raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
gpio_generic_chip_lock_irqsave(&priv->chip, flags);
}
uirq->refcnt++;
raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
/* Setup irq */
irq_set_chip_data(irq, priv);
@ -290,13 +283,13 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
struct grgpio_lirq *lirq;
struct grgpio_uirq *uirq;
unsigned long flags;
int ngpio = priv->gc.ngpio;
int ngpio = priv->chip.gc.ngpio;
int i;
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
gpio_generic_chip_lock_irqsave(&priv->chip, flags);
/* Free underlying irq if last user unmapped */
index = -1;
@ -315,13 +308,13 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
uirq = &priv->uirqs[lirq->index];
uirq->refcnt--;
if (uirq->refcnt == 0) {
raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
free_irq(uirq->uirq, priv);
return;
}
}
raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
}
static void grgpio_irq_domain_remove(void *data)
@ -341,6 +334,7 @@ static const struct irq_domain_ops grgpio_irq_domain_ops = {
static int grgpio_probe(struct platform_device *ofdev)
{
struct device_node *np = ofdev->dev.of_node;
struct gpio_generic_chip_config config;
struct device *dev = &ofdev->dev;
void __iomem *regs;
struct gpio_chip *gc;
@ -359,17 +353,24 @@ static int grgpio_probe(struct platform_device *ofdev)
if (IS_ERR(regs))
return PTR_ERR(regs);
gc = &priv->gc;
err = bgpio_init(gc, dev, 4, regs + GRGPIO_DATA,
regs + GRGPIO_OUTPUT, NULL, regs + GRGPIO_DIR, NULL,
BGPIOF_BIG_ENDIAN_BYTE_ORDER);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = regs + GRGPIO_DATA,
.set = regs + GRGPIO_OUTPUT,
.dirout = regs + GRGPIO_DIR,
.flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER,
};
gc = &priv->chip.gc;
err = gpio_generic_chip_init(&priv->chip, &config);
if (err) {
dev_err(dev, "bgpio_init() failed\n");
dev_err(dev, "failed to initialize the generic GPIO chip\n");
return err;
}
priv->regs = regs;
priv->imask = gc->read_reg(regs + GRGPIO_IMASK);
priv->imask = gpio_generic_read_reg(&priv->chip, regs + GRGPIO_IMASK);
priv->dev = dev;
gc->owner = THIS_MODULE;

View File

@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020 HiSilicon Limited. */
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
@ -33,7 +35,7 @@
#define HISI_GPIO_DRIVER_NAME "gpio-hisi"
struct hisi_gpio {
struct gpio_chip chip;
struct gpio_generic_chip chip;
struct device *dev;
void __iomem *reg_base;
unsigned int line_num;
@ -43,8 +45,8 @@ struct hisi_gpio {
static inline u32 hisi_gpio_read_reg(struct gpio_chip *chip,
unsigned int off)
{
struct hisi_gpio *hisi_gpio =
container_of(chip, struct hisi_gpio, chip);
struct hisi_gpio *hisi_gpio = container_of(to_gpio_generic_chip(chip),
struct hisi_gpio, chip);
void __iomem *reg = hisi_gpio->reg_base + off;
return readl(reg);
@ -53,8 +55,8 @@ static inline u32 hisi_gpio_read_reg(struct gpio_chip *chip,
static inline void hisi_gpio_write_reg(struct gpio_chip *chip,
unsigned int off, u32 val)
{
struct hisi_gpio *hisi_gpio =
container_of(chip, struct hisi_gpio, chip);
struct hisi_gpio *hisi_gpio = container_of(to_gpio_generic_chip(chip),
struct hisi_gpio, chip);
void __iomem *reg = hisi_gpio->reg_base + off;
writel(val, reg);
@ -180,14 +182,14 @@ static void hisi_gpio_irq_disable(struct irq_data *d)
static void hisi_gpio_irq_handler(struct irq_desc *desc)
{
struct hisi_gpio *hisi_gpio = irq_desc_get_handler_data(desc);
unsigned long irq_msk = hisi_gpio_read_reg(&hisi_gpio->chip,
unsigned long irq_msk = hisi_gpio_read_reg(&hisi_gpio->chip.gc,
HISI_GPIO_INTSTATUS_WX);
struct irq_chip *irq_c = irq_desc_get_chip(desc);
int hwirq;
chained_irq_enter(irq_c, desc);
for_each_set_bit(hwirq, &irq_msk, HISI_GPIO_LINE_NUM_MAX)
generic_handle_domain_irq(hisi_gpio->chip.irq.domain,
generic_handle_domain_irq(hisi_gpio->chip.gc.irq.domain,
hwirq);
chained_irq_exit(irq_c, desc);
}
@ -206,7 +208,7 @@ static const struct irq_chip hisi_gpio_irq_chip = {
static void hisi_gpio_init_irq(struct hisi_gpio *hisi_gpio)
{
struct gpio_chip *chip = &hisi_gpio->chip;
struct gpio_chip *chip = &hisi_gpio->chip.gc;
struct gpio_irq_chip *girq_chip = &chip->irq;
gpio_irq_chip_set_chip(girq_chip, &hisi_gpio_irq_chip);
@ -264,6 +266,7 @@ static void hisi_gpio_get_pdata(struct device *dev,
static int hisi_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct hisi_gpio *hisi_gpio;
int port_num;
@ -289,27 +292,32 @@ static int hisi_gpio_probe(struct platform_device *pdev)
hisi_gpio->dev = dev;
ret = bgpio_init(&hisi_gpio->chip, hisi_gpio->dev, 0x4,
hisi_gpio->reg_base + HISI_GPIO_EXT_PORT_WX,
hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_SET_WX,
hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_CLR_WX,
hisi_gpio->reg_base + HISI_GPIO_SWPORT_DDR_SET_WX,
hisi_gpio->reg_base + HISI_GPIO_SWPORT_DDR_CLR_WX,
BGPIOF_NO_SET_ON_INPUT);
config = (struct gpio_generic_chip_config) {
.dev = hisi_gpio->dev,
.sz = 4,
.dat = hisi_gpio->reg_base + HISI_GPIO_EXT_PORT_WX,
.set = hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_SET_WX,
.clr = hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_CLR_WX,
.dirout = hisi_gpio->reg_base + HISI_GPIO_SWPORT_DDR_SET_WX,
.dirin = hisi_gpio->reg_base + HISI_GPIO_SWPORT_DDR_CLR_WX,
.flags = GPIO_GENERIC_NO_SET_ON_INPUT |
GPIO_GENERIC_UNREADABLE_REG_DIR,
};
ret = gpio_generic_chip_init(&hisi_gpio->chip, &config);
if (ret) {
dev_err(dev, "failed to init, ret = %d\n", ret);
return ret;
}
hisi_gpio->chip.set_config = hisi_gpio_set_config;
hisi_gpio->chip.ngpio = hisi_gpio->line_num;
hisi_gpio->chip.bgpio_dir_unreadable = 1;
hisi_gpio->chip.base = -1;
hisi_gpio->chip.gc.set_config = hisi_gpio_set_config;
hisi_gpio->chip.gc.ngpio = hisi_gpio->line_num;
hisi_gpio->chip.gc.base = -1;
if (hisi_gpio->irq > 0)
hisi_gpio_init_irq(hisi_gpio);
ret = devm_gpiochip_add_data(dev, &hisi_gpio->chip, hisi_gpio);
ret = devm_gpiochip_add_data(dev, &hisi_gpio->chip.gc, hisi_gpio);
if (ret) {
dev_err(dev, "failed to register gpiochip, ret = %d\n", ret);
return ret;

View File

@ -6,6 +6,7 @@
// Nintendo Wii (Hollywood) GPIO driver
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -48,7 +49,7 @@
#define HW_GPIO_OWNER 0x3c
struct hlwd_gpio {
struct gpio_chip gpioc;
struct gpio_generic_chip gpioc;
struct device *dev;
void __iomem *regs;
int irq;
@ -61,45 +62,44 @@ static void hlwd_gpio_irqhandler(struct irq_desc *desc)
struct hlwd_gpio *hlwd =
gpiochip_get_data(irq_desc_get_handler_data(desc));
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned long flags;
unsigned long pending;
int hwirq;
u32 emulated_pending;
raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG);
pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
scoped_guard(gpio_generic_lock_irqsave, &hlwd->gpioc) {
pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG);
pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
/* Treat interrupts due to edge trigger emulation separately */
emulated_pending = hlwd->edge_emulation & pending;
pending &= ~emulated_pending;
if (emulated_pending) {
u32 level, rising, falling;
/* Treat interrupts due to edge trigger emulation separately */
emulated_pending = hlwd->edge_emulation & pending;
pending &= ~emulated_pending;
if (emulated_pending) {
u32 level, rising, falling;
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
rising = level & emulated_pending;
falling = ~level & emulated_pending;
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
rising = level & emulated_pending;
falling = ~level & emulated_pending;
/* Invert the levels */
iowrite32be(level ^ emulated_pending,
hlwd->regs + HW_GPIOB_INTLVL);
/* Invert the levels */
iowrite32be(level ^ emulated_pending,
hlwd->regs + HW_GPIOB_INTLVL);
/* Ack all emulated-edge interrupts */
iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG);
/* Ack all emulated-edge interrupts */
iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG);
/* Signal interrupts only on the correct edge */
rising &= hlwd->rising_edge;
falling &= hlwd->falling_edge;
/* Signal interrupts only on the correct edge */
rising &= hlwd->rising_edge;
falling &= hlwd->falling_edge;
/* Mark emulated interrupts as pending */
pending |= rising | falling;
/* Mark emulated interrupts as pending */
pending |= rising | falling;
}
}
raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
chained_irq_enter(chip, desc);
for_each_set_bit(hwirq, &pending, 32)
generic_handle_domain_irq(hlwd->gpioc.irq.domain, hwirq);
generic_handle_domain_irq(hlwd->gpioc.gc.irq.domain, hwirq);
chained_irq_exit(chip, desc);
}
@ -116,30 +116,29 @@ static void hlwd_gpio_irq_mask(struct irq_data *data)
{
struct hlwd_gpio *hlwd =
gpiochip_get_data(irq_data_get_irq_chip_data(data));
unsigned long flags;
u32 mask;
raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
mask &= ~BIT(data->hwirq);
iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK);
raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
gpiochip_disable_irq(&hlwd->gpioc, irqd_to_hwirq(data));
scoped_guard(gpio_generic_lock_irqsave, &hlwd->gpioc) {
mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
mask &= ~BIT(data->hwirq);
iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK);
}
gpiochip_disable_irq(&hlwd->gpioc.gc, irqd_to_hwirq(data));
}
static void hlwd_gpio_irq_unmask(struct irq_data *data)
{
struct hlwd_gpio *hlwd =
gpiochip_get_data(irq_data_get_irq_chip_data(data));
unsigned long flags;
u32 mask;
gpiochip_enable_irq(&hlwd->gpioc, irqd_to_hwirq(data));
raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
gpiochip_enable_irq(&hlwd->gpioc.gc, irqd_to_hwirq(data));
guard(gpio_generic_lock_irqsave)(&hlwd->gpioc);
mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
mask |= BIT(data->hwirq);
iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK);
raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
}
static void hlwd_gpio_irq_enable(struct irq_data *data)
@ -173,10 +172,9 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
{
struct hlwd_gpio *hlwd =
gpiochip_get_data(irq_data_get_irq_chip_data(data));
unsigned long flags;
u32 level;
raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&hlwd->gpioc);
hlwd->edge_emulation &= ~BIT(data->hwirq);
@ -197,11 +195,9 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type);
break;
default:
raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
return -EINVAL;
}
raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
return 0;
}
@ -225,6 +221,7 @@ static const struct irq_chip hlwd_gpio_irq_chip = {
static int hlwd_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct hlwd_gpio *hlwd;
u32 ngpios;
int res;
@ -244,25 +241,31 @@ static int hlwd_gpio_probe(struct platform_device *pdev)
* systems where the AHBPROT memory firewall hasn't been configured to
* permit PPC access to HW_GPIO_*.
*
* Note that this has to happen before bgpio_init reads the
* HW_GPIOB_OUT and HW_GPIOB_DIR, because otherwise it reads the wrong
* values.
* Note that this has to happen before gpio_generic_chip_init() reads
* the HW_GPIOB_OUT and HW_GPIOB_DIR, because otherwise it reads the
* wrong values.
*/
iowrite32be(0xffffffff, hlwd->regs + HW_GPIO_OWNER);
res = bgpio_init(&hlwd->gpioc, &pdev->dev, 4,
hlwd->regs + HW_GPIOB_IN, hlwd->regs + HW_GPIOB_OUT,
NULL, hlwd->regs + HW_GPIOB_DIR, NULL,
BGPIOF_BIG_ENDIAN_BYTE_ORDER);
config = (struct gpio_generic_chip_config) {
.dev = &pdev->dev,
.sz = 4,
.dat = hlwd->regs + HW_GPIOB_IN,
.set = hlwd->regs + HW_GPIOB_OUT,
.dirout = hlwd->regs + HW_GPIOB_DIR,
.flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER,
};
res = gpio_generic_chip_init(&hlwd->gpioc, &config);
if (res < 0) {
dev_warn(&pdev->dev, "bgpio_init failed: %d\n", res);
dev_warn(&pdev->dev, "failed to initialize generic GPIO chip: %d\n", res);
return res;
}
res = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios);
if (res)
ngpios = 32;
hlwd->gpioc.ngpio = ngpios;
hlwd->gpioc.gc.ngpio = ngpios;
/* Mask and ack all interrupts */
iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK);
@ -282,7 +285,7 @@ static int hlwd_gpio_probe(struct platform_device *pdev)
return hlwd->irq;
}
girq = &hlwd->gpioc.irq;
girq = &hlwd->gpioc.gc.irq;
gpio_irq_chip_set_chip(girq, &hlwd_gpio_irq_chip);
girq->parent_handler = hlwd_gpio_irqhandler;
girq->num_parents = 1;
@ -296,7 +299,7 @@ static int hlwd_gpio_probe(struct platform_device *pdev)
girq->handler = handle_level_irq;
}
return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd);
return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc.gc, hlwd);
}
static const struct of_device_id hlwd_gpio_match[] = {

View File

@ -3,6 +3,7 @@
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@ -18,7 +19,7 @@
#define IDT_GPIO_ISTAT 0x0C
struct idt_gpio_ctrl {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *pic;
void __iomem *gpio;
u32 mask_cache;
@ -50,14 +51,13 @@ static int idt_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
unsigned int sense = flow_type & IRQ_TYPE_SENSE_MASK;
unsigned long flags;
u32 ilevel;
/* hardware only supports level triggered */
if (sense == IRQ_TYPE_NONE || (sense & IRQ_TYPE_EDGE_BOTH))
return -EINVAL;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&ctrl->chip);
ilevel = readl(ctrl->gpio + IDT_GPIO_ILEVEL);
if (sense & IRQ_TYPE_LEVEL_HIGH)
@ -68,7 +68,6 @@ static int idt_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
writel(ilevel, ctrl->gpio + IDT_GPIO_ILEVEL);
irq_set_handler_locked(d, handle_level_irq);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@ -84,14 +83,11 @@ static void idt_gpio_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
ctrl->mask_cache |= BIT(d->hwirq);
writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
scoped_guard(gpio_generic_lock_irqsave, &ctrl->chip) {
ctrl->mask_cache |= BIT(d->hwirq);
writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
}
gpiochip_disable_irq(gc, irqd_to_hwirq(d));
}
@ -100,15 +96,13 @@ static void idt_gpio_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
unsigned long flags;
gpiochip_enable_irq(gc, irqd_to_hwirq(d));
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&ctrl->chip);
ctrl->mask_cache &= ~BIT(d->hwirq);
writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static int idt_gpio_irq_init_hw(struct gpio_chip *gc)
@ -134,6 +128,7 @@ static const struct irq_chip idt_gpio_irqchip = {
static int idt_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct gpio_irq_chip *girq;
struct idt_gpio_ctrl *ctrl;
@ -150,18 +145,24 @@ static int idt_gpio_probe(struct platform_device *pdev)
if (IS_ERR(ctrl->gpio))
return PTR_ERR(ctrl->gpio);
ctrl->gc.parent = dev;
ctrl->chip.gc.parent = dev;
ret = bgpio_init(&ctrl->gc, &pdev->dev, 4, ctrl->gpio + IDT_GPIO_DATA,
NULL, NULL, ctrl->gpio + IDT_GPIO_DIR, NULL, 0);
config = (struct gpio_generic_chip_config) {
.dev = &pdev->dev,
.sz = 4,
.dat = ctrl->gpio + IDT_GPIO_DATA,
.dirout = ctrl->gpio + IDT_GPIO_DIR,
};
ret = gpio_generic_chip_init(&ctrl->chip, &config);
if (ret) {
dev_err(dev, "bgpio_init failed\n");
dev_err(dev, "failed to initialize the generic GPIO chip\n");
return ret;
}
ret = device_property_read_u32(dev, "ngpios", &ngpios);
if (!ret)
ctrl->gc.ngpio = ngpios;
ctrl->chip.gc.ngpio = ngpios;
if (device_property_read_bool(dev, "interrupt-controller")) {
ctrl->pic = devm_platform_ioremap_resource_byname(pdev, "pic");
@ -172,7 +173,7 @@ static int idt_gpio_probe(struct platform_device *pdev)
if (parent_irq < 0)
return parent_irq;
girq = &ctrl->gc.irq;
girq = &ctrl->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &idt_gpio_irqchip);
girq->init_hw = idt_gpio_irq_init_hw;
girq->parent_handler = idt_gpio_dispatch;
@ -188,7 +189,7 @@ static int idt_gpio_probe(struct platform_device *pdev)
girq->handler = handle_bad_irq;
}
return devm_gpiochip_add_data(&pdev->dev, &ctrl->gc, ctrl);
return devm_gpiochip_add_data(&pdev->dev, &ctrl->chip.gc, ctrl);
}
static const struct of_device_id idt_gpio_of_match[] = {

View File

@ -8,6 +8,7 @@
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
@ -53,14 +54,14 @@
/**
* struct ixp4xx_gpio - IXP4 GPIO state container
* @chip: generic GPIO chip for this instance
* @dev: containing device for this instance
* @gc: gpiochip for this instance
* @base: remapped I/O-memory base
* @irq_edge: Each bit represents an IRQ: 1: edge-triggered,
* 0: level triggered
*/
struct ixp4xx_gpio {
struct gpio_chip gc;
struct gpio_generic_chip chip;
struct device *dev;
void __iomem *base;
unsigned long long irq_edge;
@ -100,7 +101,6 @@ static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ixp4xx_gpio *g = gpiochip_get_data(gc);
int line = d->hwirq;
unsigned long flags;
u32 int_style;
u32 int_reg;
u32 val;
@ -144,26 +144,24 @@ static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type)
int_reg = IXP4XX_REG_GPIT1;
}
raw_spin_lock_irqsave(&g->gc.bgpio_lock, flags);
scoped_guard(gpio_generic_lock_irqsave, &g->chip) {
/* Clear the style for the appropriate pin */
val = __raw_readl(g->base + int_reg);
val &= ~(IXP4XX_GPIO_STYLE_MASK << (line * IXP4XX_GPIO_STYLE_SIZE));
__raw_writel(val, g->base + int_reg);
/* Clear the style for the appropriate pin */
val = __raw_readl(g->base + int_reg);
val &= ~(IXP4XX_GPIO_STYLE_MASK << (line * IXP4XX_GPIO_STYLE_SIZE));
__raw_writel(val, g->base + int_reg);
__raw_writel(BIT(line), g->base + IXP4XX_REG_GPIS);
__raw_writel(BIT(line), g->base + IXP4XX_REG_GPIS);
/* Set the new style */
val = __raw_readl(g->base + int_reg);
val |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE));
__raw_writel(val, g->base + int_reg);
/* Set the new style */
val = __raw_readl(g->base + int_reg);
val |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE));
__raw_writel(val, g->base + int_reg);
/* Force-configure this line as an input */
val = __raw_readl(g->base + IXP4XX_REG_GPOE);
val |= BIT(d->hwirq);
__raw_writel(val, g->base + IXP4XX_REG_GPOE);
raw_spin_unlock_irqrestore(&g->gc.bgpio_lock, flags);
/* Force-configure this line as an input */
val = __raw_readl(g->base + IXP4XX_REG_GPOE);
val |= BIT(d->hwirq);
__raw_writel(val, g->base + IXP4XX_REG_GPOE);
}
/* This parent only accept level high (asserted) */
return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
@ -206,6 +204,7 @@ static int ixp4xx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
static int ixp4xx_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
unsigned long flags;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
@ -290,35 +289,38 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
* for big endian.
*/
#if defined(CONFIG_CPU_BIG_ENDIAN)
flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER;
#else
flags = 0;
#endif
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = g->base + IXP4XX_REG_GPIN,
.set = g->base + IXP4XX_REG_GPOUT,
.dirin = g->base + IXP4XX_REG_GPOE,
.flags = flags,
};
/* Populate and register gpio chip */
ret = bgpio_init(&g->gc, dev, 4,
g->base + IXP4XX_REG_GPIN,
g->base + IXP4XX_REG_GPOUT,
NULL,
NULL,
g->base + IXP4XX_REG_GPOE,
flags);
ret = gpio_generic_chip_init(&g->chip, &config);
if (ret) {
dev_err(dev, "unable to init generic GPIO\n");
return ret;
}
g->gc.ngpio = 16;
g->gc.label = "IXP4XX_GPIO_CHIP";
g->chip.gc.ngpio = 16;
g->chip.gc.label = "IXP4XX_GPIO_CHIP";
/*
* TODO: when we have migrated to device tree and all GPIOs
* are fetched using phandles, set this to -1 to get rid of
* the fixed gpiochip base.
*/
g->gc.base = 0;
g->gc.parent = &pdev->dev;
g->gc.owner = THIS_MODULE;
g->chip.gc.base = 0;
g->chip.gc.parent = &pdev->dev;
g->chip.gc.owner = THIS_MODULE;
girq = &g->gc.irq;
girq = &g->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &ixp4xx_gpio_irqchip);
girq->fwnode = dev_fwnode(dev);
girq->parent_domain = parent;
@ -326,7 +328,7 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
girq->handler = handle_bad_irq;
girq->default_type = IRQ_TYPE_NONE;
ret = devm_gpiochip_add_data(dev, &g->gc, g);
ret = devm_gpiochip_add_data(dev, &g->chip.gc, g);
if (ret) {
dev_err(dev, "failed to add SoC gpiochip\n");
return ret;

View File

@ -7,12 +7,16 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/reset.h>
#include <asm/types.h>
enum loongson_gpio_mode {
@ -27,10 +31,18 @@ struct loongson_gpio_chip_data {
unsigned int out_offset;
unsigned int in_offset;
unsigned int inten_offset;
unsigned int intpol_offset;
unsigned int intedge_offset;
unsigned int intclr_offset;
unsigned int intsts_offset;
unsigned int intdual_offset;
unsigned int intr_num;
irq_flow_handler_t irq_handler;
const struct irq_chip *girqchip;
};
struct loongson_gpio_chip {
struct gpio_chip chip;
struct gpio_generic_chip chip;
spinlock_t lock;
void __iomem *reg_base;
const struct loongson_gpio_chip_data *chip_data;
@ -38,7 +50,8 @@ struct loongson_gpio_chip {
static inline struct loongson_gpio_chip *to_loongson_gpio_chip(struct gpio_chip *chip)
{
return container_of(chip, struct loongson_gpio_chip, chip);
return container_of(to_gpio_generic_chip(chip),
struct loongson_gpio_chip, chip);
}
static inline void loongson_commit_direction(struct loongson_gpio_chip *lgpio, unsigned int pin,
@ -135,39 +148,184 @@ static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
return platform_get_irq(pdev, offset);
}
static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgpio,
static void loongson_gpio_irq_ack(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
irq_hw_number_t hwirq = irqd_to_hwirq(data);
writeb(0x1, lgpio->reg_base + lgpio->chip_data->intclr_offset + hwirq);
}
static void loongson_gpio_irq_mask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
irq_hw_number_t hwirq = irqd_to_hwirq(data);
writeb(0x0, lgpio->reg_base + lgpio->chip_data->inten_offset + hwirq);
}
static void loongson_gpio_irq_unmask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
irq_hw_number_t hwirq = irqd_to_hwirq(data);
writeb(0x1, lgpio->reg_base + lgpio->chip_data->inten_offset + hwirq);
}
static int loongson_gpio_irq_set_type(struct irq_data *data, unsigned int type)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
irq_hw_number_t hwirq = irqd_to_hwirq(data);
u8 pol = 0, edge = 0, dual = 0;
if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
edge = 1;
dual = 1;
irq_set_handler_locked(data, handle_edge_irq);
} else {
switch (type) {
case IRQ_TYPE_LEVEL_HIGH:
pol = 1;
fallthrough;
case IRQ_TYPE_LEVEL_LOW:
irq_set_handler_locked(data, handle_level_irq);
break;
case IRQ_TYPE_EDGE_RISING:
pol = 1;
fallthrough;
case IRQ_TYPE_EDGE_FALLING:
edge = 1;
irq_set_handler_locked(data, handle_edge_irq);
break;
default:
return -EINVAL;
}
}
writeb(pol, lgpio->reg_base + lgpio->chip_data->intpol_offset + hwirq);
writeb(edge, lgpio->reg_base + lgpio->chip_data->intedge_offset + hwirq);
writeb(dual, lgpio->reg_base + lgpio->chip_data->intdual_offset + hwirq);
return 0;
}
static void loongson_gpio_ls2k0300_irq_handler(struct irq_desc *desc)
{
struct loongson_gpio_chip *lgpio = irq_desc_get_handler_data(desc);
struct irq_chip *girqchip = irq_desc_get_chip(desc);
int i;
chained_irq_enter(girqchip, desc);
for (i = 0; i < lgpio->chip.gc.ngpio; i++) {
/*
* For the GPIO controller of LS2K0300, interrupts status bits
* may be wrongly set even if the corresponding interrupt is
* disabled. Thus interrupt enable bits are checked along with
* status bits to detect interrupts reliably.
*/
if (readb(lgpio->reg_base + lgpio->chip_data->intsts_offset + i) &&
readb(lgpio->reg_base + lgpio->chip_data->inten_offset + i))
generic_handle_domain_irq(lgpio->chip.gc.irq.domain, i);
}
chained_irq_exit(girqchip, desc);
}
static const struct irq_chip loongson_gpio_ls2k0300_irqchip = {
.irq_ack = loongson_gpio_irq_ack,
.irq_mask = loongson_gpio_irq_mask,
.irq_unmask = loongson_gpio_irq_unmask,
.irq_set_type = loongson_gpio_irq_set_type,
.flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static int loongson_gpio_init_irqchip(struct platform_device *pdev,
struct loongson_gpio_chip *lgpio)
{
const struct loongson_gpio_chip_data *data = lgpio->chip_data;
struct gpio_chip *chip = &lgpio->chip.gc;
int i;
chip->irq.default_type = IRQ_TYPE_NONE;
chip->irq.handler = handle_bad_irq;
chip->irq.parent_handler = data->irq_handler;
chip->irq.parent_handler_data = lgpio;
gpio_irq_chip_set_chip(&chip->irq, data->girqchip);
chip->irq.num_parents = data->intr_num;
chip->irq.parents = devm_kcalloc(&pdev->dev, data->intr_num,
sizeof(*chip->irq.parents), GFP_KERNEL);
if (!chip->parent)
return -ENOMEM;
for (i = 0; i < data->intr_num; i++) {
int ret;
ret = platform_get_irq(pdev, i);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"failed to get IRQ %d\n", i);
chip->irq.parents[i] = ret;
}
for (i = 0; i < data->intr_num; i++) {
writeb(0x0, lgpio->reg_base + data->inten_offset + i);
writeb(0x1, lgpio->reg_base + data->intclr_offset + i);
}
return 0;
}
static int loongson_gpio_init(struct platform_device *pdev, struct loongson_gpio_chip *lgpio,
void __iomem *reg_base)
{
struct gpio_generic_chip_config config;
int ret;
lgpio->reg_base = reg_base;
if (lgpio->chip_data->mode == BIT_CTRL_MODE) {
ret = bgpio_init(&lgpio->chip, dev, 8,
lgpio->reg_base + lgpio->chip_data->in_offset,
lgpio->reg_base + lgpio->chip_data->out_offset,
NULL, NULL,
lgpio->reg_base + lgpio->chip_data->conf_offset,
0);
config = (struct gpio_generic_chip_config) {
.dev = &pdev->dev,
.sz = 8,
.dat = lgpio->reg_base + lgpio->chip_data->in_offset,
.set = lgpio->reg_base + lgpio->chip_data->out_offset,
.dirin = lgpio->reg_base + lgpio->chip_data->conf_offset,
};
ret = gpio_generic_chip_init(&lgpio->chip, &config);
if (ret) {
dev_err(dev, "unable to init generic GPIO\n");
dev_err(&pdev->dev, "unable to init generic GPIO\n");
return ret;
}
} else {
lgpio->chip.direction_input = loongson_gpio_direction_input;
lgpio->chip.get = loongson_gpio_get;
lgpio->chip.get_direction = loongson_gpio_get_direction;
lgpio->chip.direction_output = loongson_gpio_direction_output;
lgpio->chip.set = loongson_gpio_set;
lgpio->chip.parent = dev;
lgpio->chip.gc.direction_input = loongson_gpio_direction_input;
lgpio->chip.gc.get = loongson_gpio_get;
lgpio->chip.gc.get_direction = loongson_gpio_get_direction;
lgpio->chip.gc.direction_output = loongson_gpio_direction_output;
lgpio->chip.gc.set = loongson_gpio_set;
lgpio->chip.gc.parent = &pdev->dev;
spin_lock_init(&lgpio->lock);
}
lgpio->chip.label = lgpio->chip_data->label;
lgpio->chip.can_sleep = false;
if (lgpio->chip_data->inten_offset)
lgpio->chip.to_irq = loongson_gpio_to_irq;
lgpio->chip.gc.label = lgpio->chip_data->label;
lgpio->chip.gc.can_sleep = false;
if (lgpio->chip_data->girqchip) {
ret = loongson_gpio_init_irqchip(pdev, lgpio);
if (ret)
return dev_err_probe(&pdev->dev, ret, "failed to initialize irqchip\n");
} else if (lgpio->chip_data->inten_offset) {
lgpio->chip.gc.to_irq = loongson_gpio_to_irq;
}
return devm_gpiochip_add_data(dev, &lgpio->chip, lgpio);
return devm_gpiochip_add_data(&pdev->dev, &lgpio->chip.gc, lgpio);
}
static int loongson_gpio_probe(struct platform_device *pdev)
@ -175,6 +333,7 @@ static int loongson_gpio_probe(struct platform_device *pdev)
void __iomem *reg_base;
struct loongson_gpio_chip *lgpio;
struct device *dev = &pdev->dev;
struct reset_control *rst;
lgpio = devm_kzalloc(dev, sizeof(*lgpio), GFP_KERNEL);
if (!lgpio)
@ -186,7 +345,11 @@ static int loongson_gpio_probe(struct platform_device *pdev)
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
return loongson_gpio_init(dev, lgpio, reg_base);
rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
if (IS_ERR(rst))
return dev_err_probe(&pdev->dev, PTR_ERR(rst), "failed to get reset control\n");
return loongson_gpio_init(pdev, lgpio, reg_base);
}
static const struct loongson_gpio_chip_data loongson_gpio_ls2k_data = {
@ -198,6 +361,23 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k_data = {
.inten_offset = 0x30,
};
static const struct loongson_gpio_chip_data loongson_gpio_ls2k0300_data = {
.label = "ls2k0300_gpio",
.mode = BYTE_CTRL_MODE,
.conf_offset = 0x800,
.in_offset = 0xa00,
.out_offset = 0x900,
.inten_offset = 0xb00,
.intpol_offset = 0xc00,
.intedge_offset = 0xd00,
.intclr_offset = 0xe00,
.intsts_offset = 0xf00,
.intdual_offset = 0xf80,
.intr_num = 7,
.irq_handler = loongson_gpio_ls2k0300_irq_handler,
.girqchip = &loongson_gpio_ls2k0300_irqchip,
};
static const struct loongson_gpio_chip_data loongson_gpio_ls2k0500_data0 = {
.label = "ls2k0500_gpio",
.mode = BIT_CTRL_MODE,
@ -294,6 +474,10 @@ static const struct of_device_id loongson_gpio_of_match[] = {
.compatible = "loongson,ls2k-gpio",
.data = &loongson_gpio_ls2k_data,
},
{
.compatible = "loongson,ls2k0300-gpio",
.data = &loongson_gpio_ls2k0300_data,
},
{
.compatible = "loongson,ls2k0500-gpio0",
.data = &loongson_gpio_ls2k0500_data0,

View File

@ -5,10 +5,11 @@
* Copyright (C) 2015-2023 Keguang Zhang <keguang.zhang@gmail.com>
*/
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
/* Loongson 1 GPIO Register Definitions */
#define GPIO_CFG 0x0
@ -17,19 +18,18 @@
#define GPIO_OUTPUT 0x30
struct ls1x_gpio_chip {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *reg_base;
};
static int ls1x_gpio_request(struct gpio_chip *gc, unsigned int offset)
{
struct ls1x_gpio_chip *ls1x_gc = gpiochip_get_data(gc);
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&ls1x_gc->chip);
__raw_writel(__raw_readl(ls1x_gc->reg_base + GPIO_CFG) | BIT(offset),
ls1x_gc->reg_base + GPIO_CFG);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
return 0;
}
@ -37,16 +37,16 @@ static int ls1x_gpio_request(struct gpio_chip *gc, unsigned int offset)
static void ls1x_gpio_free(struct gpio_chip *gc, unsigned int offset)
{
struct ls1x_gpio_chip *ls1x_gc = gpiochip_get_data(gc);
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&ls1x_gc->chip);
__raw_writel(__raw_readl(ls1x_gc->reg_base + GPIO_CFG) & ~BIT(offset),
ls1x_gc->reg_base + GPIO_CFG);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static int ls1x_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct ls1x_gpio_chip *ls1x_gc;
int ret;
@ -59,29 +59,35 @@ static int ls1x_gpio_probe(struct platform_device *pdev)
if (IS_ERR(ls1x_gc->reg_base))
return PTR_ERR(ls1x_gc->reg_base);
ret = bgpio_init(&ls1x_gc->gc, dev, 4, ls1x_gc->reg_base + GPIO_DATA,
ls1x_gc->reg_base + GPIO_OUTPUT, NULL,
NULL, ls1x_gc->reg_base + GPIO_DIR, 0);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = ls1x_gc->reg_base + GPIO_DATA,
.set = ls1x_gc->reg_base + GPIO_OUTPUT,
.dirin = ls1x_gc->reg_base + GPIO_DIR,
};
ret = gpio_generic_chip_init(&ls1x_gc->chip, &config);
if (ret)
goto err;
ls1x_gc->gc.owner = THIS_MODULE;
ls1x_gc->gc.request = ls1x_gpio_request;
ls1x_gc->gc.free = ls1x_gpio_free;
ls1x_gc->chip.gc.owner = THIS_MODULE;
ls1x_gc->chip.gc.request = ls1x_gpio_request;
ls1x_gc->chip.gc.free = ls1x_gpio_free;
/*
* Clear ngpio to let gpiolib get the correct number
* by reading ngpios property
*/
ls1x_gc->gc.ngpio = 0;
ls1x_gc->chip.gc.ngpio = 0;
ret = devm_gpiochip_add_data(dev, &ls1x_gc->gc, ls1x_gc);
ret = devm_gpiochip_add_data(dev, &ls1x_gc->chip.gc, ls1x_gc);
if (ret)
goto err;
platform_set_drvdata(pdev, ls1x_gc);
dev_info(dev, "GPIO controller registered with %d pins\n",
ls1x_gc->gc.ngpio);
ls1x_gc->chip.gc.ngpio);
return 0;
err:

257
drivers/gpio/gpio-max7360.c Normal file
View File

@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2025 Bootlin
*
* Author: Kamel BOUHARA <kamel.bouhara@bootlin.com>
* Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
*/
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/regmap.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mfd/max7360.h>
#include <linux/minmax.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#define MAX7360_GPIO_PORT 1
#define MAX7360_GPIO_COL 2
struct max7360_gpio_plat_data {
unsigned int function;
};
static struct max7360_gpio_plat_data max7360_gpio_port_plat = { .function = MAX7360_GPIO_PORT };
static struct max7360_gpio_plat_data max7360_gpio_col_plat = { .function = MAX7360_GPIO_COL };
static int max7360_get_available_gpos(struct device *dev, unsigned int *available_gpios)
{
u32 columns;
int ret;
ret = device_property_read_u32(dev->parent, "keypad,num-columns", &columns);
if (ret) {
dev_err(dev, "Failed to read columns count\n");
return ret;
}
*available_gpios = min(MAX7360_MAX_GPO, MAX7360_MAX_KEY_COLS - columns);
return 0;
}
static int max7360_gpo_init_valid_mask(struct gpio_chip *gc,
unsigned long *valid_mask,
unsigned int ngpios)
{
unsigned int available_gpios;
int ret;
ret = max7360_get_available_gpos(gc->parent, &available_gpios);
if (ret)
return ret;
bitmap_clear(valid_mask, 0, MAX7360_MAX_KEY_COLS - available_gpios);
return 0;
}
static int max7360_set_gpos_count(struct device *dev, struct regmap *regmap)
{
/*
* MAX7360 COL0 to COL7 pins can be used either as keypad columns,
* general purpose output or a mix of both.
* By default, all pins are used as keypad, here we update this
* configuration to allow to use some of them as GPIOs.
*/
unsigned int available_gpios;
unsigned int val;
int ret;
ret = max7360_get_available_gpos(dev, &available_gpios);
if (ret)
return ret;
/*
* Configure which GPIOs will be used for keypad.
* MAX7360_REG_DEBOUNCE contains configuration both for keypad debounce
* timings and gpos/keypad columns repartition. Only the later is
* modified here.
*/
val = FIELD_PREP(MAX7360_PORTS, available_gpios);
ret = regmap_write_bits(regmap, MAX7360_REG_DEBOUNCE, MAX7360_PORTS, val);
if (ret)
dev_err(dev, "Failed to write max7360 columns/gpos configuration");
return ret;
}
static int max7360_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
if (base == MAX7360_REG_PWMBASE) {
/*
* GPIO output is using PWM duty cycle registers: one register
* per line, with value being either 0 or 255.
*/
*reg = base + offset;
*mask = GENMASK(7, 0);
} else {
*reg = base;
*mask = BIT(offset);
}
return 0;
}
static const struct regmap_irq max7360_regmap_irqs[MAX7360_MAX_GPIO] = {
REGMAP_IRQ_REG(0, 0, BIT(0)),
REGMAP_IRQ_REG(1, 0, BIT(1)),
REGMAP_IRQ_REG(2, 0, BIT(2)),
REGMAP_IRQ_REG(3, 0, BIT(3)),
REGMAP_IRQ_REG(4, 0, BIT(4)),
REGMAP_IRQ_REG(5, 0, BIT(5)),
REGMAP_IRQ_REG(6, 0, BIT(6)),
REGMAP_IRQ_REG(7, 0, BIT(7)),
};
static int max7360_handle_mask_sync(const int index,
const unsigned int mask_buf_def,
const unsigned int mask_buf,
void *const irq_drv_data)
{
struct regmap *regmap = irq_drv_data;
int ret;
for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {
ret = regmap_assign_bits(regmap, MAX7360_REG_PWMCFG(i),
MAX7360_PORT_CFG_INTERRUPT_MASK, mask_buf & BIT(i));
if (ret)
return ret;
}
return 0;
}
static int max7360_gpio_probe(struct platform_device *pdev)
{
const struct max7360_gpio_plat_data *plat_data;
struct gpio_regmap_config gpio_config = { };
struct regmap_irq_chip *irq_chip;
struct device *dev = &pdev->dev;
struct regmap *regmap;
unsigned int outconf;
int ret;
regmap = dev_get_regmap(dev->parent, NULL);
if (!regmap)
return dev_err_probe(dev, -ENODEV, "could not get parent regmap\n");
plat_data = device_get_match_data(dev);
if (plat_data->function == MAX7360_GPIO_PORT) {
if (device_property_read_bool(dev, "interrupt-controller")) {
/*
* Port GPIOs with interrupt-controller property: add IRQ
* controller.
*/
gpio_config.regmap_irq_flags = IRQF_ONESHOT | IRQF_SHARED;
gpio_config.regmap_irq_line =
fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti");
if (gpio_config.regmap_irq_line < 0)
return dev_err_probe(dev, gpio_config.regmap_irq_line,
"Failed to get IRQ\n");
/* Create custom IRQ configuration. */
irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL);
gpio_config.regmap_irq_chip = irq_chip;
if (!irq_chip)
return -ENOMEM;
irq_chip->name = dev_name(dev);
irq_chip->status_base = MAX7360_REG_GPIOIN;
irq_chip->status_is_level = true;
irq_chip->num_regs = 1;
irq_chip->num_irqs = MAX7360_MAX_GPIO;
irq_chip->irqs = max7360_regmap_irqs;
irq_chip->handle_mask_sync = max7360_handle_mask_sync;
irq_chip->irq_drv_data = regmap;
for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {
ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),
MAX7360_PORT_CFG_INTERRUPT_EDGES,
MAX7360_PORT_CFG_INTERRUPT_EDGES);
if (ret)
return dev_err_probe(dev, ret,
"Failed to enable interrupts\n");
}
}
/*
* Port GPIOs: set output mode configuration (constant-current or not).
* This property is optional.
*/
ret = device_property_read_u32(dev, "maxim,constant-current-disable", &outconf);
if (!ret) {
ret = regmap_write(regmap, MAX7360_REG_GPIOOUTM, outconf);
if (ret)
return dev_err_probe(dev, ret,
"Failed to set constant-current configuration\n");
}
}
/* Add gpio device. */
gpio_config.parent = dev;
gpio_config.regmap = regmap;
if (plat_data->function == MAX7360_GPIO_PORT) {
gpio_config.ngpio = MAX7360_MAX_GPIO;
gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOIN);
gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PWMBASE);
gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOCTRL);
gpio_config.ngpio_per_reg = MAX7360_MAX_GPIO;
gpio_config.reg_mask_xlate = max7360_gpio_reg_mask_xlate;
} else {
ret = max7360_set_gpos_count(dev, regmap);
if (ret)
return dev_err_probe(dev, ret, "Failed to set GPOS pin count\n");
gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PORTS);
gpio_config.ngpio = MAX7360_MAX_KEY_COLS;
gpio_config.init_valid_mask = max7360_gpo_init_valid_mask;
}
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
}
static const struct of_device_id max7360_gpio_of_match[] = {
{
.compatible = "maxim,max7360-gpo",
.data = &max7360_gpio_col_plat
}, {
.compatible = "maxim,max7360-gpio",
.data = &max7360_gpio_port_plat
}, {
}
};
MODULE_DEVICE_TABLE(of, max7360_gpio_of_match);
static struct platform_driver max7360_gpio_driver = {
.driver = {
.name = "max7360-gpio",
.of_match_table = max7360_gpio_of_match,
},
.probe = max7360_gpio_probe,
};
module_platform_driver(max7360_gpio_driver);
MODULE_DESCRIPTION("MAX7360 GPIO driver");
MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>");
MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");
MODULE_LICENSE("GPL");

View File

@ -12,6 +12,7 @@
#include <linux/mcb.h>
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#define MEN_Z127_CTRL 0x00
#define MEN_Z127_PSR 0x04
@ -30,7 +31,7 @@
(db <= MEN_Z127_DB_MAX_US))
struct men_z127_gpio {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *reg_base;
struct resource *mem;
};
@ -64,7 +65,7 @@ static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio,
debounce /= 50;
}
raw_spin_lock(&gc->bgpio_lock);
guard(gpio_generic_lock)(&priv->chip);
db_en = readl(priv->reg_base + MEN_Z127_DBER);
@ -79,8 +80,6 @@ static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio,
writel(db_en, priv->reg_base + MEN_Z127_DBER);
writel(db_cnt, priv->reg_base + GPIO_TO_DBCNT_REG(gpio));
raw_spin_unlock(&gc->bgpio_lock);
return 0;
}
@ -91,7 +90,8 @@ static int men_z127_set_single_ended(struct gpio_chip *gc,
struct men_z127_gpio *priv = gpiochip_get_data(gc);
u32 od_en;
raw_spin_lock(&gc->bgpio_lock);
guard(gpio_generic_lock)(&priv->chip);
od_en = readl(priv->reg_base + MEN_Z127_ODER);
if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN)
@ -101,7 +101,6 @@ static int men_z127_set_single_ended(struct gpio_chip *gc,
od_en &= ~BIT(offset);
writel(od_en, priv->reg_base + MEN_Z127_ODER);
raw_spin_unlock(&gc->bgpio_lock);
return 0;
}
@ -137,6 +136,7 @@ static void men_z127_release_mem(void *data)
static int men_z127_probe(struct mcb_device *mdev,
const struct mcb_device_id *id)
{
struct gpio_generic_chip_config config;
struct men_z127_gpio *men_z127_gpio;
struct device *dev = &mdev->dev;
int ret;
@ -163,18 +163,21 @@ static int men_z127_probe(struct mcb_device *mdev,
mcb_set_drvdata(mdev, men_z127_gpio);
ret = bgpio_init(&men_z127_gpio->gc, &mdev->dev, 4,
men_z127_gpio->reg_base + MEN_Z127_PSR,
men_z127_gpio->reg_base + MEN_Z127_CTRL,
NULL,
men_z127_gpio->reg_base + MEN_Z127_GPIODR,
NULL, 0);
config = (struct gpio_generic_chip_config) {
.dev = &mdev->dev,
.sz = 4,
.dat = men_z127_gpio->reg_base + MEN_Z127_PSR,
.set = men_z127_gpio->reg_base + MEN_Z127_CTRL,
.dirout = men_z127_gpio->reg_base + MEN_Z127_GPIODR,
};
ret = gpio_generic_chip_init(&men_z127_gpio->chip, &config);
if (ret)
return ret;
men_z127_gpio->gc.set_config = men_z127_set_config;
men_z127_gpio->chip.gc.set_config = men_z127_set_config;
ret = devm_gpiochip_add_data(dev, &men_z127_gpio->gc, men_z127_gpio);
ret = devm_gpiochip_add_data(dev, &men_z127_gpio->chip.gc, men_z127_gpio);
if (ret)
return dev_err_probe(dev, ret,
"failed to register MEN 16Z127 GPIO controller");

View File

@ -4,6 +4,7 @@
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -37,7 +38,7 @@ struct mlxbf_gpio_context_save_regs {
/* Device state structure. */
struct mlxbf_gpio_state {
struct gpio_chip gc;
struct gpio_generic_chip chip;
/* Memory Address */
void __iomem *base;
@ -49,6 +50,7 @@ struct mlxbf_gpio_state {
static int mlxbf_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct mlxbf_gpio_state *gs;
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
@ -62,21 +64,24 @@ static int mlxbf_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gs->base))
return PTR_ERR(gs->base);
gc = &gs->gc;
ret = bgpio_init(gc, dev, 8,
gs->base + MLXBF_GPIO_PIN_STATE,
NULL,
NULL,
gs->base + MLXBF_GPIO_PIN_DIR_O,
gs->base + MLXBF_GPIO_PIN_DIR_I,
0);
gc = &gs->chip.gc;
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 8,
.dat = gs->base + MLXBF_GPIO_PIN_STATE,
.dirout = gs->base + MLXBF_GPIO_PIN_DIR_O,
.dirin = gs->base + MLXBF_GPIO_PIN_DIR_I,
};
ret = gpio_generic_chip_init(&gs->chip, &config);
if (ret)
return -ENODEV;
gc->owner = THIS_MODULE;
gc->ngpio = MLXBF_GPIO_NR;
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
ret = devm_gpiochip_add_data(dev, &gs->chip.gc, gs);
if (ret) {
dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n");
return ret;

View File

@ -6,8 +6,10 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
@ -65,7 +67,7 @@ struct mlxbf2_gpio_context_save_regs {
/* BlueField-2 gpio block context structure. */
struct mlxbf2_gpio_context {
struct gpio_chip gc;
struct gpio_generic_chip chip;
/* YU GPIO blocks address */
void __iomem *gpio_io;
@ -132,7 +134,7 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
u32 arm_gpio_lock_val;
mutex_lock(yu_arm_gpio_lock_param.lock);
raw_spin_lock(&gs->gc.bgpio_lock);
gpio_generic_chip_lock(&gs->chip);
arm_gpio_lock_val = readl(yu_arm_gpio_lock_param.io);
@ -140,7 +142,7 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
* When lock active bit[31] is set, ModeX is write enabled
*/
if (YU_LOCK_ACTIVE_BIT(arm_gpio_lock_val)) {
raw_spin_unlock(&gs->gc.bgpio_lock);
gpio_generic_chip_unlock(&gs->chip);
mutex_unlock(yu_arm_gpio_lock_param.lock);
return -EINVAL;
}
@ -154,11 +156,11 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
* Release the YU arm_gpio_lock after changing the direction mode.
*/
static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs)
__releases(&gs->gc.bgpio_lock)
__releases(&gs->chip.lock)
__releases(yu_arm_gpio_lock_param.lock)
{
writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io);
raw_spin_unlock(&gs->gc.bgpio_lock);
gpio_generic_chip_unlock(&gs->chip);
mutex_unlock(yu_arm_gpio_lock_param.lock);
}
@ -235,11 +237,10 @@ static void mlxbf2_gpio_irq_enable(struct irq_data *irqd)
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(irqd);
unsigned long flags;
u32 val;
gpiochip_enable_irq(gc, irqd_to_hwirq(irqd));
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&gs->chip);
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
val |= BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
@ -247,7 +248,6 @@ static void mlxbf2_gpio_irq_enable(struct irq_data *irqd)
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
val |= BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
}
static void mlxbf2_gpio_irq_disable(struct irq_data *irqd)
@ -255,21 +255,21 @@ static void mlxbf2_gpio_irq_disable(struct irq_data *irqd)
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(irqd);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
val &= ~BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
scoped_guard(gpio_generic_lock_irqsave, &gs->chip) {
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
val &= ~BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
}
gpiochip_disable_irq(gc, irqd_to_hwirq(irqd));
}
static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr)
{
struct mlxbf2_gpio_context *gs = ptr;
struct gpio_chip *gc = &gs->gc;
struct gpio_chip *gc = &gs->chip.gc;
unsigned long pending;
u32 level;
@ -288,7 +288,6 @@ mlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(irqd);
unsigned long flags;
bool fall = false;
bool rise = false;
u32 val;
@ -308,7 +307,8 @@ mlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
return -EINVAL;
}
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&gs->chip);
if (fall) {
val = readl(gs->gpio_io + YU_GPIO_CAUSE_FALL_EN);
val |= BIT(offset);
@ -320,7 +320,6 @@ mlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
val |= BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_RISE_EN);
}
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
return 0;
}
@ -347,6 +346,7 @@ static const struct irq_chip mlxbf2_gpio_irq_chip = {
static int
mlxbf2_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct mlxbf2_gpio_context *gs;
struct device *dev = &pdev->dev;
struct gpio_irq_chip *girq;
@ -369,28 +369,25 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
return PTR_ERR(gs->gpio_io);
ret = mlxbf2_gpio_get_lock_res(pdev);
if (ret) {
dev_err(dev, "Failed to get yu_arm_gpio_lock resource\n");
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "Failed to get yu_arm_gpio_lock resource\n");
if (device_property_read_u32(dev, "npins", &npins))
npins = MLXBF2_GPIO_MAX_PINS_PER_BLOCK;
gc = &gs->gc;
gc = &gs->chip.gc;
ret = bgpio_init(gc, dev, 4,
gs->gpio_io + YU_GPIO_DATAIN,
gs->gpio_io + YU_GPIO_DATASET,
gs->gpio_io + YU_GPIO_DATACLEAR,
NULL,
NULL,
0);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = gs->gpio_io + YU_GPIO_DATAIN,
.set = gs->gpio_io + YU_GPIO_DATASET,
.clr = gs->gpio_io + YU_GPIO_DATACLEAR,
};
if (ret) {
dev_err(dev, "bgpio_init failed\n");
return ret;
}
ret = gpio_generic_chip_init(&gs->chip, &config);
if (ret)
return dev_err_probe(dev, ret, "failed to initialize the generic GPIO chip\n");
gc->direction_input = mlxbf2_gpio_direction_input;
gc->direction_output = mlxbf2_gpio_direction_output;
@ -399,7 +396,7 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
irq = platform_get_irq_optional(pdev, 0);
if (irq >= 0) {
girq = &gs->gc.irq;
girq = &gs->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &mlxbf2_gpio_irq_chip);
girq->handler = handle_simple_irq;
girq->default_type = IRQ_TYPE_NONE;
@ -414,19 +411,15 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
*/
ret = devm_request_irq(dev, irq, mlxbf2_gpio_irq_handler,
IRQF_SHARED, name, gs);
if (ret) {
dev_err(dev, "failed to request IRQ");
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "failed to request IRQ");
}
platform_set_drvdata(pdev, gs);
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
if (ret) {
dev_err(dev, "Failed adding memory mapped gpiochip\n");
return ret;
}
ret = devm_gpiochip_add_data(dev, &gs->chip.gc, gs);
if (ret)
return dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n");
return 0;
}

View File

@ -6,6 +6,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
@ -42,7 +43,7 @@
#define MLXBF_GPIO_CLR_ALL_INTS GENMASK(31, 0)
struct mlxbf3_gpio_context {
struct gpio_chip gc;
struct gpio_generic_chip chip;
/* YU GPIO block address */
void __iomem *gpio_set_io;
@ -58,18 +59,17 @@ static void mlxbf3_gpio_irq_enable(struct irq_data *irqd)
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
irq_hw_number_t offset = irqd_to_hwirq(irqd);
unsigned long flags;
u32 val;
gpiochip_enable_irq(gc, offset);
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&gs->chip);
writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE);
val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
val |= BIT(offset);
writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
}
static void mlxbf3_gpio_irq_disable(struct irq_data *irqd)
@ -77,16 +77,15 @@ static void mlxbf3_gpio_irq_disable(struct irq_data *irqd)
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
irq_hw_number_t offset = irqd_to_hwirq(irqd);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
val &= ~BIT(offset);
writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
scoped_guard(gpio_generic_lock_irqsave, &gs->chip) {
val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
val &= ~BIT(offset);
writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE);
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE);
}
gpiochip_disable_irq(gc, offset);
}
@ -94,7 +93,7 @@ static void mlxbf3_gpio_irq_disable(struct irq_data *irqd)
static irqreturn_t mlxbf3_gpio_irq_handler(int irq, void *ptr)
{
struct mlxbf3_gpio_context *gs = ptr;
struct gpio_chip *gc = &gs->gc;
struct gpio_chip *gc = &gs->chip.gc;
unsigned long pending;
u32 level;
@ -113,37 +112,33 @@ mlxbf3_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc);
irq_hw_number_t offset = irqd_to_hwirq(irqd);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_BOTH:
val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
break;
case IRQ_TYPE_EDGE_RISING:
val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
break;
case IRQ_TYPE_EDGE_FALLING:
val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
break;
default:
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
return -EINVAL;
scoped_guard(gpio_generic_lock_irqsave, &gs->chip) {
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_BOTH:
val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
break;
case IRQ_TYPE_EDGE_RISING:
val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN);
break;
case IRQ_TYPE_EDGE_FALLING:
val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN);
break;
default:
return -EINVAL;
}
}
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
irq_set_handler_locked(irqd, handle_edge_irq);
return 0;
@ -186,6 +181,7 @@ static int mlxbf3_gpio_add_pin_ranges(struct gpio_chip *chip)
static int mlxbf3_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct mlxbf3_gpio_context *gs;
struct gpio_irq_chip *girq;
@ -211,16 +207,23 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
gs->gpio_clr_io = devm_platform_ioremap_resource(pdev, 3);
if (IS_ERR(gs->gpio_clr_io))
return PTR_ERR(gs->gpio_clr_io);
gc = &gs->gc;
gc = &gs->chip.gc;
ret = bgpio_init(gc, dev, 4,
gs->gpio_io + MLXBF_GPIO_READ_DATA_IN,
gs->gpio_set_io + MLXBF_GPIO_FW_DATA_OUT_SET,
gs->gpio_clr_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR,
gs->gpio_set_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET,
gs->gpio_clr_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR, 0);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = gs->gpio_io + MLXBF_GPIO_READ_DATA_IN,
.set = gs->gpio_set_io + MLXBF_GPIO_FW_DATA_OUT_SET,
.clr = gs->gpio_clr_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR,
.dirout = gs->gpio_set_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET,
.dirin = gs->gpio_clr_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR,
};
ret = gpio_generic_chip_init(&gs->chip, &config);
if (ret)
return dev_err_probe(dev, ret, "%s: bgpio_init() failed", __func__);
return dev_err_probe(dev, ret,
"%s: failed to initialize the generic GPIO chip",
__func__);
gc->request = gpiochip_generic_request;
gc->free = gpiochip_generic_free;
@ -229,7 +232,7 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
irq = platform_get_irq_optional(pdev, 0);
if (irq >= 0) {
girq = &gs->gc.irq;
girq = &gs->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip);
girq->default_type = IRQ_TYPE_NONE;
/* This will let us handle the parent IRQ in the driver */
@ -250,7 +253,7 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, gs);
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
ret = devm_gpiochip_add_data(dev, gc, gs);
if (ret)
dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n");

View File

@ -57,6 +57,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include <linux/types.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include "gpiolib.h"
@ -124,20 +125,23 @@ static unsigned long bgpio_read32be(void __iomem *reg)
static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int line)
{
if (gc->be_bits)
return BIT(gc->bgpio_bits - 1 - line);
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
if (chip->be_bits)
return BIT(chip->bits - 1 - line);
return BIT(line);
}
static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long pinmask = bgpio_line2mask(gc, gpio);
bool dir = !!(gc->bgpio_dir & pinmask);
bool dir = !!(chip->sdir & pinmask);
if (dir)
return !!(gc->read_reg(gc->reg_set) & pinmask);
else
return !!(gc->read_reg(gc->reg_dat) & pinmask);
return !!(chip->read_reg(chip->reg_set) & pinmask);
return !!(chip->read_reg(chip->reg_dat) & pinmask);
}
/*
@ -147,26 +151,28 @@ static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
static int bgpio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
unsigned long get_mask = 0;
unsigned long set_mask = 0;
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long get_mask = 0, set_mask = 0;
/* Make sure we first clear any bits that are zero when we read the register */
*bits &= ~*mask;
set_mask = *mask & gc->bgpio_dir;
get_mask = *mask & ~gc->bgpio_dir;
set_mask = *mask & chip->sdir;
get_mask = *mask & ~chip->sdir;
if (set_mask)
*bits |= gc->read_reg(gc->reg_set) & set_mask;
*bits |= chip->read_reg(chip->reg_set) & set_mask;
if (get_mask)
*bits |= gc->read_reg(gc->reg_dat) & get_mask;
*bits |= chip->read_reg(chip->reg_dat) & get_mask;
return 0;
}
static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
{
return !!(gc->read_reg(gc->reg_dat) & bgpio_line2mask(gc, gpio));
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
return !!(chip->read_reg(chip->reg_dat) & bgpio_line2mask(gc, gpio));
}
/*
@ -175,9 +181,11 @@ static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
/* Make sure we first clear any bits that are zero when we read the register */
*bits &= ~*mask;
*bits |= gc->read_reg(gc->reg_dat) & *mask;
*bits |= chip->read_reg(chip->reg_dat) & *mask;
return 0;
}
@ -187,6 +195,7 @@ static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long readmask = 0;
unsigned long val;
int bit;
@ -199,7 +208,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
readmask |= bgpio_line2mask(gc, bit);
/* Read the register */
val = gc->read_reg(gc->reg_dat) & readmask;
val = chip->read_reg(chip->reg_dat) & readmask;
/*
* Mirror the result into the "bits" result, this will give line 0
@ -218,19 +227,20 @@ static int bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val)
static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long mask = bgpio_line2mask(gc, gpio);
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
raw_spin_lock_irqsave(&chip->lock, flags);
if (val)
gc->bgpio_data |= mask;
chip->sdata |= mask;
else
gc->bgpio_data &= ~mask;
chip->sdata &= ~mask;
gc->write_reg(gc->reg_dat, gc->bgpio_data);
chip->write_reg(chip->reg_dat, chip->sdata);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
raw_spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
@ -238,31 +248,32 @@ static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
int val)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long mask = bgpio_line2mask(gc, gpio);
if (val)
gc->write_reg(gc->reg_set, mask);
chip->write_reg(chip->reg_set, mask);
else
gc->write_reg(gc->reg_clr, mask);
chip->write_reg(chip->reg_clr, mask);
return 0;
}
static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
unsigned long mask = bgpio_line2mask(gc, gpio);
unsigned long flags;
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long mask = bgpio_line2mask(gc, gpio), flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
raw_spin_lock_irqsave(&chip->lock, flags);
if (val)
gc->bgpio_data |= mask;
chip->sdata |= mask;
else
gc->bgpio_data &= ~mask;
chip->sdata &= ~mask;
gc->write_reg(gc->reg_set, gc->bgpio_data);
chip->write_reg(chip->reg_set, chip->sdata);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
raw_spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
@ -272,12 +283,13 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc,
unsigned long *set_mask,
unsigned long *clear_mask)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
int i;
*set_mask = 0;
*clear_mask = 0;
for_each_set_bit(i, mask, gc->bgpio_bits) {
for_each_set_bit(i, mask, chip->bits) {
if (test_bit(i, bits))
*set_mask |= bgpio_line2mask(gc, i);
else
@ -290,25 +302,27 @@ static void bgpio_set_multiple_single_reg(struct gpio_chip *gc,
unsigned long *bits,
void __iomem *reg)
{
unsigned long flags;
unsigned long set_mask, clear_mask;
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long flags, set_mask, clear_mask;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
raw_spin_lock_irqsave(&chip->lock, flags);
bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
gc->bgpio_data |= set_mask;
gc->bgpio_data &= ~clear_mask;
chip->sdata |= set_mask;
chip->sdata &= ~clear_mask;
gc->write_reg(reg, gc->bgpio_data);
chip->write_reg(reg, chip->sdata);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
raw_spin_unlock_irqrestore(&chip->lock, flags);
}
static int bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_dat);
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
bgpio_set_multiple_single_reg(gc, mask, bits, chip->reg_dat);
return 0;
}
@ -316,7 +330,9 @@ static int bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
static int bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_set);
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
bgpio_set_multiple_single_reg(gc, mask, bits, chip->reg_set);
return 0;
}
@ -325,21 +341,24 @@ static int bgpio_set_multiple_with_clear(struct gpio_chip *gc,
unsigned long *mask,
unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long set_mask, clear_mask;
bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
if (set_mask)
gc->write_reg(gc->reg_set, set_mask);
chip->write_reg(chip->reg_set, set_mask);
if (clear_mask)
gc->write_reg(gc->reg_clr, clear_mask);
chip->write_reg(chip->reg_clr, clear_mask);
return 0;
}
static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out)
{
if (!gc->bgpio_pinctrl)
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
if (!chip->pinctrl)
return 0;
if (dir_out)
@ -374,39 +393,42 @@ static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
raw_spin_lock_irqsave(&chip->lock, flags);
gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio);
chip->sdir &= ~bgpio_line2mask(gc, gpio);
if (gc->reg_dir_in)
gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir);
if (gc->reg_dir_out)
gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
if (chip->reg_dir_in)
chip->write_reg(chip->reg_dir_in, ~chip->sdir);
if (chip->reg_dir_out)
chip->write_reg(chip->reg_dir_out, chip->sdir);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
raw_spin_unlock_irqrestore(&chip->lock, flags);
return bgpio_dir_return(gc, gpio, false);
}
static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
/* Return 0 if output, 1 if input */
if (gc->bgpio_dir_unreadable) {
if (gc->bgpio_dir & bgpio_line2mask(gc, gpio))
if (chip->dir_unreadable) {
if (chip->sdir & bgpio_line2mask(gc, gpio))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
if (gc->reg_dir_out) {
if (gc->read_reg(gc->reg_dir_out) & bgpio_line2mask(gc, gpio))
if (chip->reg_dir_out) {
if (chip->read_reg(chip->reg_dir_out) & bgpio_line2mask(gc, gpio))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
if (gc->reg_dir_in)
if (!(gc->read_reg(gc->reg_dir_in) & bgpio_line2mask(gc, gpio)))
if (chip->reg_dir_in)
if (!(chip->read_reg(chip->reg_dir_in) & bgpio_line2mask(gc, gpio)))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
@ -414,18 +436,19 @@ static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
raw_spin_lock_irqsave(&chip->lock, flags);
gc->bgpio_dir |= bgpio_line2mask(gc, gpio);
chip->sdir |= bgpio_line2mask(gc, gpio);
if (gc->reg_dir_in)
gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir);
if (gc->reg_dir_out)
gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
if (chip->reg_dir_in)
chip->write_reg(chip->reg_dir_in, ~chip->sdir);
if (chip->reg_dir_out)
chip->write_reg(chip->reg_dir_out, chip->sdir);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
raw_spin_unlock_irqrestore(&chip->lock, flags);
}
static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
@ -445,31 +468,30 @@ static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
}
static int bgpio_setup_accessors(struct device *dev,
struct gpio_chip *gc,
struct gpio_generic_chip *chip,
bool byte_be)
{
switch (gc->bgpio_bits) {
switch (chip->bits) {
case 8:
gc->read_reg = bgpio_read8;
gc->write_reg = bgpio_write8;
chip->read_reg = bgpio_read8;
chip->write_reg = bgpio_write8;
break;
case 16:
if (byte_be) {
gc->read_reg = bgpio_read16be;
gc->write_reg = bgpio_write16be;
chip->read_reg = bgpio_read16be;
chip->write_reg = bgpio_write16be;
} else {
gc->read_reg = bgpio_read16;
gc->write_reg = bgpio_write16;
chip->read_reg = bgpio_read16;
chip->write_reg = bgpio_write16;
}
break;
case 32:
if (byte_be) {
gc->read_reg = bgpio_read32be;
gc->write_reg = bgpio_write32be;
chip->read_reg = bgpio_read32be;
chip->write_reg = bgpio_write32be;
} else {
gc->read_reg = bgpio_read32;
gc->write_reg = bgpio_write32;
chip->read_reg = bgpio_read32;
chip->write_reg = bgpio_write32;
}
break;
#if BITS_PER_LONG >= 64
@ -479,13 +501,13 @@ static int bgpio_setup_accessors(struct device *dev,
"64 bit big endian byte order unsupported\n");
return -EINVAL;
} else {
gc->read_reg = bgpio_read64;
gc->write_reg = bgpio_write64;
chip->read_reg = bgpio_read64;
chip->write_reg = bgpio_write64;
}
break;
#endif /* BITS_PER_LONG >= 64 */
default:
dev_err(dev, "unsupported data width %u bits\n", gc->bgpio_bits);
dev_err(dev, "unsupported data width %u bits\n", chip->bits);
return -EINVAL;
}
@ -514,27 +536,25 @@ static int bgpio_setup_accessors(struct device *dev,
* - an input direction register (named "dirin") where a 1 bit indicates
* the GPIO is an input.
*/
static int bgpio_setup_io(struct gpio_chip *gc,
void __iomem *dat,
void __iomem *set,
void __iomem *clr,
unsigned long flags)
static int bgpio_setup_io(struct gpio_generic_chip *chip,
const struct gpio_generic_chip_config *cfg)
{
struct gpio_chip *gc = &chip->gc;
gc->reg_dat = dat;
if (!gc->reg_dat)
chip->reg_dat = cfg->dat;
if (!chip->reg_dat)
return -EINVAL;
if (set && clr) {
gc->reg_set = set;
gc->reg_clr = clr;
if (cfg->set && cfg->clr) {
chip->reg_set = cfg->set;
chip->reg_clr = cfg->clr;
gc->set = bgpio_set_with_clear;
gc->set_multiple = bgpio_set_multiple_with_clear;
} else if (set && !clr) {
gc->reg_set = set;
} else if (cfg->set && !cfg->clr) {
chip->reg_set = cfg->set;
gc->set = bgpio_set_set;
gc->set_multiple = bgpio_set_multiple_set;
} else if (flags & BGPIOF_NO_OUTPUT) {
} else if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) {
gc->set = bgpio_set_none;
gc->set_multiple = NULL;
} else {
@ -542,10 +562,10 @@ static int bgpio_setup_io(struct gpio_chip *gc,
gc->set_multiple = bgpio_set_multiple;
}
if (!(flags & BGPIOF_UNREADABLE_REG_SET) &&
(flags & BGPIOF_READ_OUTPUT_REG_SET)) {
if (!(cfg->flags & GPIO_GENERIC_UNREADABLE_REG_SET) &&
(cfg->flags & GPIO_GENERIC_READ_OUTPUT_REG_SET)) {
gc->get = bgpio_get_set;
if (!gc->be_bits)
if (!chip->be_bits)
gc->get_multiple = bgpio_get_set_multiple;
/*
* We deliberately avoid assigning the ->get_multiple() call
@ -556,7 +576,7 @@ static int bgpio_setup_io(struct gpio_chip *gc,
*/
} else {
gc->get = bgpio_get;
if (gc->be_bits)
if (chip->be_bits)
gc->get_multiple = bgpio_get_multiple_be;
else
gc->get_multiple = bgpio_get_multiple;
@ -565,27 +585,27 @@ static int bgpio_setup_io(struct gpio_chip *gc,
return 0;
}
static int bgpio_setup_direction(struct gpio_chip *gc,
void __iomem *dirout,
void __iomem *dirin,
unsigned long flags)
static int bgpio_setup_direction(struct gpio_generic_chip *chip,
const struct gpio_generic_chip_config *cfg)
{
if (dirout || dirin) {
gc->reg_dir_out = dirout;
gc->reg_dir_in = dirin;
if (flags & BGPIOF_NO_SET_ON_INPUT)
struct gpio_chip *gc = &chip->gc;
if (cfg->dirout || cfg->dirin) {
chip->reg_dir_out = cfg->dirout;
chip->reg_dir_in = cfg->dirin;
if (cfg->flags & GPIO_GENERIC_NO_SET_ON_INPUT)
gc->direction_output = bgpio_dir_out_dir_first;
else
gc->direction_output = bgpio_dir_out_val_first;
gc->direction_input = bgpio_dir_in;
gc->get_direction = bgpio_get_dir;
} else {
if (flags & BGPIOF_NO_OUTPUT)
if (cfg->flags & GPIO_GENERIC_NO_OUTPUT)
gc->direction_output = bgpio_dir_out_err;
else
gc->direction_output = bgpio_simple_dir_out;
if (flags & BGPIOF_NO_INPUT)
if (cfg->flags & GPIO_GENERIC_NO_INPUT)
gc->direction_input = bgpio_dir_in_err;
else
gc->direction_input = bgpio_simple_dir_in;
@ -594,117 +614,101 @@ static int bgpio_setup_direction(struct gpio_chip *gc,
return 0;
}
static int bgpio_request(struct gpio_chip *chip, unsigned gpio_pin)
static int bgpio_request(struct gpio_chip *gc, unsigned int gpio_pin)
{
if (gpio_pin >= chip->ngpio)
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
if (gpio_pin >= gc->ngpio)
return -EINVAL;
if (chip->bgpio_pinctrl)
return gpiochip_generic_request(chip, gpio_pin);
if (chip->pinctrl)
return gpiochip_generic_request(gc, gpio_pin);
return 0;
}
/**
* bgpio_init() - Initialize generic GPIO accessor functions
* @gc: the GPIO chip to set up
* @dev: the parent device of the new GPIO chip (compulsory)
* @sz: the size (width) of the MMIO registers in bytes, typically 1, 2 or 4
* @dat: MMIO address for the register to READ the value of the GPIO lines, it
* is expected that a 1 in the corresponding bit in this register means the
* line is asserted
* @set: MMIO address for the register to SET the value of the GPIO lines, it is
* expected that we write the line with 1 in this register to drive the GPIO line
* high.
* @clr: MMIO address for the register to CLEAR the value of the GPIO lines, it is
* expected that we write the line with 1 in this register to drive the GPIO line
* low. It is allowed to leave this address as NULL, in that case the SET register
* will be assumed to also clear the GPIO lines, by actively writing the line
* with 0.
* @dirout: MMIO address for the register to set the line as OUTPUT. It is assumed
* that setting a line to 1 in this register will turn that line into an
* output line. Conversely, setting the line to 0 will turn that line into
* an input.
* @dirin: MMIO address for the register to set this line as INPUT. It is assumed
* that setting a line to 1 in this register will turn that line into an
* input line. Conversely, setting the line to 0 will turn that line into
* an output.
* @flags: Different flags that will affect the behaviour of the device, such as
* endianness etc.
* gpio_generic_chip_init() - Initialize a generic GPIO chip.
* @chip: Generic GPIO chip to set up.
* @cfg: Generic GPIO chip configuration.
*
* Returns 0 on success, negative error number on failure.
*/
int bgpio_init(struct gpio_chip *gc, struct device *dev,
unsigned long sz, void __iomem *dat, void __iomem *set,
void __iomem *clr, void __iomem *dirout, void __iomem *dirin,
unsigned long flags)
int gpio_generic_chip_init(struct gpio_generic_chip *chip,
const struct gpio_generic_chip_config *cfg)
{
struct gpio_chip *gc = &chip->gc;
unsigned long flags = cfg->flags;
struct device *dev = cfg->dev;
int ret;
if (!is_power_of_2(sz))
if (!is_power_of_2(cfg->sz))
return -EINVAL;
gc->bgpio_bits = sz * 8;
if (gc->bgpio_bits > BITS_PER_LONG)
chip->bits = cfg->sz * 8;
if (chip->bits > BITS_PER_LONG)
return -EINVAL;
raw_spin_lock_init(&gc->bgpio_lock);
raw_spin_lock_init(&chip->lock);
gc->parent = dev;
gc->label = dev_name(dev);
gc->base = -1;
gc->request = bgpio_request;
gc->be_bits = !!(flags & BGPIOF_BIG_ENDIAN);
chip->be_bits = !!(flags & GPIO_GENERIC_BIG_ENDIAN);
ret = gpiochip_get_ngpios(gc, dev);
if (ret)
gc->ngpio = gc->bgpio_bits;
gc->ngpio = chip->bits;
ret = bgpio_setup_io(gc, dat, set, clr, flags);
ret = bgpio_setup_io(chip, cfg);
if (ret)
return ret;
ret = bgpio_setup_accessors(dev, gc, flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER);
ret = bgpio_setup_accessors(dev, chip,
flags & GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER);
if (ret)
return ret;
ret = bgpio_setup_direction(gc, dirout, dirin, flags);
ret = bgpio_setup_direction(chip, cfg);
if (ret)
return ret;
if (flags & BGPIOF_PINCTRL_BACKEND) {
gc->bgpio_pinctrl = true;
if (flags & GPIO_GENERIC_PINCTRL_BACKEND) {
chip->pinctrl = true;
/* Currently this callback is only used for pincontrol */
gc->free = gpiochip_generic_free;
}
gc->bgpio_data = gc->read_reg(gc->reg_dat);
chip->sdata = chip->read_reg(chip->reg_dat);
if (gc->set == bgpio_set_set &&
!(flags & BGPIOF_UNREADABLE_REG_SET))
gc->bgpio_data = gc->read_reg(gc->reg_set);
!(flags & GPIO_GENERIC_UNREADABLE_REG_SET))
chip->sdata = chip->read_reg(chip->reg_set);
if (flags & BGPIOF_UNREADABLE_REG_DIR)
gc->bgpio_dir_unreadable = true;
if (flags & GPIO_GENERIC_UNREADABLE_REG_DIR)
chip->dir_unreadable = true;
/*
* Inspect hardware to find initial direction setting.
*/
if ((gc->reg_dir_out || gc->reg_dir_in) &&
!(flags & BGPIOF_UNREADABLE_REG_DIR)) {
if (gc->reg_dir_out)
gc->bgpio_dir = gc->read_reg(gc->reg_dir_out);
else if (gc->reg_dir_in)
gc->bgpio_dir = ~gc->read_reg(gc->reg_dir_in);
if ((chip->reg_dir_out || chip->reg_dir_in) &&
!(flags & GPIO_GENERIC_UNREADABLE_REG_DIR)) {
if (chip->reg_dir_out)
chip->sdir = chip->read_reg(chip->reg_dir_out);
else if (chip->reg_dir_in)
chip->sdir = ~chip->read_reg(chip->reg_dir_in);
/*
* If we have two direction registers, synchronise
* input setting to output setting, the library
* can not handle a line being input and output at
* the same time.
*/
if (gc->reg_dir_out && gc->reg_dir_in)
gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir);
if (chip->reg_dir_out && chip->reg_dir_in)
chip->write_reg(chip->reg_dir_in, ~chip->sdir);
}
return ret;
}
EXPORT_SYMBOL_GPL(bgpio_init);
EXPORT_SYMBOL_GPL(gpio_generic_chip_init);
#if IS_ENABLED(CONFIG_GPIO_GENERIC_PLATFORM)
@ -730,12 +734,15 @@ static const struct of_device_id bgpio_of_match[] = {
{ .compatible = "brcm,bcm6345-gpio" },
{ .compatible = "wd,mbl-gpio" },
{ .compatible = "ni,169445-nand-gpio" },
{ .compatible = "intel,ixp4xx-expansion-bus-mmio-gpio" },
{ }
};
MODULE_DEVICE_TABLE(of, bgpio_of_match);
static int bgpio_pdev_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct gpio_generic_chip *gen_gc;
struct device *dev = &pdev->dev;
struct resource *r;
void __iomem *dat;
@ -747,7 +754,6 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
unsigned long flags = 0;
unsigned int base;
int err;
struct gpio_chip *gc;
const char *label;
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
@ -776,23 +782,34 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
if (IS_ERR(dirin))
return PTR_ERR(dirin);
gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
gen_gc = devm_kzalloc(&pdev->dev, sizeof(*gen_gc), GFP_KERNEL);
if (!gen_gc)
return -ENOMEM;
if (device_is_big_endian(dev))
flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER;
flags |= GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER;
if (device_property_read_bool(dev, "no-output"))
flags |= BGPIOF_NO_OUTPUT;
flags |= GPIO_GENERIC_NO_OUTPUT;
err = bgpio_init(gc, dev, sz, dat, set, clr, dirout, dirin, flags);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = sz,
.dat = dat,
.set = set,
.clr = clr,
.dirout = dirout,
.dirin = dirin,
.flags = flags,
};
err = gpio_generic_chip_init(gen_gc, &config);
if (err)
return err;
err = device_property_read_string(dev, "label", &label);
if (!err)
gc->label = label;
gen_gc->gc.label = label;
/*
* This property *must not* be used in device-tree sources, it's only
@ -800,11 +817,11 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
*/
err = device_property_read_u32(dev, "gpio-mmio,base", &base);
if (!err && base <= INT_MAX)
gc->base = base;
gen_gc->gc.base = base;
platform_set_drvdata(pdev, gc);
platform_set_drvdata(pdev, &gen_gc->gc);
return devm_gpiochip_add_data(&pdev->dev, gc, NULL);
return devm_gpiochip_add_data(&pdev->dev, &gen_gc->gc, NULL);
}
static const struct platform_device_id bgpio_id_table[] = {

View File

@ -9,6 +9,7 @@
#include <linux/acpi.h>
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -34,7 +35,7 @@
#define GPIO_IBE 0x18
struct mpc8xxx_gpio_chip {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *regs;
raw_spinlock_t lock;
@ -66,9 +67,11 @@ static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio)
struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);
u32 out_mask, out_shadow;
out_mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_DIR);
val = gc->read_reg(mpc8xxx_gc->regs + GPIO_DAT) & ~out_mask;
out_shadow = gc->bgpio_data & out_mask;
out_mask = gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_DIR);
val = gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_DAT) & ~out_mask;
out_shadow = mpc8xxx_gc->chip.sdata & out_mask;
return !!((val | out_shadow) & mpc_pin2mask(gpio));
}
@ -108,12 +111,13 @@ static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
static irqreturn_t mpc8xxx_gpio_irq_cascade(int irq, void *data)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = data;
struct gpio_chip *gc = &mpc8xxx_gc->gc;
unsigned long mask;
int i;
mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_IER)
& gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR);
mask = gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_IER) &
gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_IMR);
for_each_set_bit(i, &mask, 32)
generic_handle_domain_irq(mpc8xxx_gc->irq, 31 - i);
@ -124,15 +128,17 @@ static void mpc8xxx_irq_unmask(struct irq_data *d)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct gpio_chip *gc = &mpc8xxx_gc->gc;
struct gpio_chip *gc = &mpc8xxx_gc->chip.gc;
unsigned long flags;
gpiochip_enable_irq(gc, hwirq);
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR,
gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR)
gpio_generic_write_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_IMR,
gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_IMR)
| mpc_pin2mask(irqd_to_hwirq(d)));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
@ -142,13 +148,14 @@ static void mpc8xxx_irq_mask(struct irq_data *d)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct gpio_chip *gc = &mpc8xxx_gc->gc;
struct gpio_chip *gc = &mpc8xxx_gc->chip.gc;
unsigned long flags;
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR,
gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR)
gpio_generic_write_reg(&mpc8xxx_gc->chip, mpc8xxx_gc->regs + GPIO_IMR,
gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_IMR)
& ~mpc_pin2mask(irqd_to_hwirq(d)));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
@ -159,32 +166,34 @@ static void mpc8xxx_irq_mask(struct irq_data *d)
static void mpc8xxx_irq_ack(struct irq_data *d)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
struct gpio_chip *gc = &mpc8xxx_gc->gc;
gc->write_reg(mpc8xxx_gc->regs + GPIO_IER,
gpio_generic_write_reg(&mpc8xxx_gc->chip, mpc8xxx_gc->regs + GPIO_IER,
mpc_pin2mask(irqd_to_hwirq(d)));
}
static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
struct gpio_chip *gc = &mpc8xxx_gc->gc;
unsigned long flags;
switch (flow_type) {
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_LEVEL_LOW:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
gc->write_reg(mpc8xxx_gc->regs + GPIO_ICR,
gc->read_reg(mpc8xxx_gc->regs + GPIO_ICR)
gpio_generic_write_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_ICR,
gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_ICR)
| mpc_pin2mask(irqd_to_hwirq(d)));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
case IRQ_TYPE_EDGE_BOTH:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
gc->write_reg(mpc8xxx_gc->regs + GPIO_ICR,
gc->read_reg(mpc8xxx_gc->regs + GPIO_ICR)
gpio_generic_write_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_ICR,
gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_ICR)
& ~mpc_pin2mask(irqd_to_hwirq(d)));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
@ -199,7 +208,6 @@ static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
struct gpio_chip *gc = &mpc8xxx_gc->gc;
unsigned long gpio = irqd_to_hwirq(d);
void __iomem *reg;
unsigned int shift;
@ -217,7 +225,9 @@ static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type)
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_LEVEL_LOW:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift))
gpio_generic_write_reg(&mpc8xxx_gc->chip, reg,
(gpio_generic_read_reg(&mpc8xxx_gc->chip,
reg) & ~(3 << shift))
| (2 << shift));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
@ -225,14 +235,18 @@ static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type)
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift))
gpio_generic_write_reg(&mpc8xxx_gc->chip, reg,
(gpio_generic_read_reg(&mpc8xxx_gc->chip,
reg) & ~(3 << shift))
| (1 << shift));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
case IRQ_TYPE_EDGE_BOTH:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift)));
gpio_generic_write_reg(&mpc8xxx_gc->chip, reg,
(gpio_generic_read_reg(&mpc8xxx_gc->chip,
reg) & ~(3 << shift)));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
@ -309,6 +323,7 @@ static const struct of_device_id mpc8xxx_gpio_ids[] = {
static int mpc8xxx_probe(struct platform_device *pdev)
{
const struct mpc8xxx_gpio_devtype *devtype = NULL;
struct gpio_generic_chip_config config;
struct mpc8xxx_gpio_chip *mpc8xxx_gc;
struct device *dev = &pdev->dev;
struct fwnode_handle *fwnode;
@ -327,26 +342,28 @@ static int mpc8xxx_probe(struct platform_device *pdev)
if (IS_ERR(mpc8xxx_gc->regs))
return PTR_ERR(mpc8xxx_gc->regs);
gc = &mpc8xxx_gc->gc;
gc = &mpc8xxx_gc->chip.gc;
gc->parent = dev;
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = mpc8xxx_gc->regs + GPIO_DAT,
.dirout = mpc8xxx_gc->regs + GPIO_DIR,
.flags = GPIO_GENERIC_BIG_ENDIAN
};
if (device_property_read_bool(dev, "little-endian")) {
ret = bgpio_init(gc, dev, 4, mpc8xxx_gc->regs + GPIO_DAT,
NULL, NULL, mpc8xxx_gc->regs + GPIO_DIR,
NULL, BGPIOF_BIG_ENDIAN);
if (ret)
return ret;
dev_dbg(dev, "GPIO registers are LITTLE endian\n");
} else {
ret = bgpio_init(gc, dev, 4, mpc8xxx_gc->regs + GPIO_DAT,
NULL, NULL, mpc8xxx_gc->regs + GPIO_DIR,
NULL, BGPIOF_BIG_ENDIAN
| BGPIOF_BIG_ENDIAN_BYTE_ORDER);
if (ret)
return ret;
config.flags |= GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER;
dev_dbg(dev, "GPIO registers are BIG endian\n");
}
ret = gpio_generic_chip_init(&mpc8xxx_gc->chip, &config);
if (ret)
return ret;
mpc8xxx_gc->direction_output = gc->direction_output;
devtype = device_get_match_data(dev);
@ -379,10 +396,14 @@ static int mpc8xxx_probe(struct platform_device *pdev)
device_is_compatible(dev, "fsl,ls1028a-gpio") ||
device_is_compatible(dev, "fsl,ls1088a-gpio") ||
is_acpi_node(fwnode)) {
gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff);
gpio_generic_write_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff);
/* Also, latch state of GPIOs configured as output by bootloader. */
gc->bgpio_data = gc->read_reg(mpc8xxx_gc->regs + GPIO_DAT) &
gc->read_reg(mpc8xxx_gc->regs + GPIO_DIR);
mpc8xxx_gc->chip.sdata =
gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_DAT) &
gpio_generic_read_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_DIR);
}
ret = devm_gpiochip_add_data(dev, gc, mpc8xxx_gc);
@ -405,8 +426,10 @@ static int mpc8xxx_probe(struct platform_device *pdev)
return 0;
/* ack and mask all irqs */
gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff);
gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0);
gpio_generic_write_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_IER, 0xffffffff);
gpio_generic_write_reg(&mpc8xxx_gc->chip,
mpc8xxx_gc->regs + GPIO_IMR, 0);
ret = devm_request_irq(dev, mpc8xxx_gc->irqn,
mpc8xxx_gpio_irq_cascade,

View File

@ -69,7 +69,7 @@ static int mpfs_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio_in
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_OUT | MPFS_GPIO_EN_OUT_BUF);
regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index),
value << gpio_index);

View File

@ -6,11 +6,11 @@
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#define MTK_BANK_CNT 3
#define MTK_BANK_WIDTH 32
@ -30,8 +30,7 @@
struct mtk_gc {
struct irq_chip irq_chip;
struct gpio_chip chip;
spinlock_t lock;
struct gpio_generic_chip chip;
int bank;
u32 rising;
u32 falling;
@ -59,27 +58,29 @@ struct mtk {
static inline struct mtk_gc *
to_mediatek_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct mtk_gc, chip);
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(chip);
return container_of(gen_gc, struct mtk_gc, chip);
}
static inline void
mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val)
{
struct gpio_chip *gc = &rg->chip;
struct gpio_chip *gc = &rg->chip.gc;
struct mtk *mtk = gpiochip_get_data(gc);
offset = (rg->bank * GPIO_BANK_STRIDE) + offset;
gc->write_reg(mtk->base + offset, val);
gpio_generic_write_reg(&rg->chip, mtk->base + offset, val);
}
static inline u32
mtk_gpio_r32(struct mtk_gc *rg, u32 offset)
{
struct gpio_chip *gc = &rg->chip;
struct gpio_chip *gc = &rg->chip.gc;
struct mtk *mtk = gpiochip_get_data(gc);
offset = (rg->bank * GPIO_BANK_STRIDE) + offset;
return gc->read_reg(mtk->base + offset);
return gpio_generic_read_reg(&rg->chip, mtk->base + offset);
}
static irqreturn_t
@ -108,12 +109,12 @@ mediatek_gpio_irq_unmask(struct irq_data *d)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = to_mediatek_gpio(gc);
int pin = d->hwirq;
unsigned long flags;
u32 rise, fall, high, low;
gpiochip_enable_irq(gc, d->hwirq);
spin_lock_irqsave(&rg->lock, flags);
guard(gpio_generic_lock_irqsave)(&rg->chip);
rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
@ -122,7 +123,6 @@ mediatek_gpio_irq_unmask(struct irq_data *d)
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel));
spin_unlock_irqrestore(&rg->lock, flags);
}
static void
@ -131,19 +131,18 @@ mediatek_gpio_irq_mask(struct irq_data *d)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = to_mediatek_gpio(gc);
int pin = d->hwirq;
unsigned long flags;
u32 rise, fall, high, low;
spin_lock_irqsave(&rg->lock, flags);
rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
low = mtk_gpio_r32(rg, GPIO_REG_LLVL);
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin));
spin_unlock_irqrestore(&rg->lock, flags);
scoped_guard(gpio_generic_lock_irqsave, &rg->chip) {
rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
low = mtk_gpio_r32(rg, GPIO_REG_LLVL);
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin));
}
gpiochip_disable_irq(gc, d->hwirq);
}
@ -220,6 +219,7 @@ static const struct irq_chip mt7621_irq_chip = {
static int
mediatek_gpio_bank_probe(struct device *dev, int bank)
{
struct gpio_generic_chip_config config;
struct mtk *mtk = dev_get_drvdata(dev);
struct mtk_gc *rg;
void __iomem *dat, *set, *ctrl, *diro;
@ -228,7 +228,6 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
rg = &mtk->gc_map[bank];
memset(rg, 0, sizeof(*rg));
spin_lock_init(&rg->lock);
rg->bank = bank;
dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE);
@ -236,21 +235,30 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
ctrl = mtk->base + GPIO_REG_DCLR + (rg->bank * GPIO_BANK_STRIDE);
diro = mtk->base + GPIO_REG_CTRL + (rg->bank * GPIO_BANK_STRIDE);
ret = bgpio_init(&rg->chip, dev, 4, dat, set, ctrl, diro, NULL,
BGPIOF_NO_SET_ON_INPUT);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = dat,
.set = set,
.clr = ctrl,
.dirout = diro,
.flags = GPIO_GENERIC_NO_SET_ON_INPUT,
};
ret = gpio_generic_chip_init(&rg->chip, &config);
if (ret) {
dev_err(dev, "bgpio_init() failed\n");
dev_err(dev, "failed to initialize generic GPIO chip\n");
return ret;
}
rg->chip.of_gpio_n_cells = 2;
rg->chip.of_xlate = mediatek_gpio_xlate;
rg->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
rg->chip.gc.of_gpio_n_cells = 2;
rg->chip.gc.of_xlate = mediatek_gpio_xlate;
rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
dev_name(dev), bank);
if (!rg->chip.label)
if (!rg->chip.gc.label)
return -ENOMEM;
rg->chip.offset = bank * MTK_BANK_WIDTH;
rg->chip.gc.offset = bank * MTK_BANK_WIDTH;
if (mtk->gpio_irq) {
struct gpio_irq_chip *girq;
@ -261,7 +269,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
*/
ret = devm_request_irq(dev, mtk->gpio_irq,
mediatek_gpio_irq_handler, IRQF_SHARED,
rg->chip.label, &rg->chip);
rg->chip.gc.label, &rg->chip.gc);
if (ret) {
dev_err(dev, "Error requesting IRQ %d: %d\n",
@ -269,7 +277,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
return ret;
}
girq = &rg->chip.irq;
girq = &rg->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &mt7621_irq_chip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
@ -279,17 +287,17 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
girq->handler = handle_simple_irq;
}
ret = devm_gpiochip_add_data(dev, &rg->chip, mtk);
ret = devm_gpiochip_add_data(dev, &rg->chip.gc, mtk);
if (ret < 0) {
dev_err(dev, "Could not register gpio %d, ret=%d\n",
rg->chip.ngpio, ret);
rg->chip.gc.ngpio, ret);
return ret;
}
/* set polarity to low for all gpios */
mtk_gpio_w32(rg, GPIO_REG_POL, 0);
dev_info(dev, "registering %d gpios\n", rg->chip.ngpio);
dev_info(dev, "registering %d gpios\n", rg->chip.gc.ngpio);
return 0;
}

View File

@ -602,7 +602,6 @@ static const struct regmap_config mvebu_gpio_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
};
/*
@ -899,7 +898,7 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
msk = BIT(i);
is_out = !(io_conf & msk);
seq_printf(s, " gpio-%-3d (%-20.20s)", chip->base + i, label);
seq_printf(s, " gpio-%-3d (%-20.20s)", i, label);
if (is_out) {
seq_printf(s, " out %s %s\n",

View File

@ -481,7 +481,7 @@ static int mxc_gpio_probe(struct platform_device *pdev)
config.dat = port->base + GPIO_PSR;
config.set = port->base + GPIO_DR;
config.dirout = port->base + GPIO_GDIR;
config.flags = BGPIOF_READ_OUTPUT_REG_SET;
config.flags = GPIO_GENERIC_READ_OUTPUT_REG_SET;
err = gpio_generic_chip_init(&port->gen_gc, &config);
if (err)

View File

@ -7,17 +7,18 @@
// Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#define MXS_SET 0x4
#define MXS_CLR 0x8
@ -48,7 +49,7 @@ struct mxs_gpio_port {
int id;
int irq;
struct irq_domain *domain;
struct gpio_chip gc;
struct gpio_generic_chip chip;
struct device *dev;
enum mxs_gpio_id devid;
u32 both_edges;
@ -258,6 +259,7 @@ MODULE_DEVICE_TABLE(of, mxs_gpio_dt_ids);
static int mxs_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct gpio_generic_chip_config config;
struct device_node *parent;
static void __iomem *base;
struct mxs_gpio_port *port;
@ -319,19 +321,24 @@ static int mxs_gpio_probe(struct platform_device *pdev)
irq_set_chained_handler_and_data(port->irq, mxs_gpio_irq_handler,
port);
err = bgpio_init(&port->gc, &pdev->dev, 4,
port->base + PINCTRL_DIN(port),
port->base + PINCTRL_DOUT(port) + MXS_SET,
port->base + PINCTRL_DOUT(port) + MXS_CLR,
port->base + PINCTRL_DOE(port), NULL, 0);
config = (struct gpio_generic_chip_config) {
.dev = &pdev->dev,
.sz = 4,
.dat = port->base + PINCTRL_DIN(port),
.set = port->base + PINCTRL_DOUT(port) + MXS_SET,
.clr = port->base + PINCTRL_DOUT(port) + MXS_CLR,
.dirout = port->base + PINCTRL_DOE(port),
};
err = gpio_generic_chip_init(&port->chip, &config);
if (err)
goto out_irqdomain_remove;
port->gc.to_irq = mxs_gpio_to_irq;
port->gc.get_direction = mxs_gpio_get_direction;
port->gc.base = port->id * 32;
port->chip.gc.to_irq = mxs_gpio_to_irq;
port->chip.gc.get_direction = mxs_gpio_get_direction;
port->chip.gc.base = port->id * 32;
err = gpiochip_add_data(&port->gc, port);
err = gpiochip_add_data(&port->chip.gc, port);
if (err)
goto out_irqdomain_remove;

499
drivers/gpio/gpio-nct6694.c Normal file
View File

@ -0,0 +1,499 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Nuvoton NCT6694 GPIO controller driver based on USB interface.
*
* Copyright (C) 2025 Nuvoton Technology Corp.
*/
#include <linux/bits.h>
#include <linux/gpio/driver.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
#include <linux/platform_device.h>
/*
* USB command module type for NCT6694 GPIO controller.
* This defines the module type used for communication with the NCT6694
* GPIO controller over the USB interface.
*/
#define NCT6694_GPIO_MOD 0xFF
#define NCT6694_GPIO_VER 0x90
#define NCT6694_GPIO_VALID 0x110
#define NCT6694_GPI_DATA 0x120
#define NCT6694_GPO_DIR 0x170
#define NCT6694_GPO_TYPE 0x180
#define NCT6694_GPO_DATA 0x190
#define NCT6694_GPI_STS 0x130
#define NCT6694_GPI_CLR 0x140
#define NCT6694_GPI_FALLING 0x150
#define NCT6694_GPI_RISING 0x160
#define NCT6694_NR_GPIO 8
struct nct6694_gpio_data {
struct nct6694 *nct6694;
struct gpio_chip gpio;
struct mutex lock;
/* Protect irq operation */
struct mutex irq_lock;
unsigned char reg_val;
unsigned char irq_trig_falling;
unsigned char irq_trig_rising;
/* Current gpio group */
unsigned char group;
int irq;
};
static int nct6694_get_direction(struct gpio_chip *gpio, unsigned int offset)
{
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPO_DIR + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
int ret;
guard(mutex)(&data->lock);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
return !(BIT(offset) & data->reg_val);
}
static int nct6694_direction_input(struct gpio_chip *gpio, unsigned int offset)
{
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPO_DIR + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
int ret;
guard(mutex)(&data->lock);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
data->reg_val &= ~BIT(offset);
return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);
}
static int nct6694_direction_output(struct gpio_chip *gpio,
unsigned int offset, int val)
{
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPO_DIR + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
int ret;
guard(mutex)(&data->lock);
/* Set direction to output */
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
data->reg_val |= BIT(offset);
ret = nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
/* Then set output level */
cmd_hd.offset = cpu_to_le16(NCT6694_GPO_DATA + data->group);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
if (val)
data->reg_val |= BIT(offset);
else
data->reg_val &= ~BIT(offset);
return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);
}
static int nct6694_get_value(struct gpio_chip *gpio, unsigned int offset)
{
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPO_DIR + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
int ret;
guard(mutex)(&data->lock);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
if (BIT(offset) & data->reg_val) {
cmd_hd.offset = cpu_to_le16(NCT6694_GPO_DATA + data->group);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
return !!(BIT(offset) & data->reg_val);
}
cmd_hd.offset = cpu_to_le16(NCT6694_GPI_DATA + data->group);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
return !!(BIT(offset) & data->reg_val);
}
static int nct6694_set_value(struct gpio_chip *gpio, unsigned int offset,
int val)
{
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPO_DATA + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
int ret;
guard(mutex)(&data->lock);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
if (val)
data->reg_val |= BIT(offset);
else
data->reg_val &= ~BIT(offset);
return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);
}
static int nct6694_set_config(struct gpio_chip *gpio, unsigned int offset,
unsigned long config)
{
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPO_TYPE + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
int ret;
guard(mutex)(&data->lock);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
data->reg_val |= BIT(offset);
break;
case PIN_CONFIG_DRIVE_PUSH_PULL:
data->reg_val &= ~BIT(offset);
break;
default:
return -ENOTSUPP;
}
return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);
}
static int nct6694_init_valid_mask(struct gpio_chip *gpio,
unsigned long *valid_mask,
unsigned int ngpios)
{
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPIO_VALID + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
int ret;
guard(mutex)(&data->lock);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret < 0)
return ret;
*valid_mask = data->reg_val;
return ret;
}
static irqreturn_t nct6694_irq_handler(int irq, void *priv)
{
struct nct6694_gpio_data *data = priv;
struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPI_STS + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
unsigned char status;
int ret;
guard(mutex)(&data->lock);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);
if (ret)
return IRQ_NONE;
status = data->reg_val;
while (status) {
int bit = __ffs(status);
data->reg_val = BIT(bit);
handle_nested_irq(irq_find_mapping(data->gpio.irq.domain, bit));
status &= ~BIT(bit);
cmd_hd.offset = cpu_to_le16(NCT6694_GPI_CLR + data->group);
nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);
}
return IRQ_HANDLED;
}
static int nct6694_get_irq_trig(struct nct6694_gpio_data *data)
{
struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPI_FALLING + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
int ret;
guard(mutex)(&data->lock);
ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->irq_trig_falling);
if (ret)
return ret;
cmd_hd.offset = cpu_to_le16(NCT6694_GPI_RISING + data->group);
return nct6694_read_msg(data->nct6694, &cmd_hd, &data->irq_trig_rising);
}
static void nct6694_irq_mask(struct irq_data *d)
{
struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
gpiochip_disable_irq(gpio, hwirq);
}
static void nct6694_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
gpiochip_enable_irq(gpio, hwirq);
}
static int nct6694_irq_set_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
guard(mutex)(&data->lock);
switch (type) {
case IRQ_TYPE_EDGE_RISING:
data->irq_trig_rising |= BIT(hwirq);
break;
case IRQ_TYPE_EDGE_FALLING:
data->irq_trig_falling |= BIT(hwirq);
break;
case IRQ_TYPE_EDGE_BOTH:
data->irq_trig_rising |= BIT(hwirq);
data->irq_trig_falling |= BIT(hwirq);
break;
default:
return -ENOTSUPP;
}
return 0;
}
static void nct6694_irq_bus_lock(struct irq_data *d)
{
struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
mutex_lock(&data->irq_lock);
}
static void nct6694_irq_bus_sync_unlock(struct irq_data *d)
{
struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);
struct nct6694_gpio_data *data = gpiochip_get_data(gpio);
struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_GPIO_MOD,
.offset = cpu_to_le16(NCT6694_GPI_FALLING + data->group),
.len = cpu_to_le16(sizeof(data->reg_val))
};
scoped_guard(mutex, &data->lock) {
nct6694_write_msg(data->nct6694, &cmd_hd, &data->irq_trig_falling);
cmd_hd.offset = cpu_to_le16(NCT6694_GPI_RISING + data->group);
nct6694_write_msg(data->nct6694, &cmd_hd, &data->irq_trig_rising);
}
mutex_unlock(&data->irq_lock);
}
static const struct irq_chip nct6694_irq_chip = {
.name = "gpio-nct6694",
.irq_mask = nct6694_irq_mask,
.irq_unmask = nct6694_irq_unmask,
.irq_set_type = nct6694_irq_set_type,
.irq_bus_lock = nct6694_irq_bus_lock,
.irq_bus_sync_unlock = nct6694_irq_bus_sync_unlock,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void nct6694_irq_dispose_mapping(void *d)
{
struct nct6694_gpio_data *data = d;
irq_dispose_mapping(data->irq);
}
static void nct6694_gpio_ida_free(void *d)
{
struct nct6694_gpio_data *data = d;
struct nct6694 *nct6694 = data->nct6694;
ida_free(&nct6694->gpio_ida, data->group);
}
static int nct6694_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct nct6694 *nct6694 = dev_get_drvdata(dev->parent);
struct nct6694_gpio_data *data;
struct gpio_irq_chip *girq;
int ret, i;
char **names;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->nct6694 = nct6694;
ret = ida_alloc(&nct6694->gpio_ida, GFP_KERNEL);
if (ret < 0)
return ret;
data->group = ret;
ret = devm_add_action_or_reset(dev, nct6694_gpio_ida_free, data);
if (ret)
return ret;
names = devm_kcalloc(dev, NCT6694_NR_GPIO, sizeof(char *),
GFP_KERNEL);
if (!names)
return -ENOMEM;
for (i = 0; i < NCT6694_NR_GPIO; i++) {
names[i] = devm_kasprintf(dev, GFP_KERNEL, "GPIO%X%d",
data->group, i);
if (!names[i])
return -ENOMEM;
}
data->irq = irq_create_mapping(nct6694->domain,
NCT6694_IRQ_GPIO0 + data->group);
if (!data->irq)
return -EINVAL;
ret = devm_add_action_or_reset(dev, nct6694_irq_dispose_mapping, data);
if (ret)
return ret;
data->gpio.names = (const char * const*)names;
data->gpio.label = pdev->name;
data->gpio.direction_input = nct6694_direction_input;
data->gpio.get = nct6694_get_value;
data->gpio.direction_output = nct6694_direction_output;
data->gpio.set = nct6694_set_value;
data->gpio.get_direction = nct6694_get_direction;
data->gpio.set_config = nct6694_set_config;
data->gpio.init_valid_mask = nct6694_init_valid_mask;
data->gpio.base = -1;
data->gpio.can_sleep = false;
data->gpio.owner = THIS_MODULE;
data->gpio.ngpio = NCT6694_NR_GPIO;
platform_set_drvdata(pdev, data);
ret = devm_mutex_init(dev, &data->lock);
if (ret)
return ret;
ret = devm_mutex_init(dev, &data->irq_lock);
if (ret)
return ret;
ret = nct6694_get_irq_trig(data);
if (ret) {
dev_err_probe(dev, ret, "Failed to get irq trigger type\n");
return ret;
}
girq = &data->gpio.irq;
gpio_irq_chip_set_chip(girq, &nct6694_irq_chip);
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_level_irq;
girq->threaded = true;
ret = devm_request_threaded_irq(dev, data->irq, NULL, nct6694_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
"gpio-nct6694", data);
if (ret) {
dev_err_probe(dev, ret, "Failed to request irq\n");
return ret;
}
return devm_gpiochip_add_data(dev, &data->gpio, data);
}
static struct platform_driver nct6694_gpio_driver = {
.driver = {
.name = "nct6694-gpio",
},
.probe = nct6694_gpio_probe,
};
module_platform_driver(nct6694_gpio_driver);
MODULE_DESCRIPTION("USB-GPIO controller driver for NCT6694");
MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:nct6694-gpio");

View File

@ -20,6 +20,7 @@
*/
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@ -396,10 +397,12 @@ static int nmk_gpio_get_mode(struct nmk_gpio_chip *nmk_chip, int offset)
}
void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev,
struct gpio_chip *chip, unsigned int offset,
unsigned int gpio)
struct gpio_chip *chip, unsigned int offset)
{
struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip);
#ifdef CONFIG_PINCTRL_NOMADIK
struct gpio_desc *desc;
#endif
int mode;
bool is_out;
bool data_out;
@ -425,15 +428,15 @@ void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev,
data_out = !!(readl(nmk_chip->addr + NMK_GPIO_DAT) & BIT(offset));
mode = nmk_gpio_get_mode(nmk_chip, offset);
#ifdef CONFIG_PINCTRL_NOMADIK
if (mode == NMK_GPIO_ALT_C && pctldev)
mode = nmk_prcm_gpiocr_get_mode(pctldev, gpio);
if (mode == NMK_GPIO_ALT_C && pctldev) {
desc = gpio_device_get_desc(chip->gpiodev, offset);
mode = nmk_prcm_gpiocr_get_mode(pctldev, desc_to_gpio(desc));
}
#endif
if (is_out) {
seq_printf(s, " gpio-%-3d (%-20.20s) out %s %s",
gpio,
label ?: "(none)",
str_hi_lo(data_out),
offset, label ?: "(none)", str_hi_lo(data_out),
(mode < 0) ? "unknown" : modes[mode]);
} else {
int irq = chip->to_irq(chip, offset);
@ -445,9 +448,7 @@ void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev,
};
seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s",
gpio,
label ?: "(none)",
pulls[pullidx],
offset, label ?: "(none)", pulls[pullidx],
(mode < 0) ? "unknown" : modes[mode]);
val = nmk_gpio_get_input(chip, offset);
@ -479,10 +480,10 @@ void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev,
static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
unsigned int i, gpio = chip->base;
unsigned int i;
for (i = 0; i < chip->ngpio; i++, gpio++) {
nmk_gpio_dbg_show_one(s, NULL, chip, i, gpio);
for (i = 0; i < chip->ngpio; i++) {
nmk_gpio_dbg_show_one(s, NULL, chip, i);
seq_puts(s, "\n");
}
}

View File

@ -108,11 +108,6 @@ static const struct gpio_chip template_chip = {
.can_sleep = true,
};
static void pisosr_mutex_destroy(void *lock)
{
mutex_destroy(lock);
}
static int pisosr_gpio_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
@ -139,8 +134,7 @@ static int pisosr_gpio_probe(struct spi_device *spi)
return dev_err_probe(dev, PTR_ERR(gpio->load_gpio),
"Unable to allocate load GPIO\n");
mutex_init(&gpio->lock);
ret = devm_add_action_or_reset(dev, pisosr_mutex_destroy, &gpio->lock);
ret = devm_mutex_init(dev, &gpio->lock);
if (ret)
return ret;

View File

@ -8,6 +8,7 @@
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@ -35,7 +36,7 @@
#define RDA_GPIO_BANK_NR 32
struct rda_gpio {
struct gpio_chip chip;
struct gpio_generic_chip chip;
void __iomem *base;
spinlock_t lock;
int irq;
@ -208,6 +209,7 @@ static const struct irq_chip rda_gpio_irq_chip = {
static int rda_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct gpio_irq_chip *girq;
struct rda_gpio *rda_gpio;
@ -235,24 +237,29 @@ static int rda_gpio_probe(struct platform_device *pdev)
spin_lock_init(&rda_gpio->lock);
ret = bgpio_init(&rda_gpio->chip, dev, 4,
rda_gpio->base + RDA_GPIO_VAL,
rda_gpio->base + RDA_GPIO_SET,
rda_gpio->base + RDA_GPIO_CLR,
rda_gpio->base + RDA_GPIO_OEN_SET_OUT,
rda_gpio->base + RDA_GPIO_OEN_SET_IN,
BGPIOF_READ_OUTPUT_REG_SET);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = rda_gpio->base + RDA_GPIO_VAL,
.set = rda_gpio->base + RDA_GPIO_SET,
.clr = rda_gpio->base + RDA_GPIO_CLR,
.dirout = rda_gpio->base + RDA_GPIO_OEN_SET_OUT,
.dirin = rda_gpio->base + RDA_GPIO_OEN_SET_IN,
.flags = GPIO_GENERIC_READ_OUTPUT_REG_SET,
};
ret = gpio_generic_chip_init(&rda_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;
}
rda_gpio->chip.label = dev_name(dev);
rda_gpio->chip.ngpio = ngpios;
rda_gpio->chip.base = -1;
rda_gpio->chip.gc.label = dev_name(dev);
rda_gpio->chip.gc.ngpio = ngpios;
rda_gpio->chip.gc.base = -1;
if (rda_gpio->irq >= 0) {
girq = &rda_gpio->chip.irq;
girq = &rda_gpio->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &rda_gpio_irq_chip);
girq->handler = handle_bad_irq;
girq->default_type = IRQ_TYPE_NONE;
@ -269,7 +276,7 @@ static int rda_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rda_gpio);
return devm_gpiochip_add_data(dev, &rda_gpio->chip, rda_gpio);
return devm_gpiochip_add_data(dev, &rda_gpio->chip.gc, rda_gpio);
}
static const struct of_device_id rda_gpio_of_match[] = {

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/gpio/driver.h>
#include <linux/cpumask.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/irq.h>
#include <linux/minmax.h>
#include <linux/mod_devicetable.h>
@ -41,7 +42,7 @@
/**
* realtek_gpio_ctrl - Realtek Otto GPIO driver data
*
* @gc: Associated gpio_chip instance
* @chip: Associated gpio_generic_chip instance
* @base: Base address of the register block for a GPIO bank
* @lock: Lock for accessing the IRQ registers and values
* @intr_mask: Mask for interrupts lines
@ -64,7 +65,7 @@
* IMR on changes.
*/
struct realtek_gpio_ctrl {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *base;
void __iomem *cpumask_base;
struct cpumask cpu_irq_maskable;
@ -101,7 +102,7 @@ static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
return container_of(gc, struct realtek_gpio_ctrl, gc);
return container_of(to_gpio_generic_chip(gc), struct realtek_gpio_ctrl, chip);
}
/*
@ -194,7 +195,7 @@ static void realtek_gpio_irq_unmask(struct irq_data *data)
unsigned int line = irqd_to_hwirq(data);
unsigned long flags;
gpiochip_enable_irq(&ctrl->gc, line);
gpiochip_enable_irq(&ctrl->chip.gc, line);
raw_spin_lock_irqsave(&ctrl->lock, flags);
ctrl->intr_mask[line] = REALTEK_GPIO_IMR_LINE_MASK;
@ -213,7 +214,7 @@ static void realtek_gpio_irq_mask(struct irq_data *data)
realtek_gpio_update_line_imr(ctrl, line);
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
gpiochip_disable_irq(&ctrl->gc, line);
gpiochip_disable_irq(&ctrl->chip.gc, line);
}
static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
@ -356,8 +357,9 @@ MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
static int realtek_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
unsigned long bgpio_flags;
unsigned long gen_gc_flags;
unsigned int dev_flags;
struct gpio_irq_chip *girq;
struct realtek_gpio_ctrl *ctrl;
@ -388,32 +390,37 @@ static int realtek_gpio_probe(struct platform_device *pdev)
raw_spin_lock_init(&ctrl->lock);
if (dev_flags & GPIO_PORTS_REVERSED) {
bgpio_flags = 0;
gen_gc_flags = 0;
ctrl->bank_read = realtek_gpio_bank_read;
ctrl->bank_write = realtek_gpio_bank_write;
ctrl->line_imr_pos = realtek_gpio_line_imr_pos;
} else {
bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
gen_gc_flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER;
ctrl->bank_read = realtek_gpio_bank_read_swapped;
ctrl->bank_write = realtek_gpio_bank_write_swapped;
ctrl->line_imr_pos = realtek_gpio_line_imr_pos_swapped;
}
err = bgpio_init(&ctrl->gc, dev, 4,
ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL,
ctrl->base + REALTEK_GPIO_REG_DIR, NULL,
bgpio_flags);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = ctrl->base + REALTEK_GPIO_REG_DATA,
.dirout = ctrl->base + REALTEK_GPIO_REG_DIR,
.flags = gen_gc_flags,
};
err = gpio_generic_chip_init(&ctrl->chip, &config);
if (err) {
dev_err(dev, "unable to init generic GPIO");
return err;
}
ctrl->gc.ngpio = ngpios;
ctrl->gc.owner = THIS_MODULE;
ctrl->chip.gc.ngpio = ngpios;
ctrl->chip.gc.owner = THIS_MODULE;
irq = platform_get_irq_optional(pdev, 0);
if (!(dev_flags & GPIO_INTERRUPTS_DISABLED) && irq > 0) {
girq = &ctrl->gc.irq;
girq = &ctrl->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &realtek_gpio_irq_chip);
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
@ -442,7 +449,7 @@ static int realtek_gpio_probe(struct platform_device *pdev)
cpumask_set_cpu(cpu, &ctrl->cpu_irq_maskable);
}
return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
return devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl);
}
static struct platform_driver realtek_gpio_driver = {

View File

@ -32,6 +32,11 @@ struct gpio_regmap {
unsigned int reg_dir_in_base;
unsigned int reg_dir_out_base;
#ifdef CONFIG_REGMAP_IRQ
int regmap_irq_line;
struct regmap_irq_chip_data *irq_chip_data;
#endif
int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
unsigned int *mask);
@ -215,6 +220,7 @@ EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
*/
struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)
{
struct irq_domain *irq_domain;
struct gpio_regmap *gpio;
struct gpio_chip *chip;
int ret;
@ -255,6 +261,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
chip->names = config->names;
chip->label = config->label ?: dev_name(config->parent);
chip->can_sleep = regmap_might_sleep(config->regmap);
chip->init_valid_mask = config->init_valid_mask;
chip->request = gpiochip_generic_request;
chip->free = gpiochip_generic_free;
@ -295,8 +302,22 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
if (ret < 0)
goto err_free_gpio;
if (config->irq_domain) {
ret = gpiochip_irqchip_add_domain(chip, config->irq_domain);
#ifdef CONFIG_REGMAP_IRQ
if (config->regmap_irq_chip) {
gpio->regmap_irq_line = config->regmap_irq_line;
ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap,
config->regmap_irq_line, config->regmap_irq_flags,
0, config->regmap_irq_chip, &gpio->irq_chip_data);
if (ret)
goto err_free_gpio;
irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);
} else
#endif
irq_domain = config->irq_domain;
if (irq_domain) {
ret = gpiochip_irqchip_add_domain(chip, irq_domain);
if (ret)
goto err_remove_gpiochip;
}
@ -317,6 +338,11 @@ EXPORT_SYMBOL_GPL(gpio_regmap_register);
*/
void gpio_regmap_unregister(struct gpio_regmap *gpio)
{
#ifdef CONFIG_REGMAP_IRQ
if (gpio->irq_chip_data)
regmap_del_irq_chip(gpio->regmap_irq_line, gpio->irq_chip_data);
#endif
gpiochip_remove(&gpio->gpio_chip);
kfree(gpio);
}

View File

@ -7,6 +7,7 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/property.h>
@ -32,7 +33,7 @@
struct sifive_gpio {
void __iomem *base;
struct gpio_chip gc;
struct gpio_generic_chip gen_gc;
struct regmap *regs;
unsigned long irq_state;
unsigned int trigger[SIFIVE_GPIO_MAX];
@ -41,10 +42,10 @@ struct sifive_gpio {
static void sifive_gpio_set_ie(struct sifive_gpio *chip, unsigned int offset)
{
unsigned long flags;
unsigned int trigger;
raw_spin_lock_irqsave(&chip->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&chip->gen_gc);
trigger = (chip->irq_state & BIT(offset)) ? chip->trigger[offset] : 0;
regmap_update_bits(chip->regs, SIFIVE_GPIO_RISE_IE, BIT(offset),
(trigger & IRQ_TYPE_EDGE_RISING) ? BIT(offset) : 0);
@ -54,7 +55,6 @@ static void sifive_gpio_set_ie(struct sifive_gpio *chip, unsigned int offset)
(trigger & IRQ_TYPE_LEVEL_HIGH) ? BIT(offset) : 0);
regmap_update_bits(chip->regs, SIFIVE_GPIO_LOW_IE, BIT(offset),
(trigger & IRQ_TYPE_LEVEL_LOW) ? BIT(offset) : 0);
raw_spin_unlock_irqrestore(&chip->gc.bgpio_lock, flags);
}
static int sifive_gpio_irq_set_type(struct irq_data *d, unsigned int trigger)
@ -72,13 +72,12 @@ static int sifive_gpio_irq_set_type(struct irq_data *d, unsigned int trigger)
}
static void sifive_gpio_irq_enable(struct irq_data *d)
{
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct sifive_gpio *chip = gpiochip_get_data(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
int offset = hwirq % SIFIVE_GPIO_MAX;
u32 bit = BIT(offset);
unsigned long flags;
gpiochip_enable_irq(gc, hwirq);
irq_chip_enable_parent(d);
@ -86,13 +85,13 @@ static void sifive_gpio_irq_enable(struct irq_data *d)
/* Switch to input */
gc->direction_input(gc, offset);
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
/* Clear any sticky pending interrupts */
regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
scoped_guard(gpio_generic_lock_irqsave, &chip->gen_gc) {
/* Clear any sticky pending interrupts */
regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit);
}
/* Enable interrupts */
assign_bit(offset, &chip->irq_state, 1);
@ -118,15 +117,14 @@ static void sifive_gpio_irq_eoi(struct irq_data *d)
struct sifive_gpio *chip = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(d) % SIFIVE_GPIO_MAX;
u32 bit = BIT(offset);
unsigned long flags;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
/* Clear all pending interrupts */
regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
scoped_guard(gpio_generic_lock_irqsave, &chip->gen_gc) {
/* Clear all pending interrupts */
regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit);
regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit);
}
irq_chip_eoi_parent(d);
}
@ -174,12 +172,12 @@ static const struct regmap_config sifive_gpio_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
.disable_locking = true,
};
static int sifive_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct irq_domain *parent;
struct gpio_irq_chip *girq;
@ -218,13 +216,17 @@ static int sifive_gpio_probe(struct platform_device *pdev)
*/
parent = irq_get_irq_data(chip->irq_number[0])->domain;
ret = bgpio_init(&chip->gc, dev, 4,
chip->base + SIFIVE_GPIO_INPUT_VAL,
chip->base + SIFIVE_GPIO_OUTPUT_VAL,
NULL,
chip->base + SIFIVE_GPIO_OUTPUT_EN,
chip->base + SIFIVE_GPIO_INPUT_EN,
BGPIOF_READ_OUTPUT_REG_SET);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = chip->base + SIFIVE_GPIO_INPUT_VAL,
.set = chip->base + SIFIVE_GPIO_OUTPUT_VAL,
.dirout = chip->base + SIFIVE_GPIO_OUTPUT_EN,
.dirin = chip->base + SIFIVE_GPIO_INPUT_EN,
.flags = GPIO_GENERIC_READ_OUTPUT_REG_SET,
};
ret = gpio_generic_chip_init(&chip->gen_gc, &config);
if (ret) {
dev_err(dev, "unable to init generic GPIO\n");
return ret;
@ -237,12 +239,12 @@ static int sifive_gpio_probe(struct platform_device *pdev)
regmap_write(chip->regs, SIFIVE_GPIO_LOW_IE, 0);
chip->irq_state = 0;
chip->gc.base = -1;
chip->gc.ngpio = ngpio;
chip->gc.label = dev_name(dev);
chip->gc.parent = dev;
chip->gc.owner = THIS_MODULE;
girq = &chip->gc.irq;
chip->gen_gc.gc.base = -1;
chip->gen_gc.gc.ngpio = ngpio;
chip->gen_gc.gc.label = dev_name(dev);
chip->gen_gc.gc.parent = dev;
chip->gen_gc.gc.owner = THIS_MODULE;
girq = &chip->gen_gc.gc.irq;
gpio_irq_chip_set_chip(girq, &sifive_gpio_irqchip);
girq->fwnode = dev_fwnode(dev);
girq->parent_domain = parent;
@ -250,7 +252,7 @@ static int sifive_gpio_probe(struct platform_device *pdev)
girq->handler = handle_bad_irq;
girq->default_type = IRQ_TYPE_NONE;
return gpiochip_add_data(&chip->gc, chip);
return gpiochip_add_data(&chip->gen_gc.gc, chip);
}
static const struct of_device_id sifive_gpio_match[] = {

View File

@ -262,8 +262,7 @@ static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc)
guard(mutex)(&chip->lock);
for_each_hwgpio(gc, i, label)
seq_printf(seq, " gpio-%-3d (%s) %s,%s\n",
gc->base + i,
seq_printf(seq, " gpio-%-3d (%s) %s,%s\n", i,
label ?: "<unused>",
test_bit(i, chip->direction_map) ? "input" :
test_bit(i, chip->value_map) ? "output-high" :

View File

@ -9,6 +9,7 @@
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -39,7 +40,7 @@ struct sdv_gpio_chip_data {
void __iomem *gpio_pub_base;
struct irq_domain *id;
struct irq_chip_generic *gc;
struct gpio_chip chip;
struct gpio_generic_chip gen_gc;
};
static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type)
@ -180,6 +181,7 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,
static int sdv_gpio_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_id)
{
struct gpio_generic_chip_config config;
struct sdv_gpio_chip_data *sd;
int ret;
u32 mux_val;
@ -206,15 +208,21 @@ static int sdv_gpio_probe(struct pci_dev *pdev,
if (!ret)
writel(mux_val, sd->gpio_pub_base + GPMUXCTL);
ret = bgpio_init(&sd->chip, &pdev->dev, 4,
sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR,
NULL, sd->gpio_pub_base + GPOER, NULL, 0);
config = (struct gpio_generic_chip_config) {
.dev = &pdev->dev,
.sz = 4,
.dat = sd->gpio_pub_base + GPINR,
.set = sd->gpio_pub_base + GPOUTR,
.dirout = sd->gpio_pub_base + GPOER,
};
ret = gpio_generic_chip_init(&sd->gen_gc, &config);
if (ret)
return ret;
sd->chip.ngpio = SDV_NUM_PUB_GPIOS;
sd->gen_gc.gc.ngpio = SDV_NUM_PUB_GPIOS;
ret = devm_gpiochip_add_data(&pdev->dev, &sd->chip, sd);
ret = devm_gpiochip_add_data(&pdev->dev, &sd->gen_gc.gc, sd);
if (ret < 0) {
dev_err(&pdev->dev, "gpiochip_add() failed.\n");
return ret;

View File

@ -6,6 +6,7 @@
#include <linux/clk.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -38,7 +39,7 @@
struct spacemit_gpio;
struct spacemit_gpio_bank {
struct gpio_chip gc;
struct gpio_generic_chip chip;
struct spacemit_gpio *sg;
void __iomem *base;
u32 irq_mask;
@ -72,7 +73,7 @@ static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
for_each_set_bit(n, &pending, BITS_PER_LONG)
handle_nested_irq(irq_find_mapping(gb->gc.irq.domain, n));
handle_nested_irq(irq_find_mapping(gb->chip.gc.irq.domain, n));
return IRQ_HANDLED;
}
@ -143,7 +144,7 @@ static void spacemit_gpio_irq_print_chip(struct irq_data *data, struct seq_file
{
struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(data);
seq_printf(p, "%s-%d", dev_name(gb->gc.parent), spacemit_gpio_bank_index(gb));
seq_printf(p, "%s-%d", dev_name(gb->chip.gc.parent), spacemit_gpio_bank_index(gb));
}
static struct irq_chip spacemit_gpio_chip = {
@ -165,7 +166,7 @@ static bool spacemit_of_node_instance_match(struct gpio_chip *gc, unsigned int i
if (i >= SPACEMIT_NR_BANKS)
return false;
return (gc == &sg->sgb[i].gc);
return (gc == &sg->sgb[i].chip.gc);
}
static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
@ -173,7 +174,8 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
int index, int irq)
{
struct spacemit_gpio_bank *gb = &sg->sgb[index];
struct gpio_chip *gc = &gb->gc;
struct gpio_generic_chip_config config;
struct gpio_chip *gc = &gb->chip.gc;
struct device *dev = sg->dev;
struct gpio_irq_chip *girq;
void __iomem *dat, *set, *clr, *dirin, *dirout;
@ -187,9 +189,20 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
dirin = gb->base + SPACEMIT_GCDR;
dirout = gb->base + SPACEMIT_GSDR;
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = dat,
.set = set,
.clr = clr,
.dirout = dirout,
.dirin = dirin,
.flags = GPIO_GENERIC_UNREADABLE_REG_SET |
GPIO_GENERIC_UNREADABLE_REG_DIR,
};
/* This registers 32 GPIO lines per bank */
ret = bgpio_init(gc, dev, 4, dat, set, clr, dirout, dirin,
BGPIOF_UNREADABLE_REG_SET | BGPIOF_UNREADABLE_REG_DIR);
ret = gpio_generic_chip_init(&gb->chip, &config);
if (ret)
return dev_err_probe(dev, ret, "failed to init gpio chip\n");
@ -221,7 +234,7 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
ret = devm_request_threaded_irq(dev, irq, NULL,
spacemit_gpio_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
gb->gc.label, gb);
gb->chip.gc.label, gb);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to register IRQ\n");

View File

@ -262,9 +262,8 @@ static void stmpe_gpio_irq_unmask(struct irq_data *d)
stmpe_gpio->regs[REG_IE][regoffset] |= mask;
}
static void stmpe_dbg_show_one(struct seq_file *s,
struct gpio_chip *gc,
unsigned offset, unsigned gpio)
static void stmpe_dbg_show_one(struct seq_file *s, struct gpio_chip *gc,
unsigned int offset)
{
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc);
struct stmpe *stmpe = stmpe_gpio->stmpe;
@ -286,7 +285,7 @@ static void stmpe_dbg_show_one(struct seq_file *s,
if (dir) {
seq_printf(s, " gpio-%-3d (%-20.20s) out %s",
gpio, label ?: "(none)", str_hi_lo(val));
offset, label ?: "(none)", str_hi_lo(val));
} else {
u8 edge_det_reg;
u8 rise_reg;
@ -354,7 +353,7 @@ static void stmpe_dbg_show_one(struct seq_file *s,
irqen = !!(ret & mask);
seq_printf(s, " gpio-%-3d (%-20.20s) in %s %13s %13s %25s %25s",
gpio, label ?: "(none)",
offset, label ?: "(none)",
str_hi_lo(val),
edge_det_values[edge_det],
irqen ? "IRQ-enabled" : "IRQ-disabled",
@ -366,10 +365,9 @@ static void stmpe_dbg_show_one(struct seq_file *s,
static void stmpe_dbg_show(struct seq_file *s, struct gpio_chip *gc)
{
unsigned i;
unsigned gpio = gc->base;
for (i = 0; i < gc->ngpio; i++, gpio++) {
stmpe_dbg_show_one(s, gc, i, gpio);
for (i = 0; i < gc->ngpio; i++) {
stmpe_dbg_show_one(s, gc, i);
seq_putc(s, '\n');
}
}
@ -534,10 +532,16 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
return devm_gpiochip_add_data(dev, &stmpe_gpio->chip, stmpe_gpio);
}
static const struct of_device_id stmpe_gpio_of_matches[] = {
{ .compatible = "st,stmpe-gpio", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, stmpe_gpio_of_matches);
static struct platform_driver stmpe_gpio_driver = {
.driver = {
.suppress_bind_attrs = true,
.name = "stmpe-gpio",
.name = "stmpe-gpio",
.of_match_table = stmpe_gpio_of_matches,
},
.probe = stmpe_gpio_probe,
};
@ -547,3 +551,13 @@ static int __init stmpe_gpio_init(void)
return platform_driver_register(&stmpe_gpio_driver);
}
subsys_initcall(stmpe_gpio_init);
static void __exit stmpe_gpio_exit(void)
{
platform_driver_unregister(&stmpe_gpio_driver);
}
module_exit(stmpe_gpio_exit);
MODULE_DESCRIPTION("STMPE expander GPIO");
MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
MODULE_LICENSE("GPL");

View File

@ -7,20 +7,20 @@
* Christian Ruppert <christian.ruppert@abilis.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/gpio/generic.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define TB10X_GPIO_DIR_IN (0x00000000)
#define TB10X_GPIO_DIR_OUT (0x00000001)
@ -36,13 +36,13 @@
* @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
* @chip: Generic GPIO chip structure associated with this GPIO controller
*/
struct tb10x_gpio {
void __iomem *base;
struct irq_domain *domain;
int irq;
struct gpio_chip gc;
struct gpio_generic_chip chip;
};
static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
@ -60,16 +60,13 @@ static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
u32 mask, u32 val)
{
u32 r;
unsigned long flags;
raw_spin_lock_irqsave(&gpio->gc.bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(&gpio->chip);
r = tb10x_reg_read(gpio, offs);
r = (r & ~mask) | (val & mask);
tb10x_reg_write(gpio, offs, r);
raw_spin_unlock_irqrestore(&gpio->gc.bgpio_lock, flags);
}
static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
@ -107,6 +104,7 @@ static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
static int tb10x_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct tb10x_gpio *tb10x_gpio;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
@ -127,9 +125,9 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
if (IS_ERR(tb10x_gpio->base))
return PTR_ERR(tb10x_gpio->base);
tb10x_gpio->gc.label =
tb10x_gpio->chip.gc.label =
devm_kasprintf(dev, GFP_KERNEL, "%pOF", pdev->dev.of_node);
if (!tb10x_gpio->gc.label)
if (!tb10x_gpio->chip.gc.label)
return -ENOMEM;
/*
@ -137,29 +135,30 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
* 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);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = tb10x_gpio->base + OFFSET_TO_REG_DATA,
.dirout = tb10x_gpio->base + OFFSET_TO_REG_DDR,
};
ret = gpio_generic_chip_init(&tb10x_gpio->chip, &config);
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;
tb10x_gpio->chip.gc.base = -1;
tb10x_gpio->chip.gc.parent = dev;
tb10x_gpio->chip.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.
* ngpio is set by gpio_generic_chip_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;
tb10x_gpio->chip.gc.ngpio = ngpio;
tb10x_gpio->chip.gc.request = gpiochip_generic_request;
tb10x_gpio->chip.gc.free = gpiochip_generic_free;
ret = devm_gpiochip_add_data(dev, &tb10x_gpio->gc, tb10x_gpio);
ret = devm_gpiochip_add_data(dev, &tb10x_gpio->chip.gc, tb10x_gpio);
if (ret < 0) {
dev_err(dev, "Could not add gpiochip.\n");
return ret;
@ -174,7 +173,7 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
tb10x_gpio->chip.gc.to_irq = tb10x_gpio_to_irq;
tb10x_gpio->irq = ret;
ret = devm_request_irq(dev, ret, tb10x_gpio_irq_cascade,
@ -183,14 +182,15 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
if (ret != 0)
return ret;
tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev), tb10x_gpio->gc.ngpio,
tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev),
tb10x_gpio->chip.gc.ngpio,
&irq_generic_chip_ops, NULL);
if (!tb10x_gpio->domain) {
return -ENOMEM;
}
ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
tb10x_gpio->gc.ngpio, 1, tb10x_gpio->gc.label,
tb10x_gpio->chip.gc.ngpio, 1, tb10x_gpio->chip.gc.label,
handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE,
IRQ_GC_INIT_MASK_CACHE);
if (ret)
@ -218,9 +218,9 @@ static void tb10x_gpio_remove(struct platform_device *pdev)
{
struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
if (tb10x_gpio->gc.to_irq) {
if (tb10x_gpio->chip.gc.to_irq) {
irq_remove_generic_chip(tb10x_gpio->domain->gc->gc[0],
BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0);
BIT(tb10x_gpio->chip.gc.ngpio) - 1, 0, 0);
kfree(tb10x_gpio->domain->gc);
irq_domain_remove(tb10x_gpio->domain);
}

View File

@ -20,6 +20,7 @@
#include <dt-bindings/gpio/tegra194-gpio.h>
#include <dt-bindings/gpio/tegra234-gpio.h>
#include <dt-bindings/gpio/tegra241-gpio.h>
#include <dt-bindings/gpio/tegra256-gpio.h>
/* security registers */
#define TEGRA186_GPIO_CTL_SCR 0x0c
@ -1279,6 +1280,30 @@ static const struct tegra_gpio_soc tegra241_aon_soc = {
.has_vm_support = false,
};
#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA256_MAIN_GPIO_PORT_##_name] = { \
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra256_main_ports[] = {
TEGRA256_MAIN_GPIO_PORT(A, 0, 0, 8),
TEGRA256_MAIN_GPIO_PORT(B, 0, 1, 8),
TEGRA256_MAIN_GPIO_PORT(C, 0, 2, 8),
TEGRA256_MAIN_GPIO_PORT(D, 0, 3, 8),
};
static const struct tegra_gpio_soc tegra256_main_soc = {
.num_ports = ARRAY_SIZE(tegra256_main_ports),
.ports = tegra256_main_ports,
.name = "tegra256-gpio-main",
.instance = 1,
.num_irqs_per_bank = 8,
.has_vm_support = true,
};
static const struct of_device_id tegra186_gpio_of_match[] = {
{
.compatible = "nvidia,tegra186-gpio",
@ -1298,6 +1323,9 @@ static const struct of_device_id tegra186_gpio_of_match[] = {
}, {
.compatible = "nvidia,tegra234-gpio-aon",
.data = &tegra234_aon_soc
}, {
.compatible = "nvidia,tegra256-gpio",
.data = &tegra256_main_soc
}, {
/* sentinel */
}

View File

@ -6,9 +6,10 @@
*/
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#define DEFAULT_PIN_NUMBER 16
#define INPUT_REG_OFFSET 0x00
@ -17,13 +18,14 @@
static int ts4800_gpio_probe(struct platform_device *pdev)
{
struct device_node *node;
struct gpio_chip *chip;
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct gpio_generic_chip *chip;
void __iomem *base_addr;
int retval;
u32 ngpios;
chip = devm_kzalloc(&pdev->dev, sizeof(struct gpio_chip), GFP_KERNEL);
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
@ -31,29 +33,28 @@ static int ts4800_gpio_probe(struct platform_device *pdev)
if (IS_ERR(base_addr))
return PTR_ERR(base_addr);
node = pdev->dev.of_node;
if (!node)
return -EINVAL;
retval = of_property_read_u32(node, "ngpios", &ngpios);
retval = device_property_read_u32(dev, "ngpios", &ngpios);
if (retval == -EINVAL)
ngpios = DEFAULT_PIN_NUMBER;
else if (retval)
return retval;
retval = bgpio_init(chip, &pdev->dev, 2, base_addr + INPUT_REG_OFFSET,
base_addr + OUTPUT_REG_OFFSET, NULL,
base_addr + DIRECTION_REG_OFFSET, NULL, 0);
if (retval) {
dev_err(&pdev->dev, "bgpio_init failed\n");
return retval;
}
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 2,
.dat = base_addr + INPUT_REG_OFFSET,
.set = base_addr + OUTPUT_REG_OFFSET,
.dirout = base_addr + DIRECTION_REG_OFFSET,
};
chip->ngpio = ngpios;
retval = gpio_generic_chip_init(chip, &config);
if (retval)
return dev_err_probe(dev, retval,
"failed to initialize the generic GPIO chip\n");
platform_set_drvdata(pdev, chip);
chip->gc.ngpio = ngpios;
return devm_gpiochip_add_data(&pdev->dev, chip, NULL);
return devm_gpiochip_add_data(dev, &chip->gc, NULL);
}
static const struct of_device_id ts4800_gpio_of_match[] = {

View File

@ -597,9 +597,7 @@ no_irqs:
ret = devm_add_action_or_reset(&pdev->dev, gpio_twl4030_power_off_action, d);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to install power off handler\n");
return ret;
}
return 0;

View File

@ -10,6 +10,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -28,7 +29,7 @@ struct fsl_gpio_soc_data {
};
struct vf610_gpio_port {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *base;
void __iomem *gpio_base;
const struct fsl_gpio_soc_data *sdata;
@ -108,7 +109,7 @@ static void vf610_gpio_irq_handler(struct irq_desc *desc)
for_each_set_bit(pin, &irq_isfr, VF610_GPIO_PER_PORT) {
vf610_gpio_writel(BIT(pin), port->base + PORT_ISFR);
generic_handle_domain_irq(port->gc.irq.domain, pin);
generic_handle_domain_irq(port->chip.gc.irq.domain, pin);
}
chained_irq_exit(chip, desc);
@ -214,6 +215,7 @@ static void vf610_gpio_disable_clk(void *data)
static int vf610_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct vf610_gpio_port *port;
struct gpio_chip *gc;
@ -293,22 +295,27 @@ static int vf610_gpio_probe(struct platform_device *pdev)
return ret;
}
gc = &port->gc;
flags = BGPIOF_PINCTRL_BACKEND;
gc = &port->chip.gc;
flags = GPIO_GENERIC_PINCTRL_BACKEND;
/*
* We only read the output register for current value on output
* lines if the direction register is available so we can switch
* direction.
*/
if (port->sdata->have_paddr)
flags |= BGPIOF_READ_OUTPUT_REG_SET;
ret = bgpio_init(gc, dev, 4,
port->gpio_base + GPIO_PDIR,
port->gpio_base + GPIO_PDOR,
NULL,
port->sdata->have_paddr ? port->gpio_base + GPIO_PDDR : NULL,
NULL,
flags);
flags |= GPIO_GENERIC_READ_OUTPUT_REG_SET;
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = port->gpio_base + GPIO_PDIR,
.set = port->gpio_base + GPIO_PDOR,
.dirout = port->sdata->have_paddr ?
port->gpio_base + GPIO_PDDR : NULL,
.flags = flags,
};
ret = gpio_generic_chip_init(&port->chip, &config);
if (ret)
return dev_err_probe(dev, ret, "unable to init generic GPIO\n");
gc->label = dev_name(dev);

View File

@ -10,6 +10,7 @@
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@ -32,7 +33,7 @@
struct visconti_gpio {
void __iomem *base;
spinlock_t lock; /* protect gpio register */
struct gpio_chip gpio_chip;
struct gpio_generic_chip chip;
struct device *dev;
};
@ -158,6 +159,7 @@ static const struct irq_chip visconti_gpio_irq_chip = {
static int visconti_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct visconti_gpio *priv;
struct gpio_irq_chip *girq;
@ -189,19 +191,22 @@ static int visconti_gpio_probe(struct platform_device *pdev)
return -ENODEV;
}
ret = bgpio_init(&priv->gpio_chip, dev, 4,
priv->base + GPIO_IDATA,
priv->base + GPIO_OSET,
priv->base + GPIO_OCLR,
priv->base + GPIO_DIR,
NULL,
0);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = priv->base + GPIO_IDATA,
.set = priv->base + GPIO_OSET,
.clr = priv->base + GPIO_OCLR,
.dirout = priv->base + GPIO_DIR,
};
ret = gpio_generic_chip_init(&priv->chip, &config);
if (ret) {
dev_err(dev, "unable to init generic GPIO\n");
return ret;
}
girq = &priv->gpio_chip.irq;
girq = &priv->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &visconti_gpio_irq_chip);
girq->fwnode = dev_fwnode(dev);
girq->parent_domain = parent;
@ -210,7 +215,7 @@ static int visconti_gpio_probe(struct platform_device *pdev)
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_level_irq;
return devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
return devm_gpiochip_add_data(dev, &priv->chip.gc, priv);
}
static const struct of_device_id visconti_gpio_of_match[] = {

View File

@ -159,7 +159,6 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
int i, tristated;
for (i = 0; i < chip->ngpio; i++) {
int gpio = i + chip->base;
int reg;
const char *pull, *powerdomain;
@ -175,13 +174,13 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
}
seq_printf(s, " gpio-%-3d (%-20.20s) ",
gpio, label ?: "Unrequested");
i, label ?: "Unrequested");
reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i);
if (reg < 0) {
dev_err(wm831x->dev,
"GPIO control %d read failed: %d\n",
gpio, reg);
i, reg);
seq_putc(s, '\n');
continue;
}

View File

@ -194,7 +194,6 @@ static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
int i;
for (i = 0; i < chip->ngpio; i++) {
int gpio = i + chip->base;
int reg;
/* We report the GPIO even if it's not requested since
@ -208,14 +207,13 @@ static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
continue;
}
seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio,
seq_printf(s, " gpio-%-3d (%-20.20s) ", i,
label ?: "Unrequested");
reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i);
if (reg < 0) {
dev_err(wm8994->dev,
"GPIO control %d read failed: %d\n",
gpio, reg);
"GPIO control %d read failed: %d\n", i, reg);
seq_printf(s, "\n");
continue;
}

View File

@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include "gpiolib-acpi.h"
@ -40,7 +41,7 @@
/**
* struct xgene_gpio_sb - GPIO-Standby private data structure.
* @gc: memory-mapped GPIO controllers.
* @chip: Generic GPIO chip data
* @regs: GPIO register base offset
* @irq_domain: GPIO interrupt domain
* @irq_start: GPIO pin that start support interrupt
@ -48,7 +49,7 @@
* @parent_irq_base: Start parent HWIRQ
*/
struct xgene_gpio_sb {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *regs;
struct irq_domain *irq_domain;
u16 irq_start;
@ -62,14 +63,15 @@ struct xgene_gpio_sb {
static void xgene_gpio_set_bit(struct gpio_chip *gc,
void __iomem *reg, u32 gpio, int val)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
u32 data;
data = gc->read_reg(reg);
data = gpio_generic_read_reg(chip, reg);
if (val)
data |= GPIO_MASK(gpio);
else
data &= ~GPIO_MASK(gpio);
gc->write_reg(reg, data);
gpio_generic_write_reg(chip, reg, data);
}
static int xgene_gpio_sb_irq_set_type(struct irq_data *d, unsigned int type)
@ -91,9 +93,9 @@ static int xgene_gpio_sb_irq_set_type(struct irq_data *d, unsigned int type)
break;
}
xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
xgene_gpio_set_bit(&priv->chip.gc, priv->regs + MPA_GPIO_SEL_LO,
gpio * 2, 1);
xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_INT_LVL,
xgene_gpio_set_bit(&priv->chip.gc, priv->regs + MPA_GPIO_INT_LVL,
d->hwirq, lvl_type);
/* Propagate IRQ type setting to parent */
@ -109,14 +111,14 @@ static void xgene_gpio_sb_irq_mask(struct irq_data *d)
irq_chip_mask_parent(d);
gpiochip_disable_irq(&priv->gc, d->hwirq);
gpiochip_disable_irq(&priv->chip.gc, d->hwirq);
}
static void xgene_gpio_sb_irq_unmask(struct irq_data *d)
{
struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d);
gpiochip_enable_irq(&priv->gc, d->hwirq);
gpiochip_enable_irq(&priv->chip.gc, d->hwirq);
irq_chip_unmask_parent(d);
}
@ -155,15 +157,15 @@ static int xgene_gpio_sb_domain_activate(struct irq_domain *d,
u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);
int ret;
ret = gpiochip_lock_as_irq(&priv->gc, gpio);
ret = gpiochip_lock_as_irq(&priv->chip.gc, gpio);
if (ret) {
dev_err(priv->gc.parent,
dev_err(priv->chip.gc.parent,
"Unable to configure XGene GPIO standby pin %d as IRQ\n",
gpio);
return ret;
}
xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
xgene_gpio_set_bit(&priv->chip.gc, priv->regs + MPA_GPIO_SEL_LO,
gpio * 2, 1);
return 0;
}
@ -174,8 +176,8 @@ static void xgene_gpio_sb_domain_deactivate(struct irq_domain *d,
struct xgene_gpio_sb *priv = d->host_data;
u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);
gpiochip_unlock_as_irq(&priv->gc, gpio);
xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
gpiochip_unlock_as_irq(&priv->chip.gc, gpio);
xgene_gpio_set_bit(&priv->chip.gc, priv->regs + MPA_GPIO_SEL_LO,
gpio * 2, 0);
}
@ -237,6 +239,7 @@ static const struct irq_domain_ops xgene_gpio_sb_domain_ops = {
static int xgene_gpio_sb_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct xgene_gpio_sb *priv;
int ret;
void __iomem *regs;
@ -263,14 +266,19 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
return -ENODEV;
}
ret = bgpio_init(&priv->gc, &pdev->dev, 4,
regs + MPA_GPIO_IN_ADDR,
regs + MPA_GPIO_OUT_ADDR, NULL,
regs + MPA_GPIO_OE_ADDR, NULL, 0);
config = (struct gpio_generic_chip_config) {
.dev = &pdev->dev,
.sz = 4,
.dat = regs + MPA_GPIO_IN_ADDR,
.set = regs + MPA_GPIO_OUT_ADDR,
.dirout = regs + MPA_GPIO_OE_ADDR,
};
ret = gpio_generic_chip_init(&priv->chip, &config);
if (ret)
return ret;
priv->gc.to_irq = xgene_gpio_sb_to_irq;
priv->chip.gc.to_irq = xgene_gpio_sb_to_irq;
/* Retrieve start irq pin, use default if property not found */
priv->irq_start = XGENE_DFLT_IRQ_START_PIN;
@ -283,12 +291,12 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
priv->nirq = val32;
/* Retrieve number gpio, use default if property not found */
priv->gc.ngpio = XGENE_DFLT_MAX_NGPIO;
priv->chip.gc.ngpio = XGENE_DFLT_MAX_NGPIO;
if (!device_property_read_u32(&pdev->dev, "apm,nr-gpios", &val32))
priv->gc.ngpio = val32;
priv->chip.gc.ngpio = val32;
dev_info(&pdev->dev, "Support %d gpios, %d irqs start from pin %d\n",
priv->gc.ngpio, priv->nirq, priv->irq_start);
priv->chip.gc.ngpio, priv->nirq, priv->irq_start);
platform_set_drvdata(pdev, priv);
@ -298,9 +306,9 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
if (!priv->irq_domain)
return -ENODEV;
priv->gc.irq.domain = priv->irq_domain;
priv->chip.gc.irq.domain = priv->irq_domain;
ret = devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
ret = devm_gpiochip_add_data(&pdev->dev, &priv->chip.gc, priv);
if (ret) {
dev_err(&pdev->dev,
"failed to register X-Gene GPIO Standby driver\n");
@ -311,7 +319,7 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
/* Register interrupt handlers for GPIO signaled ACPI Events */
acpi_gpiochip_request_interrupts(&priv->gc);
acpi_gpiochip_request_interrupts(&priv->chip.gc);
return ret;
}
@ -320,7 +328,7 @@ static void xgene_gpio_sb_remove(struct platform_device *pdev)
{
struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);
acpi_gpiochip_free_interrupts(&priv->gc);
acpi_gpiochip_free_interrupts(&priv->chip.gc);
irq_domain_remove(priv->irq_domain);
}

View File

@ -3,11 +3,12 @@
* Copyright (C) 2017 Broadcom
*/
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@ -28,7 +29,7 @@
#define IPROC_GPIO_CCA_INT_EDGE 0x24
struct iproc_gpio_chip {
struct gpio_chip gc;
struct gpio_generic_chip gen_gc;
spinlock_t lock;
struct device *dev;
void __iomem *base;
@ -38,7 +39,7 @@ struct iproc_gpio_chip {
static inline struct iproc_gpio_chip *
to_iproc_gpio(struct gpio_chip *gc)
{
return container_of(gc, struct iproc_gpio_chip, gc);
return container_of(to_gpio_generic_chip(gc), struct iproc_gpio_chip, gen_gc);
}
static void iproc_gpio_irq_ack(struct irq_data *d)
@ -213,6 +214,7 @@ static const struct irq_chip iproc_gpio_irq_chip = {
static int iproc_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
struct device_node *dn = pdev->dev.of_node;
struct iproc_gpio_chip *chip;
@ -231,21 +233,23 @@ static int iproc_gpio_probe(struct platform_device *pdev)
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
ret = bgpio_init(&chip->gc, dev, 4,
chip->base + IPROC_GPIO_CCA_DIN,
chip->base + IPROC_GPIO_CCA_DOUT,
NULL,
chip->base + IPROC_GPIO_CCA_OUT_EN,
NULL,
0);
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = chip->base + IPROC_GPIO_CCA_DIN,
.set = chip->base + IPROC_GPIO_CCA_DOUT,
.dirout = chip->base + IPROC_GPIO_CCA_OUT_EN,
};
ret = gpio_generic_chip_init(&chip->gen_gc, &config);
if (ret) {
dev_err(dev, "unable to init GPIO chip\n");
return ret;
}
chip->gc.label = dev_name(dev);
chip->gen_gc.gc.label = dev_name(dev);
if (!of_property_read_u32(dn, "ngpios", &num_gpios))
chip->gc.ngpio = num_gpios;
chip->gen_gc.gc.ngpio = num_gpios;
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
@ -266,13 +270,13 @@ static int iproc_gpio_probe(struct platform_device *pdev)
* a flow-handler because the irq is shared.
*/
ret = devm_request_irq(dev, irq, iproc_gpio_irq_handler,
IRQF_SHARED, chip->gc.label, &chip->gc);
IRQF_SHARED, chip->gen_gc.gc.label, &chip->gen_gc.gc);
if (ret) {
dev_err(dev, "Fail to request IRQ%d: %d\n", irq, ret);
return ret;
}
girq = &chip->gc.irq;
girq = &chip->gen_gc.gc.irq;
gpio_irq_chip_set_chip(girq, &iproc_gpio_irq_chip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
@ -282,7 +286,7 @@ static int iproc_gpio_probe(struct platform_device *pdev)
girq->handler = handle_simple_irq;
}
ret = devm_gpiochip_add_data(dev, &chip->gc, chip);
ret = devm_gpiochip_add_data(dev, &chip->gen_gc.gc, chip);
if (ret) {
dev_err(dev, "unable to add GPIO chip\n");
return ret;

View File

@ -135,8 +135,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
gcr = value[XRA_GCR + 1] << 8 | value[XRA_GCR];
gsr = value[XRA_GSR + 1] << 8 | value[XRA_GSR];
for_each_requested_gpio(chip, i, label) {
seq_printf(s, " gpio-%-3d (%-12s) %s %s\n",
chip->base + i, label,
seq_printf(s, " gpio-%-3d (%-12s) %s %s\n", i, label,
(gcr & BIT(i)) ? "in" : "out",
str_hi_lo(gsr & BIT(i)));
}

View File

@ -144,17 +144,17 @@ static void linehandle_flags_to_desc_flags(u32 lflags, unsigned long *flagsp)
{
unsigned long flags = READ_ONCE(*flagsp);
assign_bit(FLAG_ACTIVE_LOW, &flags,
assign_bit(GPIOD_FLAG_ACTIVE_LOW, &flags,
lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW);
assign_bit(FLAG_OPEN_DRAIN, &flags,
assign_bit(GPIOD_FLAG_OPEN_DRAIN, &flags,
lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN);
assign_bit(FLAG_OPEN_SOURCE, &flags,
assign_bit(GPIOD_FLAG_OPEN_SOURCE, &flags,
lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE);
assign_bit(FLAG_PULL_UP, &flags,
assign_bit(GPIOD_FLAG_PULL_UP, &flags,
lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP);
assign_bit(FLAG_PULL_DOWN, &flags,
assign_bit(GPIOD_FLAG_PULL_DOWN, &flags,
lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN);
assign_bit(FLAG_BIAS_DISABLE, &flags,
assign_bit(GPIOD_FLAG_BIAS_DISABLE, &flags,
lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE);
WRITE_ONCE(*flagsp, flags);
@ -238,7 +238,7 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
* All line descriptors were created at once with the same
* flags so just check if the first one is really output.
*/
if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags))
if (!test_bit(GPIOD_FLAG_IS_OUT, &lh->descs[0]->flags))
return -EPERM;
if (copy_from_user(&ghd, ip, sizeof(ghd)))
@ -599,10 +599,10 @@ static void linereq_put_event(struct linereq *lr,
static u64 line_event_timestamp(struct line *line)
{
if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags))
if (test_bit(GPIOD_FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags))
return ktime_get_real_ns();
else if (IS_ENABLED(CONFIG_HTE) &&
test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))
test_bit(GPIOD_FLAG_EVENT_CLOCK_HTE, &line->desc->flags))
return line->timestamp_ns;
return ktime_get_ns();
@ -725,11 +725,11 @@ static int hte_edge_setup(struct line *line, u64 eflags)
struct hte_ts_desc *hdesc = &line->hdesc;
if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING)
flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
flags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &line->desc->flags) ?
HTE_FALLING_EDGE_TS :
HTE_RISING_EDGE_TS;
if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING)
flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
flags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &line->desc->flags) ?
HTE_RISING_EDGE_TS :
HTE_FALLING_EDGE_TS;
@ -831,7 +831,7 @@ static bool debounced_value(struct line *line)
*/
value = READ_ONCE(line->level);
if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags))
if (test_bit(GPIOD_FLAG_ACTIVE_LOW, &line->desc->flags))
value = !value;
return value;
@ -939,7 +939,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
return level;
if (!(IS_ENABLED(CONFIG_HTE) &&
test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))) {
test_bit(GPIOD_FLAG_EVENT_CLOCK_HTE, &line->desc->flags))) {
irq = gpiod_to_irq(line->desc);
if (irq < 0)
return -ENXIO;
@ -1061,10 +1061,10 @@ static int edge_detector_setup(struct line *line,
return -ENXIO;
if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING)
irqflags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
irqflags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &line->desc->flags) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING)
irqflags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
irqflags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &line->desc->flags) ?
IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
irqflags |= IRQF_ONESHOT;
@ -1237,34 +1237,34 @@ static void gpio_v2_line_config_flags_to_desc_flags(u64 lflags,
{
unsigned long flags = READ_ONCE(*flagsp);
assign_bit(FLAG_ACTIVE_LOW, &flags,
assign_bit(GPIOD_FLAG_ACTIVE_LOW, &flags,
lflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
if (lflags & GPIO_V2_LINE_FLAG_OUTPUT)
set_bit(FLAG_IS_OUT, &flags);
set_bit(GPIOD_FLAG_IS_OUT, &flags);
else if (lflags & GPIO_V2_LINE_FLAG_INPUT)
clear_bit(FLAG_IS_OUT, &flags);
clear_bit(GPIOD_FLAG_IS_OUT, &flags);
assign_bit(FLAG_EDGE_RISING, &flags,
assign_bit(GPIOD_FLAG_EDGE_RISING, &flags,
lflags & GPIO_V2_LINE_FLAG_EDGE_RISING);
assign_bit(FLAG_EDGE_FALLING, &flags,
assign_bit(GPIOD_FLAG_EDGE_FALLING, &flags,
lflags & GPIO_V2_LINE_FLAG_EDGE_FALLING);
assign_bit(FLAG_OPEN_DRAIN, &flags,
assign_bit(GPIOD_FLAG_OPEN_DRAIN, &flags,
lflags & GPIO_V2_LINE_FLAG_OPEN_DRAIN);
assign_bit(FLAG_OPEN_SOURCE, &flags,
assign_bit(GPIOD_FLAG_OPEN_SOURCE, &flags,
lflags & GPIO_V2_LINE_FLAG_OPEN_SOURCE);
assign_bit(FLAG_PULL_UP, &flags,
assign_bit(GPIOD_FLAG_PULL_UP, &flags,
lflags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP);
assign_bit(FLAG_PULL_DOWN, &flags,
assign_bit(GPIOD_FLAG_PULL_DOWN, &flags,
lflags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN);
assign_bit(FLAG_BIAS_DISABLE, &flags,
assign_bit(GPIOD_FLAG_BIAS_DISABLE, &flags,
lflags & GPIO_V2_LINE_FLAG_BIAS_DISABLED);
assign_bit(FLAG_EVENT_CLOCK_REALTIME, &flags,
assign_bit(GPIOD_FLAG_EVENT_CLOCK_REALTIME, &flags,
lflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME);
assign_bit(FLAG_EVENT_CLOCK_HTE, &flags,
assign_bit(GPIOD_FLAG_EVENT_CLOCK_HTE, &flags,
lflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE);
WRITE_ONCE(*flagsp, flags);
@ -2115,10 +2115,10 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
}
if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
irqflags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE)
irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
irqflags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
irqflags |= IRQF_ONESHOT;
@ -2253,7 +2253,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
scoped_guard(srcu, &desc->gdev->desc_srcu) {
label = gpiod_get_label(desc);
if (label && test_bit(FLAG_REQUESTED, &dflags))
if (label && test_bit(GPIOD_FLAG_REQUESTED, &dflags))
strscpy(info->consumer, label,
sizeof(info->consumer));
}
@ -2270,10 +2270,10 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
* The definitive test that a line is available to userspace is to
* request it.
*/
if (test_bit(FLAG_REQUESTED, &dflags) ||
test_bit(FLAG_IS_HOGGED, &dflags) ||
test_bit(FLAG_EXPORT, &dflags) ||
test_bit(FLAG_SYSFS, &dflags) ||
if (test_bit(GPIOD_FLAG_REQUESTED, &dflags) ||
test_bit(GPIOD_FLAG_IS_HOGGED, &dflags) ||
test_bit(GPIOD_FLAG_EXPORT, &dflags) ||
test_bit(GPIOD_FLAG_SYSFS, &dflags) ||
!gpiochip_line_is_valid(guard.gc, info->offset)) {
info->flags |= GPIO_V2_LINE_FLAG_USED;
} else if (!atomic) {
@ -2281,34 +2281,34 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
info->flags |= GPIO_V2_LINE_FLAG_USED;
}
if (test_bit(FLAG_IS_OUT, &dflags))
if (test_bit(GPIOD_FLAG_IS_OUT, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_OUTPUT;
else
info->flags |= GPIO_V2_LINE_FLAG_INPUT;
if (test_bit(FLAG_ACTIVE_LOW, &dflags))
if (test_bit(GPIOD_FLAG_ACTIVE_LOW, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
if (test_bit(FLAG_OPEN_DRAIN, &dflags))
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN;
if (test_bit(FLAG_OPEN_SOURCE, &dflags))
if (test_bit(GPIOD_FLAG_OPEN_SOURCE, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE;
if (test_bit(FLAG_BIAS_DISABLE, &dflags))
if (test_bit(GPIOD_FLAG_BIAS_DISABLE, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED;
if (test_bit(FLAG_PULL_DOWN, &dflags))
if (test_bit(GPIOD_FLAG_PULL_DOWN, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN;
if (test_bit(FLAG_PULL_UP, &dflags))
if (test_bit(GPIOD_FLAG_PULL_UP, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
if (test_bit(FLAG_EDGE_RISING, &dflags))
if (test_bit(GPIOD_FLAG_EDGE_RISING, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_EDGE_RISING;
if (test_bit(FLAG_EDGE_FALLING, &dflags))
if (test_bit(GPIOD_FLAG_EDGE_FALLING, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING;
if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &dflags))
if (test_bit(GPIOD_FLAG_EVENT_CLOCK_REALTIME, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME;
else if (test_bit(FLAG_EVENT_CLOCK_HTE, &dflags))
else if (test_bit(GPIOD_FLAG_EVENT_CLOCK_HTE, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE;
debounce_period_us = READ_ONCE(desc->debounce_period_us);

View File

@ -878,7 +878,7 @@ static void of_gpiochip_remove_hog(struct gpio_chip *chip,
{
struct gpio_desc *desc;
for_each_gpio_desc_with_flag(chip, desc, FLAG_IS_HOGGED)
for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_IS_HOGGED)
if (READ_ONCE(desc->hog) == hog)
gpiochip_free_own_desc(desc);
}

View File

@ -131,7 +131,7 @@ static ssize_t direction_show(struct device *dev,
scoped_guard(mutex, &data->mutex) {
gpiod_get_direction(desc);
value = !!test_bit(FLAG_IS_OUT, &desc->flags);
value = !!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags);
}
return sysfs_emit(buf, "%s\n", value ? "out" : "in");
@ -226,14 +226,14 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
irq_flags = IRQF_SHARED;
if (flags & GPIO_IRQF_TRIGGER_FALLING) {
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
irq_flags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
set_bit(FLAG_EDGE_FALLING, &desc->flags);
set_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
}
if (flags & GPIO_IRQF_TRIGGER_RISING) {
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
irq_flags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
set_bit(FLAG_EDGE_RISING, &desc->flags);
set_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
}
/*
@ -260,8 +260,8 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
err_unlock:
gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
err_clr_bits:
clear_bit(FLAG_EDGE_RISING, &desc->flags);
clear_bit(FLAG_EDGE_FALLING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
return ret;
}
@ -281,8 +281,8 @@ static void gpio_sysfs_free_irq(struct gpiod_data *data)
data->irq_flags = 0;
free_irq(data->irq, data);
gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
clear_bit(FLAG_EDGE_RISING, &desc->flags);
clear_bit(FLAG_EDGE_FALLING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
}
static const char *const trigger_names[] = {
@ -347,10 +347,10 @@ static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value)
struct gpio_desc *desc = data->desc;
int status = 0;
if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)
if (!!test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags) == !!value)
return 0;
assign_bit(FLAG_ACTIVE_LOW, &desc->flags, value);
assign_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags, value);
/* reconfigure poll(2) support if enabled on one edge only */
if (flags == GPIO_IRQF_TRIGGER_FALLING ||
@ -373,7 +373,7 @@ static ssize_t active_low_show(struct device *dev,
int value;
scoped_guard(mutex, &data->mutex)
value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags);
value = !!test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags);
return sysfs_emit(buf, "%d\n", value);
}
@ -418,7 +418,7 @@ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
mode = 0;
if (!data->direction_can_change &&
test_bit(FLAG_IS_OUT, &data->desc->flags))
test_bit(GPIOD_FLAG_IS_OUT, &data->desc->flags))
mode = 0;
#endif /* CONFIG_GPIO_SYSFS_LEGACY */
}
@ -486,7 +486,7 @@ static int export_gpio_desc(struct gpio_desc *desc)
}
/*
* No extra locking here; FLAG_SYSFS just signifies that the
* No extra locking here; GPIOD_FLAG_SYSFS just signifies that the
* request and export were done by on behalf of userspace, so
* they may be undone on its behalf too.
*/
@ -505,7 +505,7 @@ static int export_gpio_desc(struct gpio_desc *desc)
if (ret < 0) {
gpiod_free(desc);
} else {
set_bit(FLAG_SYSFS, &desc->flags);
set_bit(GPIOD_FLAG_SYSFS, &desc->flags);
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
}
@ -515,11 +515,11 @@ static int export_gpio_desc(struct gpio_desc *desc)
static int unexport_gpio_desc(struct gpio_desc *desc)
{
/*
* No extra locking here; FLAG_SYSFS just signifies that the
* No extra locking here; GPIOD_FLAG_SYSFS just signifies that the
* request and export were done by on behalf of userspace, so
* they may be undone on its behalf too.
*/
if (!test_and_clear_bit(FLAG_SYSFS, &desc->flags))
if (!test_and_clear_bit(GPIOD_FLAG_SYSFS, &desc->flags))
return -EINVAL;
gpiod_unexport(desc);
@ -748,14 +748,14 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
if (!guard.gc)
return -ENODEV;
if (test_and_set_bit(FLAG_EXPORT, &desc->flags))
if (test_and_set_bit(GPIOD_FLAG_EXPORT, &desc->flags))
return -EPERM;
gdev = desc->gdev;
guard(mutex)(&sysfs_lock);
if (!test_bit(FLAG_REQUESTED, &desc->flags)) {
if (!test_bit(GPIOD_FLAG_REQUESTED, &desc->flags)) {
gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__);
status = -EPERM;
goto err_clear_bit;
@ -866,7 +866,7 @@ err_free_data:
#endif /* CONFIG_GPIO_SYSFS_LEGACY */
kfree(desc_data);
err_clear_bit:
clear_bit(FLAG_EXPORT, &desc->flags);
clear_bit(GPIOD_FLAG_EXPORT, &desc->flags);
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}
@ -937,7 +937,7 @@ void gpiod_unexport(struct gpio_desc *desc)
}
scoped_guard(mutex, &sysfs_lock) {
if (!test_bit(FLAG_EXPORT, &desc->flags))
if (!test_bit(GPIOD_FLAG_EXPORT, &desc->flags))
return;
gdev = gpiod_to_gpio_device(desc);
@ -956,7 +956,7 @@ void gpiod_unexport(struct gpio_desc *desc)
return;
list_del(&desc_data->list);
clear_bit(FLAG_EXPORT, &desc->flags);
clear_bit(GPIOD_FLAG_EXPORT, &desc->flags);
#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
sysfs_put(desc_data->value_kn);
device_unregister(desc_data->dev);
@ -1073,7 +1073,7 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
return;
/* unregister gpiod class devices owned by sysfs */
for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) {
for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_SYSFS) {
gpiod_unexport(desc);
gpiod_free(desc);
}

View File

@ -127,10 +127,10 @@ const char *gpiod_get_label(struct gpio_desc *desc)
label = srcu_dereference_check(desc->label, &desc->gdev->desc_srcu,
srcu_read_lock_held(&desc->gdev->desc_srcu));
if (test_bit(FLAG_USED_AS_IRQ, &flags))
if (test_bit(GPIOD_FLAG_USED_AS_IRQ, &flags))
return label ? label->str : "interrupt";
if (!test_bit(FLAG_REQUESTED, &flags))
if (!test_bit(GPIOD_FLAG_REQUESTED, &flags))
return NULL;
return label ? label->str : NULL;
@ -450,8 +450,8 @@ int gpiod_get_direction(struct gpio_desc *desc)
* Open drain emulation using input mode may incorrectly report
* input here, fix that up.
*/
if (test_bit(FLAG_OPEN_DRAIN, &flags) &&
test_bit(FLAG_IS_OUT, &flags))
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &flags) &&
test_bit(GPIOD_FLAG_IS_OUT, &flags))
return 0;
if (!guard.gc->get_direction)
@ -468,7 +468,7 @@ int gpiod_get_direction(struct gpio_desc *desc)
if (ret > 0)
ret = 1;
assign_bit(FLAG_IS_OUT, &flags, !ret);
assign_bit(GPIOD_FLAG_IS_OUT, &flags, !ret);
WRITE_ONCE(desc->flags, flags);
return ret;
@ -846,7 +846,7 @@ static void gpiochip_free_remaining_irqs(struct gpio_chip *gc)
{
struct gpio_desc *desc;
for_each_gpio_desc_with_flag(gc, desc, FLAG_USED_AS_IRQ)
for_each_gpio_desc_with_flag(gc, desc, GPIOD_FLAG_USED_AS_IRQ)
gpiod_free_irqs(desc);
}
@ -1169,10 +1169,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* lock here.
*/
if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index))
assign_bit(FLAG_IS_OUT, &desc->flags,
assign_bit(GPIOD_FLAG_IS_OUT, &desc->flags,
!gc->get_direction(gc, desc_index));
else
assign_bit(FLAG_IS_OUT,
assign_bit(GPIOD_FLAG_IS_OUT,
&desc->flags, !gc->direction_input);
}
@ -2349,11 +2349,13 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc,
EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
* gpiochip_add_pin_range_with_pins() - add a range for GPIO <-> pin mapping
* @gc: the gpiochip to add the range for
* @pinctl_name: the dev_name() of the pin controller to map to
* @gpio_offset: the start offset in the current gpio_chip number space
* @pin_offset: the start offset in the pin controller number space
* @pins: the list of non consecutive pins to accumulate in this range (if not
* NULL, pin_offset is ignored by pinctrl core)
* @npins: the number of pins from the offset of each pin space (GPIO and
* pin controller) to accumulate in this range
*
@ -2365,9 +2367,12 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
* Returns:
* 0 on success, or a negative errno on failure.
*/
int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins)
int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc,
const char *pinctl_name,
unsigned int gpio_offset,
unsigned int pin_offset,
unsigned int const *pins,
unsigned int npins)
{
struct gpio_pin_range *pin_range;
struct gpio_device *gdev = gc->gpiodev;
@ -2385,6 +2390,7 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
pin_range->range.name = gc->label;
pin_range->range.base = gdev->base + gpio_offset;
pin_range->range.pin_base = pin_offset;
pin_range->range.pins = pins;
pin_range->range.npins = npins;
pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
&pin_range->range);
@ -2394,16 +2400,21 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
kfree(pin_range);
return ret;
}
chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
gpio_offset, gpio_offset + npins - 1,
pinctl_name,
pin_offset, pin_offset + npins - 1);
if (pin_range->range.pins)
chip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }",
gpio_offset, gpio_offset + npins - 1,
pinctl_name, npins, pins[0]);
else
chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
gpio_offset, gpio_offset + npins - 1,
pinctl_name,
pin_offset, pin_offset + npins - 1);
list_add_tail(&pin_range->node, &gdev->pin_ranges);
return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_add_pin_range);
EXPORT_SYMBOL_GPL(gpiochip_add_pin_range_with_pins);
/**
* gpiochip_remove_pin_ranges() - remove all the GPIO <-> pin mappings
@ -2438,7 +2449,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
if (!guard.gc)
return -ENODEV;
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags))
if (test_and_set_bit(GPIOD_FLAG_REQUESTED, &desc->flags))
return -EBUSY;
offset = gpio_chip_hwgpio(desc);
@ -2467,7 +2478,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
return 0;
out_clear_bit:
clear_bit(FLAG_REQUESTED, &desc->flags);
clear_bit(GPIOD_FLAG_REQUESTED, &desc->flags);
return ret;
}
@ -2501,20 +2512,20 @@ static void gpiod_free_commit(struct gpio_desc *desc)
flags = READ_ONCE(desc->flags);
if (guard.gc && test_bit(FLAG_REQUESTED, &flags)) {
if (guard.gc && test_bit(GPIOD_FLAG_REQUESTED, &flags)) {
if (guard.gc->free)
guard.gc->free(guard.gc, gpio_chip_hwgpio(desc));
clear_bit(FLAG_ACTIVE_LOW, &flags);
clear_bit(FLAG_REQUESTED, &flags);
clear_bit(FLAG_OPEN_DRAIN, &flags);
clear_bit(FLAG_OPEN_SOURCE, &flags);
clear_bit(FLAG_PULL_UP, &flags);
clear_bit(FLAG_PULL_DOWN, &flags);
clear_bit(FLAG_BIAS_DISABLE, &flags);
clear_bit(FLAG_EDGE_RISING, &flags);
clear_bit(FLAG_EDGE_FALLING, &flags);
clear_bit(FLAG_IS_HOGGED, &flags);
clear_bit(GPIOD_FLAG_ACTIVE_LOW, &flags);
clear_bit(GPIOD_FLAG_REQUESTED, &flags);
clear_bit(GPIOD_FLAG_OPEN_DRAIN, &flags);
clear_bit(GPIOD_FLAG_OPEN_SOURCE, &flags);
clear_bit(GPIOD_FLAG_PULL_UP, &flags);
clear_bit(GPIOD_FLAG_PULL_DOWN, &flags);
clear_bit(GPIOD_FLAG_BIAS_DISABLE, &flags);
clear_bit(GPIOD_FLAG_EDGE_RISING, &flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &flags);
clear_bit(GPIOD_FLAG_IS_HOGGED, &flags);
#ifdef CONFIG_OF_DYNAMIC
WRITE_ONCE(desc->hog, NULL);
#endif
@ -2557,7 +2568,7 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
if (IS_ERR(desc))
return NULL;
if (!test_bit(FLAG_REQUESTED, &desc->flags))
if (!test_bit(GPIOD_FLAG_REQUESTED, &desc->flags))
return NULL;
guard(srcu)(&desc->gdev->desc_srcu);
@ -2725,11 +2736,11 @@ static int gpio_set_bias(struct gpio_desc *desc)
flags = READ_ONCE(desc->flags);
if (test_bit(FLAG_BIAS_DISABLE, &flags))
if (test_bit(GPIOD_FLAG_BIAS_DISABLE, &flags))
bias = PIN_CONFIG_BIAS_DISABLE;
else if (test_bit(FLAG_PULL_UP, &flags))
else if (test_bit(GPIOD_FLAG_PULL_UP, &flags))
bias = PIN_CONFIG_BIAS_PULL_UP;
else if (test_bit(FLAG_PULL_DOWN, &flags))
else if (test_bit(GPIOD_FLAG_PULL_DOWN, &flags))
bias = PIN_CONFIG_BIAS_PULL_DOWN;
else
return 0;
@ -2871,7 +2882,7 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc)
}
}
if (ret == 0) {
clear_bit(FLAG_IS_OUT, &desc->flags);
clear_bit(GPIOD_FLAG_IS_OUT, &desc->flags);
ret = gpio_set_bias(desc);
}
@ -2944,7 +2955,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
}
if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
set_bit(GPIOD_FLAG_IS_OUT, &desc->flags);
trace_gpio_value(desc_to_gpio(desc), 0, val);
trace_gpio_direction(desc_to_gpio(desc), 0, ret);
return ret;
@ -3010,21 +3021,21 @@ int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value)
flags = READ_ONCE(desc->flags);
if (test_bit(FLAG_ACTIVE_LOW, &flags))
if (test_bit(GPIOD_FLAG_ACTIVE_LOW, &flags))
value = !value;
else
value = !!value;
/* GPIOs used for enabled IRQs shall not be set as output */
if (test_bit(FLAG_USED_AS_IRQ, &flags) &&
test_bit(FLAG_IRQ_IS_ENABLED, &flags)) {
if (test_bit(GPIOD_FLAG_USED_AS_IRQ, &flags) &&
test_bit(GPIOD_FLAG_IRQ_IS_ENABLED, &flags)) {
gpiod_err(desc,
"%s: tried to set a GPIO tied to an IRQ as output\n",
__func__);
return -EIO;
}
if (test_bit(FLAG_OPEN_DRAIN, &flags)) {
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &flags)) {
/* First see if we can enable open drain in hardware */
ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_DRAIN);
if (!ret)
@ -3032,7 +3043,7 @@ int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value)
/* Emulate open drain by not actively driving the line high */
if (value)
goto set_output_flag;
} else if (test_bit(FLAG_OPEN_SOURCE, &flags)) {
} else if (test_bit(GPIOD_FLAG_OPEN_SOURCE, &flags)) {
ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE);
if (!ret)
goto set_output_value;
@ -3059,7 +3070,7 @@ set_output_flag:
* set the IS_OUT flag or otherwise we won't be able to set the line
* value anymore.
*/
set_bit(FLAG_IS_OUT, &desc->flags);
set_bit(GPIOD_FLAG_IS_OUT, &desc->flags);
return 0;
}
@ -3199,10 +3210,10 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
{
VALIDATE_DESC(desc);
/*
* Handle FLAG_TRANSITORY first, enabling queries to gpiolib for
* Handle GPIOD_FLAG_TRANSITORY first, enabling queries to gpiolib for
* persistence state.
*/
assign_bit(FLAG_TRANSITORY, &desc->flags, transitory);
assign_bit(GPIOD_FLAG_TRANSITORY, &desc->flags, transitory);
/* If the driver supports it, set the persistence state now */
return gpio_set_config_with_argument_optional(desc,
@ -3220,7 +3231,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
int gpiod_is_active_low(const struct gpio_desc *desc)
{
VALIDATE_DESC(desc);
return test_bit(FLAG_ACTIVE_LOW, &desc->flags);
return test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags);
}
EXPORT_SYMBOL_GPL(gpiod_is_active_low);
@ -3231,7 +3242,7 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low);
void gpiod_toggle_active_low(struct gpio_desc *desc)
{
VALIDATE_DESC_VOID(desc);
change_bit(FLAG_ACTIVE_LOW, &desc->flags);
change_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags);
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
}
EXPORT_SYMBOL_GPL(gpiod_toggle_active_low);
@ -3437,7 +3448,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(hwgpio, bits);
if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
if (!raw && test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
__assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
@ -3499,7 +3510,7 @@ int gpiod_get_value(const struct gpio_desc *desc)
if (value < 0)
return value;
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
if (test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
return value;
@ -3582,7 +3593,7 @@ static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
} else {
ret = gpiochip_direction_output(guard.gc, offset, 0);
if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
set_bit(GPIOD_FLAG_IS_OUT, &desc->flags);
}
trace_gpio_direction(desc_to_gpio(desc), value, ret);
if (ret < 0)
@ -3609,7 +3620,7 @@ static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
if (value) {
ret = gpiochip_direction_output(guard.gc, offset, 1);
if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
set_bit(GPIOD_FLAG_IS_OUT, &desc->flags);
} else {
ret = gpiochip_direction_input(guard.gc, offset);
}
@ -3624,7 +3635,7 @@ static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
{
if (unlikely(!test_bit(FLAG_IS_OUT, &desc->flags)))
if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags)))
return -EPERM;
CLASS(gpio_chip_guard, guard)(desc);
@ -3694,7 +3705,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
WARN_ON(array_info->gdev->can_sleep);
for (i = 0; i < array_size; i++) {
if (unlikely(!test_bit(FLAG_IS_OUT,
if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT,
&desc_array[i]->flags)))
return -EPERM;
}
@ -3758,7 +3769,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(i, value_bitmap);
if (unlikely(!test_bit(FLAG_IS_OUT, &desc->flags)))
if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags)))
return -EPERM;
/*
@ -3768,16 +3779,16 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
*/
if (!raw && !(array_info &&
test_bit(i, array_info->invert_mask)) &&
test_bit(FLAG_ACTIVE_LOW, &desc->flags))
test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
trace_gpio_value(desc_to_gpio(desc), 0, value);
/*
* collect all normal outputs belonging to the same chip
* open drain and open source outputs are set individually
*/
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags) && !raw) {
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags) && !raw) {
gpio_set_open_drain_value_commit(desc, value);
} else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags) && !raw) {
} else if (test_bit(GPIOD_FLAG_OPEN_SOURCE, &desc->flags) && !raw) {
gpio_set_open_source_value_commit(desc, value);
} else {
__set_bit(hwgpio, mask);
@ -3843,12 +3854,12 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
*/
static int gpiod_set_value_nocheck(struct gpio_desc *desc, int value)
{
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
if (test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags))
return gpio_set_open_drain_value_commit(desc, value);
else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
else if (test_bit(GPIOD_FLAG_OPEN_SOURCE, &desc->flags))
return gpio_set_open_source_value_commit(desc, value);
return gpiod_set_raw_value_commit(desc, value);
@ -4052,16 +4063,16 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset)
}
/* To be valid for IRQ the line needs to be input or open drain */
if (test_bit(FLAG_IS_OUT, &desc->flags) &&
!test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
if (test_bit(GPIOD_FLAG_IS_OUT, &desc->flags) &&
!test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags)) {
chip_err(gc,
"%s: tried to flag a GPIO set as output for IRQ\n",
__func__);
return -EIO;
}
set_bit(FLAG_USED_AS_IRQ, &desc->flags);
set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
set_bit(GPIOD_FLAG_USED_AS_IRQ, &desc->flags);
set_bit(GPIOD_FLAG_IRQ_IS_ENABLED, &desc->flags);
return 0;
}
@ -4083,8 +4094,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset)
if (IS_ERR(desc))
return;
clear_bit(FLAG_USED_AS_IRQ, &desc->flags);
clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
clear_bit(GPIOD_FLAG_USED_AS_IRQ, &desc->flags);
clear_bit(GPIOD_FLAG_IRQ_IS_ENABLED, &desc->flags);
}
EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
@ -4093,8 +4104,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset)
struct gpio_desc *desc = gpiochip_get_desc(gc, offset);
if (!IS_ERR(desc) &&
!WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags)))
clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
!WARN_ON(!test_bit(GPIOD_FLAG_USED_AS_IRQ, &desc->flags)))
clear_bit(GPIOD_FLAG_IRQ_IS_ENABLED, &desc->flags);
}
EXPORT_SYMBOL_GPL(gpiochip_disable_irq);
@ -4103,14 +4114,14 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset)
struct gpio_desc *desc = gpiochip_get_desc(gc, offset);
if (!IS_ERR(desc) &&
!WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) {
!WARN_ON(!test_bit(GPIOD_FLAG_USED_AS_IRQ, &desc->flags))) {
/*
* We must not be output when using IRQ UNLESS we are
* open drain.
*/
WARN_ON(test_bit(FLAG_IS_OUT, &desc->flags) &&
!test_bit(FLAG_OPEN_DRAIN, &desc->flags));
set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
WARN_ON(test_bit(GPIOD_FLAG_IS_OUT, &desc->flags) &&
!test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags));
set_bit(GPIOD_FLAG_IRQ_IS_ENABLED, &desc->flags);
}
}
EXPORT_SYMBOL_GPL(gpiochip_enable_irq);
@ -4120,7 +4131,7 @@ bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset)
if (offset >= gc->ngpio)
return false;
return test_bit(FLAG_USED_AS_IRQ, &gc->gpiodev->descs[offset].flags);
return test_bit(GPIOD_FLAG_USED_AS_IRQ, &gc->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_irq);
@ -4153,7 +4164,7 @@ bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset)
if (offset >= gc->ngpio)
return false;
return test_bit(FLAG_OPEN_DRAIN, &gc->gpiodev->descs[offset].flags);
return test_bit(GPIOD_FLAG_OPEN_DRAIN, &gc->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain);
@ -4162,7 +4173,7 @@ bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset)
if (offset >= gc->ngpio)
return false;
return test_bit(FLAG_OPEN_SOURCE, &gc->gpiodev->descs[offset].flags);
return test_bit(GPIOD_FLAG_OPEN_SOURCE, &gc->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source);
@ -4171,7 +4182,7 @@ bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset)
if (offset >= gc->ngpio)
return false;
return !test_bit(FLAG_TRANSITORY, &gc->gpiodev->descs[offset].flags);
return !test_bit(GPIOD_FLAG_TRANSITORY, &gc->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent);
@ -4213,7 +4224,7 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc)
if (value < 0)
return value;
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
if (test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
return value;
@ -4812,10 +4823,10 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
int ret;
if (lflags & GPIO_ACTIVE_LOW)
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
set_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags);
if (lflags & GPIO_OPEN_DRAIN)
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
set_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags);
else if (dflags & GPIOD_FLAGS_BIT_OPEN_DRAIN) {
/*
* This enforces open drain mode from the consumer side.
@ -4823,13 +4834,13 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
* should *REALLY* have specified them as open drain in the
* first place, so print a little warning here.
*/
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
set_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags);
gpiod_warn(desc,
"enforced open drain please flag it properly in DT/ACPI DSDT/board file\n");
}
if (lflags & GPIO_OPEN_SOURCE)
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
set_bit(GPIOD_FLAG_OPEN_SOURCE, &desc->flags);
if (((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) ||
((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DISABLE)) ||
@ -4840,11 +4851,11 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
}
if (lflags & GPIO_PULL_UP)
set_bit(FLAG_PULL_UP, &desc->flags);
set_bit(GPIOD_FLAG_PULL_UP, &desc->flags);
else if (lflags & GPIO_PULL_DOWN)
set_bit(FLAG_PULL_DOWN, &desc->flags);
set_bit(GPIOD_FLAG_PULL_DOWN, &desc->flags);
else if (lflags & GPIO_PULL_DISABLE)
set_bit(FLAG_BIAS_DISABLE, &desc->flags);
set_bit(GPIOD_FLAG_BIAS_DISABLE, &desc->flags);
ret = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY));
if (ret < 0)
@ -4949,7 +4960,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
if (!guard.gc)
return -ENODEV;
if (test_and_set_bit(FLAG_IS_HOGGED, &desc->flags))
if (test_and_set_bit(GPIOD_FLAG_IS_HOGGED, &desc->flags))
return 0;
hwnum = gpio_chip_hwgpio(desc);
@ -4957,7 +4968,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name,
lflags, dflags);
if (IS_ERR(local_desc)) {
clear_bit(FLAG_IS_HOGGED, &desc->flags);
clear_bit(GPIOD_FLAG_IS_HOGGED, &desc->flags);
ret = PTR_ERR(local_desc);
pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n",
name, gdev->label, hwnum, ret);
@ -4980,7 +4991,7 @@ static void gpiochip_free_hogs(struct gpio_chip *gc)
{
struct gpio_desc *desc;
for_each_gpio_desc_with_flag(gc, desc, FLAG_IS_HOGGED)
for_each_gpio_desc_with_flag(gc, desc, GPIOD_FLAG_IS_HOGGED)
gpiochip_free_own_desc(desc);
}
@ -5095,8 +5106,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
} else {
dflags = READ_ONCE(desc->flags);
/* Exclude open drain or open source from fast output */
if (test_bit(FLAG_OPEN_DRAIN, &dflags) ||
test_bit(FLAG_OPEN_SOURCE, &dflags))
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &dflags) ||
test_bit(GPIOD_FLAG_OPEN_SOURCE, &dflags))
__clear_bit(descs->ndescs,
array_info->set_mask);
/* Identify 'fast' pins which require invertion */
@ -5254,12 +5265,12 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
for_each_gpio_desc(gc, desc) {
guard(srcu)(&desc->gdev->desc_srcu);
flags = READ_ONCE(desc->flags);
is_irq = test_bit(FLAG_USED_AS_IRQ, &flags);
if (is_irq || test_bit(FLAG_REQUESTED, &flags)) {
is_irq = test_bit(GPIOD_FLAG_USED_AS_IRQ, &flags);
if (is_irq || test_bit(GPIOD_FLAG_REQUESTED, &flags)) {
gpiod_get_direction(desc);
is_out = test_bit(FLAG_IS_OUT, &flags);
is_out = test_bit(GPIOD_FLAG_IS_OUT, &flags);
value = gpio_chip_get_value(gc, desc);
active_low = test_bit(FLAG_ACTIVE_LOW, &flags);
active_low = test_bit(GPIOD_FLAG_ACTIVE_LOW, &flags);
seq_printf(s, " gpio-%-3u (%-20.20s|%-20.20s) %s %s %s%s\n",
gpio, desc->name ?: "", gpiod_get_label(desc),
is_out ? "out" : "in ",

View File

@ -186,24 +186,24 @@ struct gpio_desc {
struct gpio_device *gdev;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2 /* protected by sysfs_lock */
#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
#define FLAG_ACTIVE_LOW 6 /* value has active low */
#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 */
#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
#define FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */
#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
#define GPIOD_FLAG_REQUESTED 0 /* GPIO is in use */
#define GPIOD_FLAG_IS_OUT 1 /* GPIO is in output mode */
#define GPIOD_FLAG_EXPORT 2 /* GPIO is exported to user-space */
#define GPIOD_FLAG_SYSFS 3 /* GPIO is exported via /sys/class/gpio */
#define GPIOD_FLAG_ACTIVE_LOW 6 /* GPIO is active-low */
#define GPIOD_FLAG_OPEN_DRAIN 7 /* GPIO is open drain type */
#define GPIOD_FLAG_OPEN_SOURCE 8 /* GPIO is open source type */
#define GPIOD_FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#define GPIOD_FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
#define GPIOD_FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define GPIOD_FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
#define GPIOD_FLAG_PULL_UP 13 /* GPIO has pull up enabled */
#define GPIOD_FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
#define GPIOD_FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
#define GPIOD_FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
#define GPIOD_FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
#define GPIOD_FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */
#define GPIOD_FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
/* Connection label */
struct gpio_desc_label __rcu *label;

View File

@ -1708,6 +1708,16 @@ config SENSORS_NCT6683
This driver can also be built as a module. If so, the module
will be called nct6683.
config SENSORS_NCT6694
tristate "Nuvoton NCT6694 Hardware Monitor support"
depends on MFD_NCT6694
help
Say Y here to support Nuvoton NCT6694 hardware monitoring
functionality.
This driver can also be built as a module. If so, the module
will be called nct6694-hwmon.
config SENSORS_NCT6775_CORE
tristate
select REGMAP

View File

@ -175,6 +175,7 @@ obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
obj-$(CONFIG_SENSORS_NCT6694) += nct6694-hwmon.o
obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o
nct6775-objs := nct6775-platform.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o

View File

@ -0,0 +1,949 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Nuvoton NCT6694 HWMON driver based on USB interface.
*
* Copyright (C) 2025 Nuvoton Technology Corp.
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/hwmon.h>
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/*
* USB command module type for NCT6694 report channel
* This defines the module type used for communication with the NCT6694
* report channel over the USB interface.
*/
#define NCT6694_RPT_MOD 0xFF
/* Report channel */
/*
* The report channel is used to report the status of the hardware monitor
* devices, such as voltage, temperature, fan speed, and PWM.
*/
#define NCT6694_VIN_IDX(x) (0x00 + (x))
#define NCT6694_TIN_IDX(x) \
({ typeof(x) (_x) = (x); \
((_x) < 10) ? (0x10 + ((_x) * 2)) : \
(0x30 + (((_x) - 10) * 2)); })
#define NCT6694_FIN_IDX(x) (0x50 + ((x) * 2))
#define NCT6694_PWM_IDX(x) (0x70 + (x))
#define NCT6694_VIN_STS(x) (0x68 + (x))
#define NCT6694_TIN_STS(x) (0x6A + (x))
#define NCT6694_FIN_STS(x) (0x6E + (x))
/*
* USB command module type for NCT6694 HWMON controller.
* This defines the module type used for communication with the NCT6694
* HWMON controller over the USB interface.
*/
#define NCT6694_HWMON_MOD 0x00
/* Command 00h - Hardware Monitor Control */
#define NCT6694_HWMON_CONTROL 0x00
#define NCT6694_HWMON_CONTROL_SEL 0x00
/* Command 02h - Alarm Control */
#define NCT6694_HWMON_ALARM 0x02
#define NCT6694_HWMON_ALARM_SEL 0x00
/*
* USB command module type for NCT6694 PWM controller.
* This defines the module type used for communication with the NCT6694
* PWM controller over the USB interface.
*/
#define NCT6694_PWM_MOD 0x01
/* PWM Command - Manual Control */
#define NCT6694_PWM_CONTROL 0x01
#define NCT6694_PWM_CONTROL_SEL 0x00
#define NCT6694_FREQ_FROM_REG(reg) ((reg) * 25000 / 255)
#define NCT6694_FREQ_TO_REG(val) \
(DIV_ROUND_CLOSEST(clamp_val((val), 100, 25000) * 255, 25000))
#define NCT6694_LSB_REG_MASK GENMASK(7, 5)
#define NCT6694_TIN_HYST_MASK GENMASK(7, 5)
enum nct6694_hwmon_temp_mode {
NCT6694_HWMON_TWOTIME_IRQ = 0,
NCT6694_HWMON_ONETIME_IRQ,
NCT6694_HWMON_REALTIME_IRQ,
NCT6694_HWMON_COMPARE_IRQ,
};
struct __packed nct6694_hwmon_control {
u8 vin_en[2];
u8 tin_en[2];
u8 fin_en[2];
u8 pwm_en[2];
u8 reserved1[40];
u8 pwm_freq[10];
u8 reserved2[6];
};
struct __packed nct6694_hwmon_alarm {
u8 smi_ctrl;
u8 reserved1[15];
struct {
u8 hl;
u8 ll;
} vin_limit[16];
struct {
u8 hyst;
s8 hl;
} tin_cfg[32];
__be16 fin_ll[10];
u8 reserved2[4];
};
struct __packed nct6694_pwm_control {
u8 mal_en[2];
u8 mal_val[10];
u8 reserved[12];
};
union __packed nct6694_hwmon_rpt {
u8 vin;
struct {
u8 msb;
u8 lsb;
} tin;
__be16 fin;
u8 pwm;
u8 status;
};
union __packed nct6694_hwmon_msg {
struct nct6694_hwmon_alarm hwmon_alarm;
struct nct6694_pwm_control pwm_ctrl;
};
struct nct6694_hwmon_data {
struct nct6694 *nct6694;
struct mutex lock;
struct nct6694_hwmon_control hwmon_en;
union nct6694_hwmon_rpt *rpt;
union nct6694_hwmon_msg *msg;
};
static inline long in_from_reg(u8 reg)
{
return reg * 16;
}
static inline u8 in_to_reg(long val)
{
return DIV_ROUND_CLOSEST(val, 16);
}
static inline long temp_from_reg(s8 reg)
{
return reg * 1000;
}
static inline s8 temp_to_reg(long val)
{
return DIV_ROUND_CLOSEST(val, 1000);
}
#define NCT6694_HWMON_IN_CONFIG (HWMON_I_INPUT | HWMON_I_ENABLE | \
HWMON_I_MAX | HWMON_I_MIN | \
HWMON_I_ALARM)
#define NCT6694_HWMON_TEMP_CONFIG (HWMON_T_INPUT | HWMON_T_ENABLE | \
HWMON_T_MAX | HWMON_T_MAX_HYST | \
HWMON_T_MAX_ALARM)
#define NCT6694_HWMON_FAN_CONFIG (HWMON_F_INPUT | HWMON_F_ENABLE | \
HWMON_F_MIN | HWMON_F_MIN_ALARM)
#define NCT6694_HWMON_PWM_CONFIG (HWMON_PWM_INPUT | HWMON_PWM_ENABLE | \
HWMON_PWM_FREQ)
static const struct hwmon_channel_info *nct6694_info[] = {
HWMON_CHANNEL_INFO(in,
NCT6694_HWMON_IN_CONFIG, /* VIN0 */
NCT6694_HWMON_IN_CONFIG, /* VIN1 */
NCT6694_HWMON_IN_CONFIG, /* VIN2 */
NCT6694_HWMON_IN_CONFIG, /* VIN3 */
NCT6694_HWMON_IN_CONFIG, /* VIN5 */
NCT6694_HWMON_IN_CONFIG, /* VIN6 */
NCT6694_HWMON_IN_CONFIG, /* VIN7 */
NCT6694_HWMON_IN_CONFIG, /* VIN14 */
NCT6694_HWMON_IN_CONFIG, /* VIN15 */
NCT6694_HWMON_IN_CONFIG, /* VIN16 */
NCT6694_HWMON_IN_CONFIG, /* VBAT */
NCT6694_HWMON_IN_CONFIG, /* VSB */
NCT6694_HWMON_IN_CONFIG, /* AVSB */
NCT6694_HWMON_IN_CONFIG, /* VCC */
NCT6694_HWMON_IN_CONFIG, /* VHIF */
NCT6694_HWMON_IN_CONFIG), /* VTT */
HWMON_CHANNEL_INFO(temp,
NCT6694_HWMON_TEMP_CONFIG, /* THR1 */
NCT6694_HWMON_TEMP_CONFIG, /* THR2 */
NCT6694_HWMON_TEMP_CONFIG, /* THR14 */
NCT6694_HWMON_TEMP_CONFIG, /* THR15 */
NCT6694_HWMON_TEMP_CONFIG, /* THR16 */
NCT6694_HWMON_TEMP_CONFIG, /* TDP0 */
NCT6694_HWMON_TEMP_CONFIG, /* TDP1 */
NCT6694_HWMON_TEMP_CONFIG, /* TDP2 */
NCT6694_HWMON_TEMP_CONFIG, /* TDP3 */
NCT6694_HWMON_TEMP_CONFIG, /* TDP4 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN0 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN1 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN2 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN3 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN4 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN5 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN6 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN7 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN8 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN9 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN10 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN11 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN12 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN13 */
NCT6694_HWMON_TEMP_CONFIG, /* DTIN14 */
NCT6694_HWMON_TEMP_CONFIG), /* DTIN15 */
HWMON_CHANNEL_INFO(fan,
NCT6694_HWMON_FAN_CONFIG, /* FIN0 */
NCT6694_HWMON_FAN_CONFIG, /* FIN1 */
NCT6694_HWMON_FAN_CONFIG, /* FIN2 */
NCT6694_HWMON_FAN_CONFIG, /* FIN3 */
NCT6694_HWMON_FAN_CONFIG, /* FIN4 */
NCT6694_HWMON_FAN_CONFIG, /* FIN5 */
NCT6694_HWMON_FAN_CONFIG, /* FIN6 */
NCT6694_HWMON_FAN_CONFIG, /* FIN7 */
NCT6694_HWMON_FAN_CONFIG, /* FIN8 */
NCT6694_HWMON_FAN_CONFIG), /* FIN9 */
HWMON_CHANNEL_INFO(pwm,
NCT6694_HWMON_PWM_CONFIG, /* PWM0 */
NCT6694_HWMON_PWM_CONFIG, /* PWM1 */
NCT6694_HWMON_PWM_CONFIG, /* PWM2 */
NCT6694_HWMON_PWM_CONFIG, /* PWM3 */
NCT6694_HWMON_PWM_CONFIG, /* PWM4 */
NCT6694_HWMON_PWM_CONFIG, /* PWM5 */
NCT6694_HWMON_PWM_CONFIG, /* PWM6 */
NCT6694_HWMON_PWM_CONFIG, /* PWM7 */
NCT6694_HWMON_PWM_CONFIG, /* PWM8 */
NCT6694_HWMON_PWM_CONFIG), /* PWM9 */
NULL
};
static int nct6694_in_read(struct device *dev, u32 attr, int channel,
long *val)
{
struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
struct nct6694_cmd_header cmd_hd;
unsigned char vin_en;
int ret;
guard(mutex)(&data->lock);
switch (attr) {
case hwmon_in_enable:
vin_en = data->hwmon_en.vin_en[(channel / 8)];
*val = !!(vin_en & BIT(channel % 8));
return 0;
case hwmon_in_input:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_RPT_MOD,
.offset = cpu_to_le16(NCT6694_VIN_IDX(channel)),
.len = cpu_to_le16(sizeof(data->rpt->vin))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->rpt->vin);
if (ret)
return ret;
*val = in_from_reg(data->rpt->vin);
return 0;
case hwmon_in_max:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
*val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].hl);
return 0;
case hwmon_in_min:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
*val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].ll);
return 0;
case hwmon_in_alarm:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_RPT_MOD,
.offset = cpu_to_le16(NCT6694_VIN_STS(channel / 8)),
.len = cpu_to_le16(sizeof(data->rpt->status))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->rpt->status);
if (ret)
return ret;
*val = !!(data->rpt->status & BIT(channel % 8));
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nct6694_temp_read(struct device *dev, u32 attr, int channel,
long *val)
{
struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
struct nct6694_cmd_header cmd_hd;
unsigned char temp_en, temp_hyst;
signed char temp_max;
int ret, temp_raw;
guard(mutex)(&data->lock);
switch (attr) {
case hwmon_temp_enable:
temp_en = data->hwmon_en.tin_en[channel / 8];
*val = !!(temp_en & BIT(channel % 8));
return 0;
case hwmon_temp_input:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_RPT_MOD,
.offset = cpu_to_le16(NCT6694_TIN_IDX(channel)),
.len = cpu_to_le16(sizeof(data->rpt->tin))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->rpt->tin);
if (ret)
return ret;
temp_raw = data->rpt->tin.msb << 3;
temp_raw |= FIELD_GET(NCT6694_LSB_REG_MASK, data->rpt->tin.lsb);
/* Real temperature(milli degrees Celsius) = temp_raw * 1000 * 0.125 */
*val = sign_extend32(temp_raw, 10) * 125;
return 0;
case hwmon_temp_max:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
*val = temp_from_reg(data->msg->hwmon_alarm.tin_cfg[channel].hl);
return 0;
case hwmon_temp_max_hyst:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl;
temp_hyst = FIELD_GET(NCT6694_TIN_HYST_MASK,
data->msg->hwmon_alarm.tin_cfg[channel].hyst);
*val = temp_from_reg(temp_max - temp_hyst);
return 0;
case hwmon_temp_max_alarm:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_RPT_MOD,
.offset = cpu_to_le16(NCT6694_TIN_STS(channel / 8)),
.len = cpu_to_le16(sizeof(data->rpt->status))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->rpt->status);
if (ret)
return ret;
*val = !!(data->rpt->status & BIT(channel % 8));
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nct6694_fan_read(struct device *dev, u32 attr, int channel,
long *val)
{
struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
struct nct6694_cmd_header cmd_hd;
unsigned char fanin_en;
int ret;
guard(mutex)(&data->lock);
switch (attr) {
case hwmon_fan_enable:
fanin_en = data->hwmon_en.fin_en[channel / 8];
*val = !!(fanin_en & BIT(channel % 8));
return 0;
case hwmon_fan_input:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_RPT_MOD,
.offset = cpu_to_le16(NCT6694_FIN_IDX(channel)),
.len = cpu_to_le16(sizeof(data->rpt->fin))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->rpt->fin);
if (ret)
return ret;
*val = be16_to_cpu(data->rpt->fin);
return 0;
case hwmon_fan_min:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
*val = be16_to_cpu(data->msg->hwmon_alarm.fin_ll[channel]);
return 0;
case hwmon_fan_min_alarm:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_RPT_MOD,
.offset = cpu_to_le16(NCT6694_FIN_STS(channel / 8)),
.len = cpu_to_le16(sizeof(data->rpt->status))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->rpt->status);
if (ret)
return ret;
*val = !!(data->rpt->status & BIT(channel % 8));
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nct6694_pwm_read(struct device *dev, u32 attr, int channel,
long *val)
{
struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
struct nct6694_cmd_header cmd_hd;
unsigned char pwm_en;
int ret;
guard(mutex)(&data->lock);
switch (attr) {
case hwmon_pwm_enable:
pwm_en = data->hwmon_en.pwm_en[channel / 8];
*val = !!(pwm_en & BIT(channel % 8));
return 0;
case hwmon_pwm_input:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_RPT_MOD,
.offset = cpu_to_le16(NCT6694_PWM_IDX(channel)),
.len = cpu_to_le16(sizeof(data->rpt->pwm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->rpt->pwm);
if (ret)
return ret;
*val = data->rpt->pwm;
return 0;
case hwmon_pwm_freq:
*val = NCT6694_FREQ_FROM_REG(data->hwmon_en.pwm_freq[channel]);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nct6694_in_write(struct device *dev, u32 attr, int channel,
long val)
{
struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
struct nct6694_cmd_header cmd_hd;
int ret;
guard(mutex)(&data->lock);
switch (attr) {
case hwmon_in_enable:
if (val == 0)
data->hwmon_en.vin_en[channel / 8] &= ~BIT(channel % 8);
else if (val == 1)
data->hwmon_en.vin_en[channel / 8] |= BIT(channel % 8);
else
return -EINVAL;
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_CONTROL,
.sel = NCT6694_HWMON_CONTROL_SEL,
.len = cpu_to_le16(sizeof(data->hwmon_en))
};
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->hwmon_en);
case hwmon_in_max:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
val = clamp_val(val, 0, 2032);
data->msg->hwmon_alarm.vin_limit[channel].hl = in_to_reg(val);
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
case hwmon_in_min:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
val = clamp_val(val, 0, 2032);
data->msg->hwmon_alarm.vin_limit[channel].ll = in_to_reg(val);
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
default:
return -EOPNOTSUPP;
}
}
static int nct6694_temp_write(struct device *dev, u32 attr, int channel,
long val)
{
struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
struct nct6694_cmd_header cmd_hd;
unsigned char temp_hyst;
signed char temp_max;
int ret;
guard(mutex)(&data->lock);
switch (attr) {
case hwmon_temp_enable:
if (val == 0)
data->hwmon_en.tin_en[channel / 8] &= ~BIT(channel % 8);
else if (val == 1)
data->hwmon_en.tin_en[channel / 8] |= BIT(channel % 8);
else
return -EINVAL;
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_CONTROL,
.sel = NCT6694_HWMON_CONTROL_SEL,
.len = cpu_to_le16(sizeof(data->hwmon_en))
};
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->hwmon_en);
case hwmon_temp_max:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
val = clamp_val(val, -127000, 127000);
data->msg->hwmon_alarm.tin_cfg[channel].hl = temp_to_reg(val);
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
case hwmon_temp_max_hyst:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
val = clamp_val(val, -127000, 127000);
temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl;
temp_hyst = temp_max - temp_to_reg(val);
temp_hyst = clamp_val(temp_hyst, 0, 7);
data->msg->hwmon_alarm.tin_cfg[channel].hyst =
(data->msg->hwmon_alarm.tin_cfg[channel].hyst & ~NCT6694_TIN_HYST_MASK) |
FIELD_PREP(NCT6694_TIN_HYST_MASK, temp_hyst);
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
default:
return -EOPNOTSUPP;
}
}
static int nct6694_fan_write(struct device *dev, u32 attr, int channel,
long val)
{
struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
struct nct6694_cmd_header cmd_hd;
int ret;
guard(mutex)(&data->lock);
switch (attr) {
case hwmon_fan_enable:
if (val == 0)
data->hwmon_en.fin_en[channel / 8] &= ~BIT(channel % 8);
else if (val == 1)
data->hwmon_en.fin_en[channel / 8] |= BIT(channel % 8);
else
return -EINVAL;
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_CONTROL,
.sel = NCT6694_HWMON_CONTROL_SEL,
.len = cpu_to_le16(sizeof(data->hwmon_en))
};
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->hwmon_en);
case hwmon_fan_min:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
val = clamp_val(val, 1, 65535);
data->msg->hwmon_alarm.fin_ll[channel] = cpu_to_be16(val);
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
default:
return -EOPNOTSUPP;
}
}
static int nct6694_pwm_write(struct device *dev, u32 attr, int channel,
long val)
{
struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
struct nct6694_cmd_header cmd_hd;
int ret;
guard(mutex)(&data->lock);
switch (attr) {
case hwmon_pwm_enable:
if (val == 0)
data->hwmon_en.pwm_en[channel / 8] &= ~BIT(channel % 8);
else if (val == 1)
data->hwmon_en.pwm_en[channel / 8] |= BIT(channel % 8);
else
return -EINVAL;
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_CONTROL,
.sel = NCT6694_HWMON_CONTROL_SEL,
.len = cpu_to_le16(sizeof(data->hwmon_en))
};
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->hwmon_en);
case hwmon_pwm_input:
if (val < 0 || val > 255)
return -EINVAL;
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_PWM_MOD,
.cmd = NCT6694_PWM_CONTROL,
.sel = NCT6694_PWM_CONTROL_SEL,
.len = cpu_to_le16(sizeof(data->msg->pwm_ctrl))
};
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->pwm_ctrl);
if (ret)
return ret;
data->msg->pwm_ctrl.mal_val[channel] = val;
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->msg->pwm_ctrl);
case hwmon_pwm_freq:
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_CONTROL,
.sel = NCT6694_HWMON_CONTROL_SEL,
.len = cpu_to_le16(sizeof(data->hwmon_en))
};
data->hwmon_en.pwm_freq[channel] = NCT6694_FREQ_TO_REG(val);
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->hwmon_en);
default:
return -EOPNOTSUPP;
}
}
static int nct6694_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_in:
/* in mV */
return nct6694_in_read(dev, attr, channel, val);
case hwmon_temp:
/* in mC */
return nct6694_temp_read(dev, attr, channel, val);
case hwmon_fan:
/* in RPM */
return nct6694_fan_read(dev, attr, channel, val);
case hwmon_pwm:
/* in value 0~255 */
return nct6694_pwm_read(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int nct6694_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
switch (type) {
case hwmon_in:
return nct6694_in_write(dev, attr, channel, val);
case hwmon_temp:
return nct6694_temp_write(dev, attr, channel, val);
case hwmon_fan:
return nct6694_fan_write(dev, attr, channel, val);
case hwmon_pwm:
return nct6694_pwm_write(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t nct6694_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_in:
switch (attr) {
case hwmon_in_enable:
case hwmon_in_max:
case hwmon_in_min:
return 0644;
case hwmon_in_alarm:
case hwmon_in_input:
return 0444;
default:
return 0;
}
case hwmon_temp:
switch (attr) {
case hwmon_temp_enable:
case hwmon_temp_max:
case hwmon_temp_max_hyst:
return 0644;
case hwmon_temp_input:
case hwmon_temp_max_alarm:
return 0444;
default:
return 0;
}
case hwmon_fan:
switch (attr) {
case hwmon_fan_enable:
case hwmon_fan_min:
return 0644;
case hwmon_fan_input:
case hwmon_fan_min_alarm:
return 0444;
default:
return 0;
}
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_enable:
case hwmon_pwm_freq:
case hwmon_pwm_input:
return 0644;
default:
return 0;
}
default:
return 0;
}
}
static const struct hwmon_ops nct6694_hwmon_ops = {
.is_visible = nct6694_is_visible,
.read = nct6694_read,
.write = nct6694_write,
};
static const struct hwmon_chip_info nct6694_chip_info = {
.ops = &nct6694_hwmon_ops,
.info = nct6694_info,
};
static int nct6694_hwmon_init(struct nct6694_hwmon_data *data)
{
struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_CONTROL,
.sel = NCT6694_HWMON_CONTROL_SEL,
.len = cpu_to_le16(sizeof(data->hwmon_en))
};
int ret;
/*
* Record each Hardware Monitor Channel enable status
* and PWM frequency register
*/
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->hwmon_en);
if (ret)
return ret;
cmd_hd = (struct nct6694_cmd_header) {
.mod = NCT6694_HWMON_MOD,
.cmd = NCT6694_HWMON_ALARM,
.sel = NCT6694_HWMON_ALARM_SEL,
.len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
};
/* Select hwmon device alarm mode */
ret = nct6694_read_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
if (ret)
return ret;
data->msg->hwmon_alarm.smi_ctrl = NCT6694_HWMON_REALTIME_IRQ;
return nct6694_write_msg(data->nct6694, &cmd_hd,
&data->msg->hwmon_alarm);
}
static int nct6694_hwmon_probe(struct platform_device *pdev)
{
struct nct6694_hwmon_data *data;
struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent);
struct device *hwmon_dev;
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->rpt = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_rpt),
GFP_KERNEL);
if (!data->rpt)
return -ENOMEM;
data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_msg),
GFP_KERNEL);
if (!data->msg)
return -ENOMEM;
data->nct6694 = nct6694;
ret = devm_mutex_init(&pdev->dev, &data->lock);
if (ret)
return ret;
ret = nct6694_hwmon_init(data);
if (ret)
return ret;
/* Register hwmon device to HWMON framework */
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
"nct6694", data,
&nct6694_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct platform_driver nct6694_hwmon_driver = {
.driver = {
.name = "nct6694-hwmon",
},
.probe = nct6694_hwmon_probe,
};
module_platform_driver(nct6694_hwmon_driver);
MODULE_DESCRIPTION("USB-HWMON driver for NCT6694");
MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:nct6694-hwmon");

View File

@ -1357,6 +1357,16 @@ config I2C_LJCA
This driver can also be built as a module. If so, the module
will be called i2c-ljca.
config I2C_NCT6694
tristate "Nuvoton NCT6694 I2C adapter support"
depends on MFD_NCT6694
help
If you say yes to this option, support will be included for Nuvoton
NCT6694, a USB to I2C interface.
This driver can also be built as a module. If so, the module will
be called i2c-nct6694.
config I2C_CP2615
tristate "Silicon Labs CP2615 USB sound card and I2C adapter"
depends on USB

View File

@ -135,6 +135,7 @@ obj-$(CONFIG_I2C_GXP) += i2c-gxp.o
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
obj-$(CONFIG_I2C_LJCA) += i2c-ljca.o
obj-$(CONFIG_I2C_NCT6694) += i2c-nct6694.o
obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PCI1XXXX) += i2c-mchp-pci1xxxx.o

View File

@ -0,0 +1,196 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Nuvoton NCT6694 I2C adapter driver based on USB interface.
*
* Copyright (C) 2025 Nuvoton Technology Corp.
*/
#include <linux/i2c.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
#include <linux/platform_device.h>
/*
* USB command module type for NCT6694 I2C controller.
* This defines the module type used for communication with the NCT6694
* I2C controller over the USB interface.
*/
#define NCT6694_I2C_MOD 0x03
/* Command 00h - I2C Deliver */
#define NCT6694_I2C_DELIVER 0x00
#define NCT6694_I2C_DELIVER_SEL 0x00
#define NCT6694_I2C_MAX_XFER_SIZE 64
#define NCT6694_I2C_MAX_DEVS 6
static unsigned char br_reg[NCT6694_I2C_MAX_DEVS] = {[0 ... (NCT6694_I2C_MAX_DEVS - 1)] = 0xFF};
module_param_array(br_reg, byte, NULL, 0644);
MODULE_PARM_DESC(br_reg,
"I2C Baudrate register per adapter: (0=25K, 1=50K, 2=100K, 3=200K, 4=400K, 5=800K, 6=1M), default=2");
enum nct6694_i2c_baudrate {
NCT6694_I2C_BR_25K = 0,
NCT6694_I2C_BR_50K,
NCT6694_I2C_BR_100K,
NCT6694_I2C_BR_200K,
NCT6694_I2C_BR_400K,
NCT6694_I2C_BR_800K,
NCT6694_I2C_BR_1M
};
struct __packed nct6694_i2c_deliver {
u8 port;
u8 br;
u8 addr;
u8 w_cnt;
u8 r_cnt;
u8 rsv[11];
u8 write_data[NCT6694_I2C_MAX_XFER_SIZE];
u8 read_data[NCT6694_I2C_MAX_XFER_SIZE];
};
struct nct6694_i2c_data {
struct device *dev;
struct nct6694 *nct6694;
struct i2c_adapter adapter;
struct nct6694_i2c_deliver deliver;
unsigned char port;
unsigned char br;
};
static int nct6694_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct nct6694_i2c_data *data = adap->algo_data;
struct nct6694_i2c_deliver *deliver = &data->deliver;
static const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_I2C_MOD,
.cmd = NCT6694_I2C_DELIVER,
.sel = NCT6694_I2C_DELIVER_SEL,
.len = cpu_to_le16(sizeof(*deliver))
};
int ret, i;
for (i = 0; i < num; i++) {
struct i2c_msg *msg_temp = &msgs[i];
memset(deliver, 0, sizeof(*deliver));
deliver->port = data->port;
deliver->br = data->br;
deliver->addr = i2c_8bit_addr_from_msg(msg_temp);
if (msg_temp->flags & I2C_M_RD) {
deliver->r_cnt = msg_temp->len;
ret = nct6694_write_msg(data->nct6694, &cmd_hd, deliver);
if (ret < 0)
return ret;
memcpy(msg_temp->buf, deliver->read_data, msg_temp->len);
} else {
deliver->w_cnt = msg_temp->len;
memcpy(deliver->write_data, msg_temp->buf, msg_temp->len);
ret = nct6694_write_msg(data->nct6694, &cmd_hd, deliver);
if (ret < 0)
return ret;
}
}
return num;
}
static u32 nct6694_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_adapter_quirks nct6694_i2c_quirks = {
.max_read_len = NCT6694_I2C_MAX_XFER_SIZE,
.max_write_len = NCT6694_I2C_MAX_XFER_SIZE,
};
static const struct i2c_algorithm nct6694_i2c_algo = {
.xfer = nct6694_i2c_xfer,
.functionality = nct6694_i2c_func,
};
static int nct6694_i2c_set_baudrate(struct nct6694_i2c_data *data)
{
if (data->port >= NCT6694_I2C_MAX_DEVS) {
dev_err(data->dev, "Invalid I2C port index %d\n", data->port);
return -EINVAL;
}
if (br_reg[data->port] > NCT6694_I2C_BR_1M) {
dev_warn(data->dev, "Invalid baudrate %d for I2C%d, using 100K\n",
br_reg[data->port], data->port);
br_reg[data->port] = NCT6694_I2C_BR_100K;
}
data->br = br_reg[data->port];
return 0;
}
static void nct6694_i2c_ida_free(void *d)
{
struct nct6694_i2c_data *data = d;
struct nct6694 *nct6694 = data->nct6694;
ida_free(&nct6694->i2c_ida, data->port);
}
static int nct6694_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct nct6694 *nct6694 = dev_get_drvdata(dev->parent);
struct nct6694_i2c_data *data;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->dev = dev;
data->nct6694 = nct6694;
ret = ida_alloc(&nct6694->i2c_ida, GFP_KERNEL);
if (ret < 0)
return ret;
data->port = ret;
ret = devm_add_action_or_reset(dev, nct6694_i2c_ida_free, data);
if (ret)
return ret;
ret = nct6694_i2c_set_baudrate(data);
if (ret)
return ret;
sprintf(data->adapter.name, "NCT6694 I2C Adapter %d", data->port);
data->adapter.owner = THIS_MODULE;
data->adapter.algo = &nct6694_i2c_algo;
data->adapter.quirks = &nct6694_i2c_quirks;
data->adapter.dev.parent = dev;
data->adapter.algo_data = data;
platform_set_drvdata(pdev, data);
return devm_i2c_add_adapter(dev, &data->adapter);
}
static struct platform_driver nct6694_i2c_driver = {
.driver = {
.name = "nct6694-i2c",
},
.probe = nct6694_i2c_probe,
};
module_platform_driver(nct6694_i2c_driver);
MODULE_DESCRIPTION("USB-I2C adapter driver for NCT6694");
MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:nct6694-i2c");

View File

@ -422,6 +422,18 @@ config KEYBOARD_MAX7359
To compile this driver as a module, choose M here: the
module will be called max7359_keypad.
config KEYBOARD_MAX7360
tristate "Maxim MAX7360 Key Switch Controller"
select INPUT_MATRIXKMAP
depends on I2C
depends on MFD_MAX7360
help
If you say yes here you get support for the keypad controller on the
Maxim MAX7360 I/O Expander.
To compile this driver as a module, choose M here: the module will be
called max7360_keypad.
config KEYBOARD_MPR121
tristate "Freescale MPR121 Touchkey"
depends on I2C

View File

@ -42,6 +42,7 @@ obj-$(CONFIG_KEYBOARD_LPC32XX) += lpc32xx-keys.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7360) += max7360-keypad.o
obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
obj-$(CONFIG_KEYBOARD_MT6779) += mt6779-keypad.o
obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o

View File

@ -0,0 +1,308 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2025 Bootlin
*
* Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/dev_printk.h>
#include <linux/device/devres.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/interrupt.h>
#include <linux/mfd/max7360.h>
#include <linux/mod_devicetable.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
struct max7360_keypad {
struct input_dev *input;
unsigned int rows;
unsigned int cols;
unsigned int debounce_ms;
int irq;
struct regmap *regmap;
unsigned short keycodes[MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS];
};
static irqreturn_t max7360_keypad_irq(int irq, void *data)
{
struct max7360_keypad *max7360_keypad = data;
struct device *dev = max7360_keypad->input->dev.parent;
unsigned int val;
unsigned int row, col;
unsigned int release;
unsigned int code;
int error;
error = regmap_read(max7360_keypad->regmap, MAX7360_REG_KEYFIFO, &val);
if (error) {
dev_err(dev, "Failed to read MAX7360 FIFO");
return IRQ_NONE;
}
/* FIFO overflow: ignore it and get next event. */
if (val == MAX7360_FIFO_OVERFLOW) {
dev_warn(dev, "max7360 FIFO overflow");
error = regmap_read_poll_timeout(max7360_keypad->regmap, MAX7360_REG_KEYFIFO,
val, val != MAX7360_FIFO_OVERFLOW, 0, 1000);
if (error) {
dev_err(dev, "Failed to empty MAX7360 FIFO");
return IRQ_NONE;
}
}
if (val == MAX7360_FIFO_EMPTY) {
dev_dbg(dev, "Got a spurious interrupt");
return IRQ_NONE;
}
row = FIELD_GET(MAX7360_FIFO_ROW, val);
col = FIELD_GET(MAX7360_FIFO_COL, val);
release = val & MAX7360_FIFO_RELEASE;
code = MATRIX_SCAN_CODE(row, col, get_count_order(max7360_keypad->cols));
dev_dbg(dev, "key[%d:%d] %s\n", row, col, release ? "release" : "press");
input_event(max7360_keypad->input, EV_MSC, MSC_SCAN, code);
input_report_key(max7360_keypad->input, max7360_keypad->keycodes[code], !release);
input_sync(max7360_keypad->input);
return IRQ_HANDLED;
}
static int max7360_keypad_open(struct input_dev *pdev)
{
struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev);
struct device *dev = max7360_keypad->input->dev.parent;
int error;
/* Somebody is using the device: get out of sleep. */
error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG,
MAX7360_CFG_SLEEP, MAX7360_CFG_SLEEP);
if (error)
dev_err(dev, "Failed to write max7360 configuration: %d\n", error);
return error;
}
static void max7360_keypad_close(struct input_dev *pdev)
{
struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev);
struct device *dev = max7360_keypad->input->dev.parent;
int error;
/* Nobody is using the device anymore: go to sleep. */
error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG, MAX7360_CFG_SLEEP, 0);
if (error)
dev_err(dev, "Failed to write max7360 configuration: %d\n", error);
}
static int max7360_keypad_hw_init(struct max7360_keypad *max7360_keypad)
{
struct device *dev = max7360_keypad->input->dev.parent;
unsigned int val;
int error;
val = max7360_keypad->debounce_ms - MAX7360_DEBOUNCE_MIN;
error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_DEBOUNCE,
MAX7360_DEBOUNCE,
FIELD_PREP(MAX7360_DEBOUNCE, val));
if (error)
return dev_err_probe(dev, error,
"Failed to write max7360 debounce configuration\n");
error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_INTERRUPT,
MAX7360_INTERRUPT_TIME_MASK,
FIELD_PREP(MAX7360_INTERRUPT_TIME_MASK, 1));
if (error)
return dev_err_probe(dev, error,
"Failed to write max7360 keypad interrupt configuration\n");
return 0;
}
static int max7360_keypad_build_keymap(struct max7360_keypad *max7360_keypad)
{
struct input_dev *input_dev = max7360_keypad->input;
struct device *dev = input_dev->dev.parent->parent;
struct matrix_keymap_data keymap_data;
const char *propname = "linux,keymap";
unsigned int max_keys;
int error;
int size;
size = device_property_count_u32(dev, propname);
if (size <= 0) {
dev_err(dev, "missing or malformed property %s: %d\n", propname, size);
return size < 0 ? size : -EINVAL;
}
max_keys = max7360_keypad->cols * max7360_keypad->rows;
if (size > max_keys) {
dev_err(dev, "%s size overflow (%d vs max %u)\n", propname, size, max_keys);
return -EINVAL;
}
u32 *keys __free(kfree) = kmalloc_array(size, sizeof(*keys), GFP_KERNEL);
if (!keys)
return -ENOMEM;
error = device_property_read_u32_array(dev, propname, keys, size);
if (error) {
dev_err(dev, "failed to read %s property: %d\n", propname, error);
return error;
}
keymap_data.keymap = keys;
keymap_data.keymap_size = size;
error = matrix_keypad_build_keymap(&keymap_data, NULL,
max7360_keypad->rows, max7360_keypad->cols,
max7360_keypad->keycodes, max7360_keypad->input);
if (error)
return error;
return 0;
}
static int max7360_keypad_parse_fw(struct device *dev,
struct max7360_keypad *max7360_keypad,
bool *autorepeat)
{
int error;
error = matrix_keypad_parse_properties(dev->parent, &max7360_keypad->rows,
&max7360_keypad->cols);
if (error)
return error;
if (!max7360_keypad->rows || !max7360_keypad->cols ||
max7360_keypad->rows > MAX7360_MAX_KEY_ROWS ||
max7360_keypad->cols > MAX7360_MAX_KEY_COLS) {
dev_err(dev, "Invalid number of columns or rows (%ux%u)\n",
max7360_keypad->cols, max7360_keypad->rows);
return -EINVAL;
}
*autorepeat = device_property_read_bool(dev->parent, "autorepeat");
max7360_keypad->debounce_ms = MAX7360_DEBOUNCE_MIN;
error = device_property_read_u32(dev->parent, "keypad-debounce-delay-ms",
&max7360_keypad->debounce_ms);
if (error == -EINVAL) {
dev_info(dev, "Using default keypad-debounce-delay-ms: %u\n",
max7360_keypad->debounce_ms);
} else if (error < 0) {
dev_err(dev, "Failed to read keypad-debounce-delay-ms property\n");
return error;
}
if (!in_range(max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN,
MAX7360_DEBOUNCE_MAX - MAX7360_DEBOUNCE_MIN + 1)) {
dev_err(dev, "Invalid keypad-debounce-delay-ms: %u, should be between %u and %u.\n",
max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN, MAX7360_DEBOUNCE_MAX);
return -EINVAL;
}
return 0;
}
static int max7360_keypad_probe(struct platform_device *pdev)
{
struct max7360_keypad *max7360_keypad;
struct device *dev = &pdev->dev;
struct input_dev *input;
struct regmap *regmap;
bool autorepeat;
int error;
int irq;
regmap = dev_get_regmap(dev->parent, NULL);
if (!regmap)
return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n");
irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "intk");
if (irq < 0)
return dev_err_probe(dev, irq, "Failed to get IRQ\n");
max7360_keypad = devm_kzalloc(dev, sizeof(*max7360_keypad), GFP_KERNEL);
if (!max7360_keypad)
return -ENOMEM;
max7360_keypad->regmap = regmap;
error = max7360_keypad_parse_fw(dev, max7360_keypad, &autorepeat);
if (error)
return error;
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
max7360_keypad->input = input;
input->id.bustype = BUS_I2C;
input->name = pdev->name;
input->open = max7360_keypad_open;
input->close = max7360_keypad_close;
error = max7360_keypad_build_keymap(max7360_keypad);
if (error)
return dev_err_probe(dev, error, "Failed to build keymap\n");
input_set_capability(input, EV_MSC, MSC_SCAN);
if (autorepeat)
__set_bit(EV_REP, input->evbit);
input_set_drvdata(input, max7360_keypad);
error = devm_request_threaded_irq(dev, irq, NULL, max7360_keypad_irq,
IRQF_ONESHOT,
"max7360-keypad", max7360_keypad);
if (error)
return dev_err_probe(dev, error, "Failed to register interrupt\n");
error = input_register_device(input);
if (error)
return dev_err_probe(dev, error, "Could not register input device\n");
error = max7360_keypad_hw_init(max7360_keypad);
if (error)
return dev_err_probe(dev, error, "Failed to initialize max7360 keypad\n");
device_init_wakeup(dev, true);
error = dev_pm_set_wake_irq(dev, irq);
if (error)
dev_warn(dev, "Failed to set up wakeup irq: %d\n", error);
return 0;
}
static void max7360_keypad_remove(struct platform_device *pdev)
{
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
}
static struct platform_driver max7360_keypad_driver = {
.driver = {
.name = "max7360-keypad",
},
.probe = max7360_keypad_probe,
.remove = max7360_keypad_remove,
};
module_platform_driver(max7360_keypad_driver);
MODULE_DESCRIPTION("MAX7360 Keypad driver");
MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");
MODULE_LICENSE("GPL");

View File

@ -230,6 +230,16 @@ config INPUT_M68K_BEEP
tristate "M68k Beeper support"
depends on M68K
config INPUT_MAX7360_ROTARY
tristate "Maxim MAX7360 Rotary Encoder"
depends on MFD_MAX7360
help
If you say yes here you get support for the rotary encoder on the
Maxim MAX7360 I/O Expander.
To compile this driver as a module, choose M here: the module will be
called max7360_rotary.
config INPUT_MAX77650_ONKEY
tristate "Maxim MAX77650 ONKEY support"
depends on MFD_MAX77650

View File

@ -51,6 +51,7 @@ obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_MAX7360_ROTARY) += max7360-rotary.o
obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o
obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o

View File

@ -0,0 +1,192 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2025 Bootlin
*
* Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
*/
#include <linux/bitfield.h>
#include <linux/device/devres.h>
#include <linux/dev_printk.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/mfd/max7360.h>
#include <linux/property.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/types.h>
#define MAX7360_ROTARY_DEFAULT_STEPS 24
struct max7360_rotary {
struct input_dev *input;
struct regmap *regmap;
unsigned int debounce_ms;
unsigned int pos;
u32 steps;
u32 axis;
bool relative_axis;
bool rollover;
};
static void max7360_rotary_report_event(struct max7360_rotary *max7360_rotary, int steps)
{
if (max7360_rotary->relative_axis) {
input_report_rel(max7360_rotary->input, max7360_rotary->axis, steps);
} else {
int pos = max7360_rotary->pos;
int maxval = max7360_rotary->steps;
/*
* Add steps to the position.
* Make sure added steps are always in ]-maxval; maxval[
* interval, so (pos + maxval) is always >= 0.
* Then set back pos to the [0; maxval[ interval.
*/
pos += steps % maxval;
if (max7360_rotary->rollover)
pos = (pos + maxval) % maxval;
else
pos = clamp(pos, 0, maxval - 1);
max7360_rotary->pos = pos;
input_report_abs(max7360_rotary->input, max7360_rotary->axis, max7360_rotary->pos);
}
input_sync(max7360_rotary->input);
}
static irqreturn_t max7360_rotary_irq(int irq, void *data)
{
struct max7360_rotary *max7360_rotary = data;
struct device *dev = max7360_rotary->input->dev.parent;
unsigned int val;
int error;
error = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val);
if (error < 0) {
dev_err(dev, "Failed to read rotary counter\n");
return IRQ_NONE;
}
if (val == 0)
return IRQ_NONE;
max7360_rotary_report_event(max7360_rotary, sign_extend32(val, 7));
return IRQ_HANDLED;
}
static int max7360_rotary_hw_init(struct max7360_rotary *max7360_rotary)
{
struct device *dev = max7360_rotary->input->dev.parent;
int val;
int error;
val = FIELD_PREP(MAX7360_ROT_DEBOUNCE, max7360_rotary->debounce_ms) |
FIELD_PREP(MAX7360_ROT_INTCNT, 1) | MAX7360_ROT_INTCNT_DLY;
error = regmap_write(max7360_rotary->regmap, MAX7360_REG_RTRCFG, val);
if (error)
dev_err(dev, "Failed to set max7360 rotary encoder configuration\n");
return error;
}
static int max7360_rotary_probe(struct platform_device *pdev)
{
struct max7360_rotary *max7360_rotary;
struct device *dev = &pdev->dev;
struct input_dev *input;
struct regmap *regmap;
int irq;
int error;
regmap = dev_get_regmap(dev->parent, NULL);
if (!regmap)
return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n");
irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti");
if (irq < 0)
return dev_err_probe(dev, irq, "Failed to get IRQ\n");
max7360_rotary = devm_kzalloc(dev, sizeof(*max7360_rotary), GFP_KERNEL);
if (!max7360_rotary)
return -ENOMEM;
max7360_rotary->regmap = regmap;
device_property_read_u32(dev->parent, "linux,axis", &max7360_rotary->axis);
max7360_rotary->rollover = device_property_read_bool(dev->parent,
"rotary-encoder,rollover");
max7360_rotary->relative_axis =
device_property_read_bool(dev->parent, "rotary-encoder,relative-axis");
error = device_property_read_u32(dev->parent, "rotary-encoder,steps",
&max7360_rotary->steps);
if (error)
max7360_rotary->steps = MAX7360_ROTARY_DEFAULT_STEPS;
device_property_read_u32(dev->parent, "rotary-debounce-delay-ms",
&max7360_rotary->debounce_ms);
if (max7360_rotary->debounce_ms > MAX7360_ROT_DEBOUNCE_MAX)
return dev_err_probe(dev, -EINVAL, "Invalid debounce timing: %u\n",
max7360_rotary->debounce_ms);
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
max7360_rotary->input = input;
input->id.bustype = BUS_I2C;
input->name = pdev->name;
if (max7360_rotary->relative_axis)
input_set_capability(input, EV_REL, max7360_rotary->axis);
else
input_set_abs_params(input, max7360_rotary->axis, 0, max7360_rotary->steps, 0, 1);
error = devm_request_threaded_irq(dev, irq, NULL, max7360_rotary_irq,
IRQF_ONESHOT | IRQF_SHARED,
"max7360-rotary", max7360_rotary);
if (error)
return dev_err_probe(dev, error, "Failed to register interrupt\n");
error = input_register_device(input);
if (error)
return dev_err_probe(dev, error, "Could not register input device\n");
error = max7360_rotary_hw_init(max7360_rotary);
if (error)
return dev_err_probe(dev, error, "Failed to initialize max7360 rotary\n");
device_init_wakeup(dev, true);
error = dev_pm_set_wake_irq(dev, irq);
if (error)
dev_warn(dev, "Failed to set up wakeup irq: %d\n", error);
return 0;
}
static void max7360_rotary_remove(struct platform_device *pdev)
{
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
}
static struct platform_driver max7360_rotary_driver = {
.driver = {
.name = "max7360-rotary",
},
.probe = max7360_rotary_probe,
.remove = max7360_rotary_remove,
};
module_platform_driver(max7360_rotary_driver);
MODULE_DESCRIPTION("MAX7360 Rotary driver");
MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");
MODULE_LICENSE("GPL");

View File

@ -1134,6 +1134,21 @@ config MFD_MENF21BMC
This driver can also be built as a module. If so the module
will be called menf21bmc.
config MFD_NCT6694
tristate "Nuvoton NCT6694 support"
select MFD_CORE
depends on USB
help
This enables support for the Nuvoton USB device NCT6694, which shares
peripherals.
The Nuvoton NCT6694 is a peripheral expander with 16 GPIO chips,
6 I2C controllers, 2 CANfd controllers, 2 Watchdog timers, ADC,
PWM, and RTC.
This driver provides core APIs to access the NCT6694 hardware
monitoring and control features.
Additional drivers must be enabled to utilize the specific
functionalities of the device.
config MFD_OCELOT
tristate "Microsemi Ocelot External Control Support"
depends on SPI_MASTER
@ -1539,8 +1554,8 @@ config MFD_DB8500_PRCMU
through a register map.
config MFD_STMPE
bool "STMicroelectronics STMPE"
depends on I2C=y || SPI_MASTER=y
tristate "STMicroelectronics STMPE"
depends on I2C || SPI_MASTER
depends on OF
select MFD_CORE
help
@ -1568,14 +1583,14 @@ menu "STMicroelectronics STMPE Interface Drivers"
depends on MFD_STMPE
config STMPE_I2C
bool "STMicroelectronics STMPE I2C Interface"
depends on I2C=y
tristate "STMicroelectronics STMPE I2C Interface"
depends on I2C
default y
help
This is used to enable I2C interface of STMPE
config STMPE_SPI
bool "STMicroelectronics STMPE SPI Interface"
tristate "STMicroelectronics STMPE SPI Interface"
depends on SPI_MASTER
help
This is used to enable SPI interface of STMPE
@ -2481,5 +2496,19 @@ config MFD_UPBOARD_FPGA
To compile this driver as a module, choose M here: the module will be
called upboard-fpga.
config MFD_MAX7360
tristate "Maxim MAX7360 I2C IO Expander"
depends on I2C
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
Say yes here to add support for Maxim MAX7360 device, embedding
keypad, rotary encoder, PWM and GPIO features.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
of the device.
endmenu
endif

View File

@ -121,6 +121,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
obj-$(CONFIG_MFD_NCT6694) += nct6694.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
ocelot-soc-objs := ocelot-core.o ocelot-spi.o
@ -163,6 +165,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o
obj-$(CONFIG_MFD_DA9150) += da9150-core.o
obj-$(CONFIG_MFD_MAX14577) += max14577.o
obj-$(CONFIG_MFD_MAX7360) += max7360.o
obj-$(CONFIG_MFD_MAX77541) += max77541.o
obj-$(CONFIG_MFD_MAX77620) += max77620.o
obj-$(CONFIG_MFD_MAX77650) += max77650.o

171
drivers/mfd/max7360.c Normal file
View File

@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Maxim MAX7360 Core Driver
*
* Copyright 2025 Bootlin
*
* Authors:
* Kamel Bouhara <kamel.bouhara@bootlin.com>
* Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
*/
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device/devres.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max7360.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/types.h>
static const struct mfd_cell max7360_cells[] = {
{ .name = "max7360-pinctrl" },
{ .name = "max7360-pwm" },
{ .name = "max7360-keypad" },
{ .name = "max7360-rotary" },
{
.name = "max7360-gpo",
.of_compatible = "maxim,max7360-gpo",
},
{
.name = "max7360-gpio",
.of_compatible = "maxim,max7360-gpio",
},
};
static const struct regmap_range max7360_volatile_ranges[] = {
regmap_reg_range(MAX7360_REG_KEYFIFO, MAX7360_REG_KEYFIFO),
regmap_reg_range(MAX7360_REG_I2C_TIMEOUT, MAX7360_REG_RTR_CNT),
};
static const struct regmap_access_table max7360_volatile_table = {
.yes_ranges = max7360_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(max7360_volatile_ranges),
};
static const struct regmap_config max7360_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX7360_REG_PWMCFG(MAX7360_PORT_PWM_COUNT - 1),
.volatile_table = &max7360_volatile_table,
.cache_type = REGCACHE_MAPLE,
};
static int max7360_mask_irqs(struct regmap *regmap)
{
struct device *dev = regmap_get_device(regmap);
unsigned int val;
int ret;
/*
* GPIO/PWM interrupts are not masked on reset: as the MAX7360 "INTI"
* interrupt line is shared between GPIOs and rotary encoder, this could
* result in repeated spurious interrupts on the rotary encoder driver
* if the GPIO driver is not loaded. Mask them now to avoid this
* situation.
*/
for (unsigned int i = 0; i < MAX7360_PORT_PWM_COUNT; i++) {
ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),
MAX7360_PORT_CFG_INTERRUPT_MASK,
MAX7360_PORT_CFG_INTERRUPT_MASK);
if (ret)
return dev_err_probe(dev, ret,
"Failed to write MAX7360 port configuration\n");
}
/* Read GPIO in register, to ACK any pending IRQ. */
ret = regmap_read(regmap, MAX7360_REG_GPIOIN, &val);
if (ret)
return dev_err_probe(dev, ret, "Failed to read GPIO values\n");
return 0;
}
static int max7360_reset(struct regmap *regmap)
{
struct device *dev = regmap_get_device(regmap);
int ret;
ret = regmap_write(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_GPIO_RST);
if (ret) {
dev_err(dev, "Failed to reset GPIO configuration: %x\n", ret);
return ret;
}
ret = regcache_drop_region(regmap, MAX7360_REG_GPIOCFG, MAX7360_REG_GPIO_LAST);
if (ret) {
dev_err(dev, "Failed to drop regmap cache: %x\n", ret);
return ret;
}
ret = regmap_write(regmap, MAX7360_REG_SLEEP, 0);
if (ret) {
dev_err(dev, "Failed to reset autosleep configuration: %x\n", ret);
return ret;
}
ret = regmap_write(regmap, MAX7360_REG_DEBOUNCE, 0);
if (ret)
dev_err(dev, "Failed to reset GPO port count: %x\n", ret);
return ret;
}
static int max7360_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct regmap *regmap;
int ret;
regmap = devm_regmap_init_i2c(client, &max7360_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialise regmap\n");
ret = max7360_reset(regmap);
if (ret)
return dev_err_probe(dev, ret, "Failed to reset device\n");
/* Get the device out of shutdown mode. */
ret = regmap_write_bits(regmap, MAX7360_REG_GPIOCFG,
MAX7360_GPIO_CFG_GPIO_EN,
MAX7360_GPIO_CFG_GPIO_EN);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable GPIO and PWM module\n");
ret = max7360_mask_irqs(regmap);
if (ret)
return dev_err_probe(dev, ret, "Could not mask interrupts\n");
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
max7360_cells, ARRAY_SIZE(max7360_cells),
NULL, 0, NULL);
if (ret)
return dev_err_probe(dev, ret, "Failed to register child devices\n");
return 0;
}
static const struct of_device_id max7360_dt_match[] = {
{ .compatible = "maxim,max7360" },
{}
};
MODULE_DEVICE_TABLE(of, max7360_dt_match);
static struct i2c_driver max7360_driver = {
.driver = {
.name = "max7360",
.of_match_table = max7360_dt_match,
},
.probe = max7360_probe,
};
module_i2c_driver(max7360_driver);
MODULE_DESCRIPTION("Maxim MAX7360 I2C IO Expander core driver");
MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
MODULE_LICENSE("GPL");

388
drivers/mfd/nct6694.c Normal file
View File

@ -0,0 +1,388 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 Nuvoton Technology Corp.
*
* Nuvoton NCT6694 core driver using USB interface to provide
* access to the NCT6694 hardware monitoring and control features.
*
* The NCT6694 is an integrated controller that provides GPIO, I2C,
* CAN, WDT, HWMON and RTC management.
*/
#include <linux/bits.h>
#include <linux/interrupt.h>
#include <linux/idr.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
static const struct mfd_cell nct6694_devs[] = {
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-i2c"),
MFD_CELL_NAME("nct6694-i2c"),
MFD_CELL_NAME("nct6694-i2c"),
MFD_CELL_NAME("nct6694-i2c"),
MFD_CELL_NAME("nct6694-i2c"),
MFD_CELL_NAME("nct6694-i2c"),
MFD_CELL_NAME("nct6694-canfd"),
MFD_CELL_NAME("nct6694-canfd"),
MFD_CELL_NAME("nct6694-wdt"),
MFD_CELL_NAME("nct6694-wdt"),
MFD_CELL_NAME("nct6694-hwmon"),
MFD_CELL_NAME("nct6694-rtc"),
};
static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)
{
switch (err_status) {
case NCT6694_NO_ERROR:
return 0;
case NCT6694_NOT_SUPPORT_ERROR:
dev_err(nct6694->dev, "Command is not supported!\n");
break;
case NCT6694_NO_RESPONSE_ERROR:
dev_warn(nct6694->dev, "Command received no response!\n");
break;
case NCT6694_TIMEOUT_ERROR:
dev_warn(nct6694->dev, "Command timed out!\n");
break;
case NCT6694_PENDING:
dev_err(nct6694->dev, "Command is pending!\n");
break;
default:
return -EINVAL;
}
return -EIO;
}
/**
* nct6694_read_msg() - Read message from NCT6694 device
* @nct6694: NCT6694 device pointer
* @cmd_hd: command header structure
* @buf: buffer to store the response data
*
* Sends a command to the NCT6694 device and reads the response.
* The command header is specified in @cmd_hd, and the response
* data is stored in @buf.
*
* Return: Negative value on error or 0 on success.
*/
int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
{
union nct6694_usb_msg *msg = nct6694->usb_msg;
struct usb_device *udev = nct6694->udev;
int tx_len, rx_len, ret;
guard(mutex)(&nct6694->access_lock);
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
msg->cmd_header.hctrl = NCT6694_HCTRL_GET;
/* Send command packet to USB device */
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
/* Receive response packet from USB device */
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,
sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
/* Receive data packet from USB device */
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
if (rx_len != le16_to_cpu(cmd_hd->len)) {
dev_err(nct6694->dev, "Expected received length %d, but got %d\n",
le16_to_cpu(cmd_hd->len), rx_len);
return -EIO;
}
return nct6694_response_err_handling(nct6694, msg->response_header.sts);
}
EXPORT_SYMBOL_GPL(nct6694_read_msg);
/**
* nct6694_write_msg() - Write message to NCT6694 device
* @nct6694: NCT6694 device pointer
* @cmd_hd: command header structure
* @buf: buffer containing the data to be sent
*
* Sends a command to the NCT6694 device and writes the data
* from @buf. The command header is specified in @cmd_hd.
*
* Return: Negative value on error or 0 on success.
*/
int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
{
union nct6694_usb_msg *msg = nct6694->usb_msg;
struct usb_device *udev = nct6694->udev;
int tx_len, rx_len, ret;
guard(mutex)(&nct6694->access_lock);
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
msg->cmd_header.hctrl = NCT6694_HCTRL_SET;
/* Send command packet to USB device */
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
/* Send data packet to USB device */
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), buf,
le16_to_cpu(cmd_hd->len), &tx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
/* Receive response packet from USB device */
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,
sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
/* Receive data packet from USB device */
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
if (rx_len != le16_to_cpu(cmd_hd->len)) {
dev_err(nct6694->dev, "Expected transmitted length %d, but got %d\n",
le16_to_cpu(cmd_hd->len), rx_len);
return -EIO;
}
return nct6694_response_err_handling(nct6694, msg->response_header.sts);
}
EXPORT_SYMBOL_GPL(nct6694_write_msg);
static void usb_int_callback(struct urb *urb)
{
struct nct6694 *nct6694 = urb->context;
__le32 *status_le = urb->transfer_buffer;
u32 int_status;
int ret;
switch (urb->status) {
case 0:
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
return;
default:
goto resubmit;
}
int_status = le32_to_cpu(*status_le);
while (int_status) {
int irq = __ffs(int_status);
generic_handle_irq_safe(irq_find_mapping(nct6694->domain, irq));
int_status &= ~BIT(irq);
}
resubmit:
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret)
dev_warn(nct6694->dev, "Failed to resubmit urb, status %pe", ERR_PTR(ret));
}
static void nct6694_irq_enable(struct irq_data *data)
{
struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
irq_hw_number_t hwirq = irqd_to_hwirq(data);
guard(spinlock_irqsave)(&nct6694->irq_lock);
nct6694->irq_enable |= BIT(hwirq);
}
static void nct6694_irq_disable(struct irq_data *data)
{
struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
irq_hw_number_t hwirq = irqd_to_hwirq(data);
guard(spinlock_irqsave)(&nct6694->irq_lock);
nct6694->irq_enable &= ~BIT(hwirq);
}
static const struct irq_chip nct6694_irq_chip = {
.name = "nct6694-irq",
.flags = IRQCHIP_SKIP_SET_WAKE,
.irq_enable = nct6694_irq_enable,
.irq_disable = nct6694_irq_disable,
};
static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
struct nct6694 *nct6694 = d->host_data;
irq_set_chip_data(irq, nct6694);
irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);
return 0;
}
static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
{
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
}
static const struct irq_domain_ops nct6694_irq_domain_ops = {
.map = nct6694_irq_domain_map,
.unmap = nct6694_irq_domain_unmap,
};
static int nct6694_usb_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(iface);
struct usb_endpoint_descriptor *int_endpoint;
struct usb_host_interface *interface;
struct device *dev = &iface->dev;
struct nct6694 *nct6694;
int ret;
nct6694 = devm_kzalloc(dev, sizeof(*nct6694), GFP_KERNEL);
if (!nct6694)
return -ENOMEM;
nct6694->usb_msg = devm_kzalloc(dev, sizeof(union nct6694_usb_msg), GFP_KERNEL);
if (!nct6694->usb_msg)
return -ENOMEM;
nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL);
if (!nct6694->int_buffer)
return -ENOMEM;
nct6694->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!nct6694->int_in_urb)
return -ENOMEM;
nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
&nct6694_irq_domain_ops,
nct6694);
if (!nct6694->domain) {
ret = -ENODEV;
goto err_urb;
}
nct6694->dev = dev;
nct6694->udev = udev;
ida_init(&nct6694->gpio_ida);
ida_init(&nct6694->i2c_ida);
ida_init(&nct6694->canfd_ida);
ida_init(&nct6694->wdt_ida);
spin_lock_init(&nct6694->irq_lock);
ret = devm_mutex_init(dev, &nct6694->access_lock);
if (ret)
goto err_ida;
interface = iface->cur_altsetting;
int_endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(int_endpoint)) {
ret = -ENODEV;
goto err_ida;
}
usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
nct6694->int_buffer, sizeof(*nct6694->int_buffer), usb_int_callback,
nct6694, int_endpoint->bInterval);
ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);
if (ret)
goto err_ida;
usb_set_intfdata(iface, nct6694);
ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs));
if (ret)
goto err_mfd;
return 0;
err_mfd:
usb_kill_urb(nct6694->int_in_urb);
err_ida:
ida_destroy(&nct6694->wdt_ida);
ida_destroy(&nct6694->canfd_ida);
ida_destroy(&nct6694->i2c_ida);
ida_destroy(&nct6694->gpio_ida);
irq_domain_remove(nct6694->domain);
err_urb:
usb_free_urb(nct6694->int_in_urb);
return ret;
}
static void nct6694_usb_disconnect(struct usb_interface *iface)
{
struct nct6694 *nct6694 = usb_get_intfdata(iface);
mfd_remove_devices(nct6694->dev);
usb_kill_urb(nct6694->int_in_urb);
ida_destroy(&nct6694->wdt_ida);
ida_destroy(&nct6694->canfd_ida);
ida_destroy(&nct6694->i2c_ida);
ida_destroy(&nct6694->gpio_ida);
irq_domain_remove(nct6694->domain);
usb_free_urb(nct6694->int_in_urb);
}
static const struct usb_device_id nct6694_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(NCT6694_VENDOR_ID, NCT6694_PRODUCT_ID, 0xFF, 0x00, 0x00) },
{ }
};
MODULE_DEVICE_TABLE(usb, nct6694_ids);
static struct usb_driver nct6694_usb_driver = {
.name = "nct6694",
.id_table = nct6694_ids,
.probe = nct6694_usb_probe,
.disconnect = nct6694_usb_disconnect,
};
module_usb_driver(nct6694_usb_driver);
MODULE_DESCRIPTION("Nuvoton NCT6694 core driver");
MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
MODULE_LICENSE("GPL");

View File

@ -137,3 +137,4 @@ module_exit(stmpe_exit);
MODULE_DESCRIPTION("STMPE MFD I2C Interface Driver");
MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
MODULE_LICENSE("GPL v2");

View File

@ -156,3 +156,4 @@ module_exit(stmpe_exit);
MODULE_DESCRIPTION("STMPE MFD SPI Interface Driver");
MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>");
MODULE_LICENSE("GPL v2");

View File

@ -1482,6 +1482,7 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum)
return ret;
}
EXPORT_SYMBOL_GPL(stmpe_probe);
void stmpe_remove(struct stmpe *stmpe)
{
@ -1494,6 +1495,7 @@ void stmpe_remove(struct stmpe *stmpe)
mfd_remove_devices(stmpe->dev);
}
EXPORT_SYMBOL_GPL(stmpe_remove);
static int stmpe_suspend(struct device *dev)
{
@ -1517,3 +1519,7 @@ static int stmpe_resume(struct device *dev)
EXPORT_GPL_SIMPLE_DEV_PM_OPS(stmpe_dev_pm_ops,
stmpe_suspend, stmpe_resume);
MODULE_DESCRIPTION("STMPE Core driver");
MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
MODULE_LICENSE("GPL");

View File

@ -5,6 +5,7 @@
*/
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mfd/core.h>
@ -96,9 +97,11 @@ static struct mfd_cell vexpress_sysreg_cells[] = {
static int vexpress_sysreg_probe(struct platform_device *pdev)
{
struct gpio_generic_chip *mmc_gpio_chip;
struct gpio_generic_chip_config config;
struct resource *mem;
void __iomem *base;
struct gpio_chip *mmc_gpio_chip;
int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
@ -116,10 +119,22 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!mmc_gpio_chip)
return -ENOMEM;
bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI,
NULL, NULL, NULL, NULL, 0);
mmc_gpio_chip->ngpio = 2;
devm_gpiochip_add_data(&pdev->dev, mmc_gpio_chip, NULL);
config = (typeof(config)){
.dev = &pdev->dev,
.sz = 4,
.dat = base + SYS_MCI,
};
ret = gpio_generic_chip_init(mmc_gpio_chip, &config);
if (ret)
return ret;
mmc_gpio_chip->gc.ngpio = 2;
ret = devm_gpiochip_add_data(&pdev->dev, &mmc_gpio_chip->gc, NULL);
if (ret)
return ret;
return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
vexpress_sysreg_cells,

View File

@ -134,6 +134,17 @@ config CAN_MCBA_USB
This driver supports the CAN BUS Analyzer interface
from Microchip (http://www.microchip.com/development-tools/).
config CAN_NCT6694
tristate "Nuvoton NCT6694 Socket CANfd support"
depends on MFD_NCT6694
select CAN_RX_OFFLOAD
help
If you say yes to this option, support will be included for Nuvoton
NCT6694, a USB device to socket CANfd controller.
This driver can also be built as a module. If so, the module will
be called nct6694_canfd.
config CAN_PEAK_USB
tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD"
help

View File

@ -11,5 +11,6 @@ obj-$(CONFIG_CAN_F81604) += f81604.o
obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb/
obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o
obj-$(CONFIG_CAN_NCT6694) += nct6694_canfd.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
obj-$(CONFIG_CAN_UCAN) += ucan.o

View File

@ -0,0 +1,832 @@
// SPDX-License-Identifier: GPL-2.0
/* Nuvoton NCT6694 Socket CANfd driver based on USB interface.
*
* Copyright (C) 2025 Nuvoton Technology Corp.
*/
#include <linux/bitfield.h>
#include <linux/can/dev.h>
#include <linux/can/rx-offload.h>
#include <linux/ethtool.h>
#include <linux/idr.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#define DEVICE_NAME "nct6694-canfd"
/* USB command module type for NCT6694 CANfd controller.
* This defines the module type used for communication with the NCT6694
* CANfd controller over the USB interface.
*/
#define NCT6694_CANFD_MOD 0x05
/* Command 00h - CAN Setting and Initialization */
#define NCT6694_CANFD_SETTING 0x00
#define NCT6694_CANFD_SETTING_ACTIVE_CTRL1 BIT(0)
#define NCT6694_CANFD_SETTING_ACTIVE_CTRL2 BIT(1)
#define NCT6694_CANFD_SETTING_ACTIVE_NBTP_DBTP BIT(2)
#define NCT6694_CANFD_SETTING_CTRL1_MON BIT(0)
#define NCT6694_CANFD_SETTING_CTRL1_NISO BIT(1)
#define NCT6694_CANFD_SETTING_CTRL1_LBCK BIT(2)
#define NCT6694_CANFD_SETTING_NBTP_NTSEG2 GENMASK(6, 0)
#define NCT6694_CANFD_SETTING_NBTP_NTSEG1 GENMASK(15, 8)
#define NCT6694_CANFD_SETTING_NBTP_NBRP GENMASK(24, 16)
#define NCT6694_CANFD_SETTING_NBTP_NSJW GENMASK(31, 25)
#define NCT6694_CANFD_SETTING_DBTP_DSJW GENMASK(3, 0)
#define NCT6694_CANFD_SETTING_DBTP_DTSEG2 GENMASK(7, 4)
#define NCT6694_CANFD_SETTING_DBTP_DTSEG1 GENMASK(12, 8)
#define NCT6694_CANFD_SETTING_DBTP_DBRP GENMASK(20, 16)
#define NCT6694_CANFD_SETTING_DBTP_TDC BIT(23)
/* Command 01h - CAN Information */
#define NCT6694_CANFD_INFORMATION 0x01
#define NCT6694_CANFD_INFORMATION_SEL 0x00
/* Command 02h - CAN Event */
#define NCT6694_CANFD_EVENT 0x02
#define NCT6694_CANFD_EVENT_SEL(idx, mask) \
((idx ? 0x80 : 0x00) | ((mask) & 0x7F))
#define NCT6694_CANFD_EVENT_MASK GENMASK(5, 0)
#define NCT6694_CANFD_EVT_TX_FIFO_EMPTY BIT(7) /* Read-clear */
#define NCT6694_CANFD_EVT_RX_DATA_LOST BIT(5) /* Read-clear */
#define NCT6694_CANFD_EVT_RX_DATA_IN BIT(7) /* Read-clear */
/* Command 10h - CAN Deliver */
#define NCT6694_CANFD_DELIVER 0x10
#define NCT6694_CANFD_DELIVER_SEL(buf_cnt) \
((buf_cnt) & 0xFF)
/* Command 11h - CAN Receive */
#define NCT6694_CANFD_RECEIVE 0x11
#define NCT6694_CANFD_RECEIVE_SEL(idx, buf_cnt) \
((idx ? 0x80 : 0x00) | ((buf_cnt) & 0x7F))
#define NCT6694_CANFD_FRAME_TAG(idx) (0xC0 | (idx))
#define NCT6694_CANFD_FRAME_FLAG_EFF BIT(0)
#define NCT6694_CANFD_FRAME_FLAG_RTR BIT(1)
#define NCT6694_CANFD_FRAME_FLAG_FD BIT(2)
#define NCT6694_CANFD_FRAME_FLAG_BRS BIT(3)
#define NCT6694_CANFD_FRAME_FLAG_ERR BIT(4)
#define NCT6694_NAPI_WEIGHT 32
enum nct6694_event_err {
NCT6694_CANFD_EVT_ERR_NO_ERROR = 0,
NCT6694_CANFD_EVT_ERR_CRC_ERROR,
NCT6694_CANFD_EVT_ERR_STUFF_ERROR,
NCT6694_CANFD_EVT_ERR_ACK_ERROR,
NCT6694_CANFD_EVT_ERR_FORM_ERROR,
NCT6694_CANFD_EVT_ERR_BIT_ERROR,
NCT6694_CANFD_EVT_ERR_TIMEOUT_ERROR,
NCT6694_CANFD_EVT_ERR_UNKNOWN_ERROR,
};
enum nct6694_event_status {
NCT6694_CANFD_EVT_STS_ERROR_ACTIVE = 0,
NCT6694_CANFD_EVT_STS_ERROR_PASSIVE,
NCT6694_CANFD_EVT_STS_BUS_OFF,
NCT6694_CANFD_EVT_STS_WARNING,
};
struct __packed nct6694_canfd_setting {
__le32 nbr;
__le32 dbr;
u8 active;
u8 reserved[3];
__le16 ctrl1;
__le16 ctrl2;
__le32 nbtp;
__le32 dbtp;
};
struct __packed nct6694_canfd_information {
u8 tx_fifo_cnt;
u8 rx_fifo_cnt;
u8 reserved[2];
__le32 can_clk;
};
struct __packed nct6694_canfd_event {
u8 err;
u8 status;
u8 tx_evt;
u8 rx_evt;
u8 rec;
u8 tec;
u8 reserved[2];
};
struct __packed nct6694_canfd_frame {
u8 tag;
u8 flag;
u8 reserved;
u8 length;
__le32 id;
u8 data[CANFD_MAX_DLEN];
};
struct nct6694_canfd_priv {
struct can_priv can; /* must be the first member */
struct can_rx_offload offload;
struct net_device *ndev;
struct nct6694 *nct6694;
struct workqueue_struct *wq;
struct work_struct tx_work;
struct nct6694_canfd_frame tx;
struct nct6694_canfd_frame rx;
struct nct6694_canfd_event event[2];
struct can_berr_counter bec;
};
static inline struct nct6694_canfd_priv *rx_offload_to_priv(struct can_rx_offload *offload)
{
return container_of(offload, struct nct6694_canfd_priv, offload);
}
static const struct can_bittiming_const nct6694_canfd_bittiming_nominal_const = {
.name = DEVICE_NAME,
.tseg1_min = 1,
.tseg1_max = 256,
.tseg2_min = 1,
.tseg2_max = 128,
.sjw_max = 128,
.brp_min = 1,
.brp_max = 512,
.brp_inc = 1,
};
static const struct can_bittiming_const nct6694_canfd_bittiming_data_const = {
.name = DEVICE_NAME,
.tseg1_min = 1,
.tseg1_max = 32,
.tseg2_min = 1,
.tseg2_max = 16,
.sjw_max = 16,
.brp_min = 1,
.brp_max = 32,
.brp_inc = 1,
};
static void nct6694_canfd_rx_offload(struct can_rx_offload *offload,
struct sk_buff *skb)
{
struct nct6694_canfd_priv *priv = rx_offload_to_priv(offload);
int ret;
ret = can_rx_offload_queue_tail(offload, skb);
if (ret)
priv->ndev->stats.rx_fifo_errors++;
}
static void nct6694_canfd_handle_lost_msg(struct net_device *ndev)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
struct net_device_stats *stats = &ndev->stats;
struct can_frame *cf;
struct sk_buff *skb;
netdev_dbg(ndev, "RX FIFO overflow, message(s) lost.\n");
stats->rx_errors++;
stats->rx_over_errors++;
skb = alloc_can_err_skb(ndev, &cf);
if (!skb)
return;
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
nct6694_canfd_rx_offload(&priv->offload, skb);
}
static void nct6694_canfd_handle_rx(struct net_device *ndev, u8 rx_evt)
{
struct net_device_stats *stats = &ndev->stats;
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
struct nct6694_canfd_frame *frame = &priv->rx;
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_CANFD_MOD,
.cmd = NCT6694_CANFD_RECEIVE,
.sel = NCT6694_CANFD_RECEIVE_SEL(ndev->dev_port, 1),
.len = cpu_to_le16(sizeof(*frame))
};
struct sk_buff *skb;
int ret;
ret = nct6694_read_msg(priv->nct6694, &cmd_hd, frame);
if (ret)
return;
if (frame->flag & NCT6694_CANFD_FRAME_FLAG_FD) {
struct canfd_frame *cfd;
skb = alloc_canfd_skb(priv->ndev, &cfd);
if (!skb) {
stats->rx_dropped++;
return;
}
cfd->can_id = le32_to_cpu(frame->id);
cfd->len = canfd_sanitize_len(frame->length);
if (frame->flag & NCT6694_CANFD_FRAME_FLAG_EFF)
cfd->can_id |= CAN_EFF_FLAG;
if (frame->flag & NCT6694_CANFD_FRAME_FLAG_BRS)
cfd->flags |= CANFD_BRS;
if (frame->flag & NCT6694_CANFD_FRAME_FLAG_ERR)
cfd->flags |= CANFD_ESI;
memcpy(cfd->data, frame->data, cfd->len);
} else {
struct can_frame *cf;
skb = alloc_can_skb(priv->ndev, &cf);
if (!skb) {
stats->rx_dropped++;
return;
}
cf->can_id = le32_to_cpu(frame->id);
cf->len = can_cc_dlc2len(frame->length);
if (frame->flag & NCT6694_CANFD_FRAME_FLAG_EFF)
cf->can_id |= CAN_EFF_FLAG;
if (frame->flag & NCT6694_CANFD_FRAME_FLAG_RTR)
cf->can_id |= CAN_RTR_FLAG;
else
memcpy(cf->data, frame->data, cf->len);
}
nct6694_canfd_rx_offload(&priv->offload, skb);
}
static int nct6694_canfd_get_berr_counter(const struct net_device *ndev,
struct can_berr_counter *bec)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
*bec = priv->bec;
return 0;
}
static void nct6694_canfd_handle_state_change(struct net_device *ndev, u8 status)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
enum can_state new_state, rx_state, tx_state;
struct can_berr_counter bec;
struct can_frame *cf;
struct sk_buff *skb;
nct6694_canfd_get_berr_counter(ndev, &bec);
can_state_get_by_berr_counter(ndev, &bec, &tx_state, &rx_state);
new_state = max(tx_state, rx_state);
/* state hasn't changed */
if (new_state == priv->can.state)
return;
skb = alloc_can_err_skb(ndev, &cf);
can_change_state(ndev, cf, tx_state, rx_state);
if (new_state == CAN_STATE_BUS_OFF) {
can_bus_off(ndev);
} else if (cf) {
cf->can_id |= CAN_ERR_CNT;
cf->data[6] = bec.txerr;
cf->data[7] = bec.rxerr;
}
if (skb)
nct6694_canfd_rx_offload(&priv->offload, skb);
}
static void nct6694_canfd_handle_bus_err(struct net_device *ndev, u8 bus_err)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
struct can_frame *cf;
struct sk_buff *skb;
priv->can.can_stats.bus_error++;
skb = alloc_can_err_skb(ndev, &cf);
if (cf)
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
switch (bus_err) {
case NCT6694_CANFD_EVT_ERR_CRC_ERROR:
netdev_dbg(ndev, "CRC error\n");
ndev->stats.rx_errors++;
if (cf)
cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
break;
case NCT6694_CANFD_EVT_ERR_STUFF_ERROR:
netdev_dbg(ndev, "Stuff error\n");
ndev->stats.rx_errors++;
if (cf)
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
case NCT6694_CANFD_EVT_ERR_ACK_ERROR:
netdev_dbg(ndev, "Ack error\n");
ndev->stats.tx_errors++;
if (cf) {
cf->can_id |= CAN_ERR_ACK;
cf->data[2] |= CAN_ERR_PROT_TX;
}
break;
case NCT6694_CANFD_EVT_ERR_FORM_ERROR:
netdev_dbg(ndev, "Form error\n");
ndev->stats.rx_errors++;
if (cf)
cf->data[2] |= CAN_ERR_PROT_FORM;
break;
case NCT6694_CANFD_EVT_ERR_BIT_ERROR:
netdev_dbg(ndev, "Bit error\n");
ndev->stats.tx_errors++;
if (cf)
cf->data[2] |= CAN_ERR_PROT_TX | CAN_ERR_PROT_BIT;
break;
default:
break;
}
if (skb)
nct6694_canfd_rx_offload(&priv->offload, skb);
}
static void nct6694_canfd_handle_tx(struct net_device *ndev)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
struct net_device_stats *stats = &ndev->stats;
stats->tx_bytes += can_rx_offload_get_echo_skb_queue_tail(&priv->offload,
0, NULL);
stats->tx_packets++;
netif_wake_queue(ndev);
}
static irqreturn_t nct6694_canfd_irq(int irq, void *data)
{
struct net_device *ndev = data;
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
struct nct6694_canfd_event *event = &priv->event[ndev->dev_port];
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_CANFD_MOD,
.cmd = NCT6694_CANFD_EVENT,
.sel = NCT6694_CANFD_EVENT_SEL(ndev->dev_port, NCT6694_CANFD_EVENT_MASK),
.len = cpu_to_le16(sizeof(priv->event))
};
irqreturn_t handled = IRQ_NONE;
int ret;
ret = nct6694_read_msg(priv->nct6694, &cmd_hd, priv->event);
if (ret < 0)
return handled;
if (event->rx_evt & NCT6694_CANFD_EVT_RX_DATA_IN) {
nct6694_canfd_handle_rx(ndev, event->rx_evt);
handled = IRQ_HANDLED;
}
if (event->rx_evt & NCT6694_CANFD_EVT_RX_DATA_LOST) {
nct6694_canfd_handle_lost_msg(ndev);
handled = IRQ_HANDLED;
}
if (event->status) {
nct6694_canfd_handle_state_change(ndev, event->status);
handled = IRQ_HANDLED;
}
if (event->err != NCT6694_CANFD_EVT_ERR_NO_ERROR) {
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
nct6694_canfd_handle_bus_err(ndev, event->err);
handled = IRQ_HANDLED;
}
if (event->tx_evt & NCT6694_CANFD_EVT_TX_FIFO_EMPTY) {
nct6694_canfd_handle_tx(ndev);
handled = IRQ_HANDLED;
}
if (handled)
can_rx_offload_threaded_irq_finish(&priv->offload);
priv->bec.rxerr = event->rec;
priv->bec.txerr = event->tec;
return handled;
}
static void nct6694_canfd_tx_work(struct work_struct *work)
{
struct nct6694_canfd_priv *priv = container_of(work,
struct nct6694_canfd_priv,
tx_work);
struct nct6694_canfd_frame *frame = &priv->tx;
struct net_device *ndev = priv->ndev;
struct net_device_stats *stats = &ndev->stats;
struct sk_buff *skb = priv->can.echo_skb[0];
static const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_CANFD_MOD,
.cmd = NCT6694_CANFD_DELIVER,
.sel = NCT6694_CANFD_DELIVER_SEL(1),
.len = cpu_to_le16(sizeof(*frame))
};
u32 txid;
int err;
memset(frame, 0, sizeof(*frame));
frame->tag = NCT6694_CANFD_FRAME_TAG(ndev->dev_port);
if (can_is_canfd_skb(skb)) {
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
if (cfd->flags & CANFD_BRS)
frame->flag |= NCT6694_CANFD_FRAME_FLAG_BRS;
if (cfd->can_id & CAN_EFF_FLAG) {
txid = cfd->can_id & CAN_EFF_MASK;
frame->flag |= NCT6694_CANFD_FRAME_FLAG_EFF;
} else {
txid = cfd->can_id & CAN_SFF_MASK;
}
frame->flag |= NCT6694_CANFD_FRAME_FLAG_FD;
frame->id = cpu_to_le32(txid);
frame->length = canfd_sanitize_len(cfd->len);
memcpy(frame->data, cfd->data, frame->length);
} else {
struct can_frame *cf = (struct can_frame *)skb->data;
if (cf->can_id & CAN_EFF_FLAG) {
txid = cf->can_id & CAN_EFF_MASK;
frame->flag |= NCT6694_CANFD_FRAME_FLAG_EFF;
} else {
txid = cf->can_id & CAN_SFF_MASK;
}
if (cf->can_id & CAN_RTR_FLAG)
frame->flag |= NCT6694_CANFD_FRAME_FLAG_RTR;
else
memcpy(frame->data, cf->data, cf->len);
frame->id = cpu_to_le32(txid);
frame->length = cf->len;
}
err = nct6694_write_msg(priv->nct6694, &cmd_hd, frame);
if (err) {
can_free_echo_skb(ndev, 0, NULL);
stats->tx_dropped++;
stats->tx_errors++;
netif_wake_queue(ndev);
}
}
static netdev_tx_t nct6694_canfd_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
if (can_dev_dropped_skb(ndev, skb))
return NETDEV_TX_OK;
netif_stop_queue(ndev);
can_put_echo_skb(skb, ndev, 0, 0);
queue_work(priv->wq, &priv->tx_work);
return NETDEV_TX_OK;
}
static int nct6694_canfd_start(struct net_device *ndev)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
const struct can_bittiming *n_bt = &priv->can.bittiming;
const struct can_bittiming *d_bt = &priv->can.fd.data_bittiming;
struct nct6694_canfd_setting *setting __free(kfree) = NULL;
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_CANFD_MOD,
.cmd = NCT6694_CANFD_SETTING,
.sel = ndev->dev_port,
.len = cpu_to_le16(sizeof(*setting))
};
u32 en_tdc;
int ret;
setting = kzalloc(sizeof(*setting), GFP_KERNEL);
if (!setting)
return -ENOMEM;
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_MON);
if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)
setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_NISO);
if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_LBCK);
/* Disable clock divider */
setting->ctrl2 = 0;
setting->nbtp = cpu_to_le32(FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NSJW,
n_bt->sjw - 1) |
FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NBRP,
n_bt->brp - 1) |
FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NTSEG2,
n_bt->phase_seg2 - 1) |
FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NTSEG1,
n_bt->prop_seg + n_bt->phase_seg1 - 1));
if (d_bt->brp <= 2)
en_tdc = NCT6694_CANFD_SETTING_DBTP_TDC;
else
en_tdc = 0;
setting->dbtp = cpu_to_le32(FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DSJW,
d_bt->sjw - 1) |
FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DBRP,
d_bt->brp - 1) |
FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DTSEG2,
d_bt->phase_seg2 - 1) |
FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DTSEG1,
d_bt->prop_seg + d_bt->phase_seg1 - 1) |
en_tdc);
setting->active = NCT6694_CANFD_SETTING_ACTIVE_CTRL1 |
NCT6694_CANFD_SETTING_ACTIVE_CTRL2 |
NCT6694_CANFD_SETTING_ACTIVE_NBTP_DBTP;
ret = nct6694_write_msg(priv->nct6694, &cmd_hd, setting);
if (ret)
return ret;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
return 0;
}
static void nct6694_canfd_stop(struct net_device *ndev)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
struct nct6694_canfd_setting *setting __free(kfree) = NULL;
const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_CANFD_MOD,
.cmd = NCT6694_CANFD_SETTING,
.sel = ndev->dev_port,
.len = cpu_to_le16(sizeof(*setting))
};
/* The NCT6694 cannot be stopped. To ensure safe operation and avoid
* interference, the control mode is set to Listen-Only mode. This
* mode allows the device to monitor bus activity without actively
* participating in communication.
*/
setting = kzalloc(sizeof(*setting), GFP_KERNEL);
if (!setting)
return;
nct6694_read_msg(priv->nct6694, &cmd_hd, setting);
setting->ctrl1 = cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_MON);
setting->active = NCT6694_CANFD_SETTING_ACTIVE_CTRL1;
nct6694_write_msg(priv->nct6694, &cmd_hd, setting);
priv->can.state = CAN_STATE_STOPPED;
}
static int nct6694_canfd_close(struct net_device *ndev)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
netif_stop_queue(ndev);
nct6694_canfd_stop(ndev);
destroy_workqueue(priv->wq);
free_irq(ndev->irq, ndev);
can_rx_offload_disable(&priv->offload);
close_candev(ndev);
return 0;
}
static int nct6694_canfd_set_mode(struct net_device *ndev, enum can_mode mode)
{
int ret;
switch (mode) {
case CAN_MODE_START:
ret = nct6694_canfd_start(ndev);
if (ret)
return ret;
netif_wake_queue(ndev);
break;
default:
return -EOPNOTSUPP;
}
return ret;
}
static int nct6694_canfd_open(struct net_device *ndev)
{
struct nct6694_canfd_priv *priv = netdev_priv(ndev);
int ret;
ret = open_candev(ndev);
if (ret)
return ret;
can_rx_offload_enable(&priv->offload);
ret = request_threaded_irq(ndev->irq, NULL,
nct6694_canfd_irq, IRQF_ONESHOT,
"nct6694_canfd", ndev);
if (ret) {
netdev_err(ndev, "Failed to request IRQ\n");
goto can_rx_offload_disable;
}
priv->wq = alloc_ordered_workqueue("%s-nct6694_wq",
WQ_FREEZABLE | WQ_MEM_RECLAIM,
ndev->name);
if (!priv->wq) {
ret = -ENOMEM;
goto free_irq;
}
ret = nct6694_canfd_start(ndev);
if (ret)
goto destroy_wq;
netif_start_queue(ndev);
return 0;
destroy_wq:
destroy_workqueue(priv->wq);
free_irq:
free_irq(ndev->irq, ndev);
can_rx_offload_disable:
can_rx_offload_disable(&priv->offload);
close_candev(ndev);
return ret;
}
static const struct net_device_ops nct6694_canfd_netdev_ops = {
.ndo_open = nct6694_canfd_open,
.ndo_stop = nct6694_canfd_close,
.ndo_start_xmit = nct6694_canfd_start_xmit,
.ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops nct6694_canfd_ethtool_ops = {
.get_ts_info = ethtool_op_get_ts_info,
};
static int nct6694_canfd_get_clock(struct nct6694_canfd_priv *priv)
{
struct nct6694_canfd_information *info __free(kfree) = NULL;
static const struct nct6694_cmd_header cmd_hd = {
.mod = NCT6694_CANFD_MOD,
.cmd = NCT6694_CANFD_INFORMATION,
.sel = NCT6694_CANFD_INFORMATION_SEL,
.len = cpu_to_le16(sizeof(*info))
};
int ret;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
ret = nct6694_read_msg(priv->nct6694, &cmd_hd, info);
if (ret)
return ret;
return le32_to_cpu(info->can_clk);
}
static int nct6694_canfd_probe(struct platform_device *pdev)
{
struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent);
struct nct6694_canfd_priv *priv;
struct net_device *ndev;
int port, irq, ret, can_clk;
port = ida_alloc(&nct6694->canfd_ida, GFP_KERNEL);
if (port < 0)
return port;
irq = irq_create_mapping(nct6694->domain,
NCT6694_IRQ_CAN0 + port);
if (!irq) {
ret = -EINVAL;
goto free_ida;
}
ndev = alloc_candev(sizeof(struct nct6694_canfd_priv), 1);
if (!ndev) {
ret = -ENOMEM;
goto dispose_irq;
}
ndev->irq = irq;
ndev->flags |= IFF_ECHO;
ndev->dev_port = port;
ndev->netdev_ops = &nct6694_canfd_netdev_ops;
ndev->ethtool_ops = &nct6694_canfd_ethtool_ops;
priv = netdev_priv(ndev);
priv->nct6694 = nct6694;
priv->ndev = ndev;
can_clk = nct6694_canfd_get_clock(priv);
if (can_clk < 0) {
ret = dev_err_probe(&pdev->dev, can_clk,
"Failed to get clock\n");
goto free_candev;
}
INIT_WORK(&priv->tx_work, nct6694_canfd_tx_work);
priv->can.clock.freq = can_clk;
priv->can.bittiming_const = &nct6694_canfd_bittiming_nominal_const;
priv->can.fd.data_bittiming_const = &nct6694_canfd_bittiming_data_const;
priv->can.do_set_mode = nct6694_canfd_set_mode;
priv->can.do_get_berr_counter = nct6694_canfd_get_berr_counter;
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING |
CAN_CTRLMODE_FD_NON_ISO;
ret = can_set_static_ctrlmode(ndev, CAN_CTRLMODE_FD);
if (ret)
goto free_candev;
ret = can_rx_offload_add_manual(ndev, &priv->offload,
NCT6694_NAPI_WEIGHT);
if (ret) {
dev_err_probe(&pdev->dev, ret, "Failed to add rx_offload\n");
goto free_candev;
}
platform_set_drvdata(pdev, priv);
SET_NETDEV_DEV(priv->ndev, &pdev->dev);
ret = register_candev(priv->ndev);
if (ret)
goto rx_offload_del;
return 0;
rx_offload_del:
can_rx_offload_del(&priv->offload);
free_candev:
free_candev(ndev);
dispose_irq:
irq_dispose_mapping(irq);
free_ida:
ida_free(&nct6694->canfd_ida, port);
return ret;
}
static void nct6694_canfd_remove(struct platform_device *pdev)
{
struct nct6694_canfd_priv *priv = platform_get_drvdata(pdev);
struct nct6694 *nct6694 = priv->nct6694;
struct net_device *ndev = priv->ndev;
int port = ndev->dev_port;
int irq = ndev->irq;
unregister_candev(ndev);
can_rx_offload_del(&priv->offload);
free_candev(ndev);
irq_dispose_mapping(irq);
ida_free(&nct6694->canfd_ida, port);
}
static struct platform_driver nct6694_canfd_driver = {
.driver = {
.name = DEVICE_NAME,
},
.probe = nct6694_canfd_probe,
.remove = nct6694_canfd_remove,
};
module_platform_driver(nct6694_canfd_driver);
MODULE_DESCRIPTION("USB-CAN FD driver for NCT6694");
MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
MODULE_LICENSE("GPL");

View File

@ -358,6 +358,17 @@ config PINCTRL_LPC18XX
help
Pinctrl driver for NXP LPC18xx/43xx System Control Unit (SCU).
config PINCTRL_MAX7360
tristate "MAX7360 Pincontrol support"
depends on MFD_MAX7360
select PINMUX
select GENERIC_PINCONF
help
Say Y here to enable pin control support for Maxim MAX7360 keypad
controller.
This keypad controller has 8 GPIO pins that may work as GPIO, or PWM,
or rotary encoder alternate modes.
config PINCTRL_MAX77620
tristate "MAX77620/MAX20024 Pincontrol support"
depends on MFD_MAX77620 && OF

View File

@ -37,6 +37,7 @@ obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o
obj-$(CONFIG_PINCTRL_LOONGSON2) += pinctrl-loongson2.o
obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
obj-$(CONFIG_PINCTRL_LPC18XX) += pinctrl-lpc18xx.o
obj-$(CONFIG_PINCTRL_MAX7360) += pinctrl-max7360.o
obj-$(CONFIG_PINCTRL_MAX77620) += pinctrl-max77620.o
obj-$(CONFIG_PINCTRL_MCP23S08_I2C) += pinctrl-mcp23s08_i2c.o
obj-$(CONFIG_PINCTRL_MCP23S08_SPI) += pinctrl-mcp23s08_spi.o

View File

@ -584,7 +584,7 @@ static void nmk_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
seq_printf(s, "invalid pin offset");
return;
}
nmk_gpio_dbg_show_one(s, pctldev, chip, offset - chip->base, offset);
nmk_gpio_dbg_show_one(s, pctldev, chip, offset - chip->base);
}
static int nmk_dt_add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps,

View File

@ -4,6 +4,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/mfd/syscon.h>
@ -77,7 +78,7 @@
/* Structure for register banks */
struct npcm7xx_gpio {
void __iomem *base;
struct gpio_chip gc;
struct gpio_generic_chip chip;
int irqbase;
int irq;
u32 pinctrl_id;
@ -99,32 +100,26 @@ struct npcm7xx_pinctrl {
};
/* GPIO handling in the pinctrl driver */
static void npcm_gpio_set(struct gpio_chip *gc, void __iomem *reg,
static void npcm_gpio_set(struct gpio_generic_chip *chip, void __iomem *reg,
unsigned int pinmask)
{
unsigned long flags;
unsigned long val;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(chip);
val = ioread32(reg) | pinmask;
iowrite32(val, reg);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static void npcm_gpio_clr(struct gpio_chip *gc, void __iomem *reg,
static void npcm_gpio_clr(struct gpio_generic_chip *chip, void __iomem *reg,
unsigned int pinmask)
{
unsigned long flags;
unsigned long val;
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
guard(gpio_generic_lock_irqsave)(chip);
val = ioread32(reg) & ~pinmask;
iowrite32(val, reg);
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
static void npcmgpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
@ -132,9 +127,9 @@ static void npcmgpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
struct npcm7xx_gpio *bank = gpiochip_get_data(chip);
seq_printf(s, "-- module %d [gpio%d - %d]\n",
bank->gc.base / bank->gc.ngpio,
bank->gc.base,
bank->gc.base + bank->gc.ngpio);
bank->chip.gc.base / bank->chip.gc.ngpio,
bank->chip.gc.base,
bank->chip.gc.base + bank->chip.gc.ngpio);
seq_printf(s, "DIN :%.8x DOUT:%.8x IE :%.8x OE :%.8x\n",
ioread32(bank->base + NPCM7XX_GP_N_DIN),
ioread32(bank->base + NPCM7XX_GP_N_DOUT),
@ -220,7 +215,7 @@ static void npcmgpio_irq_handler(struct irq_desc *desc)
chained_irq_enter(chip, desc);
sts = ioread32(bank->base + NPCM7XX_GP_N_EVST);
en = ioread32(bank->base + NPCM7XX_GP_N_EVEN);
dev_dbg(bank->gc.parent, "==> got irq sts %.8lx %.8lx\n", sts,
dev_dbg(bank->chip.gc.parent, "==> got irq sts %.8lx %.8lx\n", sts,
en);
sts &= en;
@ -235,42 +230,42 @@ static int npcmgpio_set_irq_type(struct irq_data *d, unsigned int type)
struct npcm7xx_gpio *bank = gpiochip_get_data(gc);
unsigned int gpio = BIT(irqd_to_hwirq(d));
dev_dbg(bank->gc.parent, "setirqtype: %u.%u = %u\n", gpio,
dev_dbg(bank->chip.gc.parent, "setirqtype: %u.%u = %u\n", gpio,
d->irq, type);
switch (type) {
case IRQ_TYPE_EDGE_RISING:
dev_dbg(bank->gc.parent, "edge.rising\n");
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
dev_dbg(bank->chip.gc.parent, "edge.rising\n");
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_EVBE, gpio);
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_POL, gpio);
break;
case IRQ_TYPE_EDGE_FALLING:
dev_dbg(bank->gc.parent, "edge.falling\n");
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
dev_dbg(bank->chip.gc.parent, "edge.falling\n");
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_EVBE, gpio);
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_POL, gpio);
break;
case IRQ_TYPE_EDGE_BOTH:
dev_dbg(bank->gc.parent, "edge.both\n");
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
dev_dbg(bank->chip.gc.parent, "edge.both\n");
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_EVBE, gpio);
break;
case IRQ_TYPE_LEVEL_LOW:
dev_dbg(bank->gc.parent, "level.low\n");
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
dev_dbg(bank->chip.gc.parent, "level.low\n");
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_POL, gpio);
break;
case IRQ_TYPE_LEVEL_HIGH:
dev_dbg(bank->gc.parent, "level.high\n");
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
dev_dbg(bank->chip.gc.parent, "level.high\n");
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_POL, gpio);
break;
default:
dev_dbg(bank->gc.parent, "invalid irq type\n");
dev_dbg(bank->chip.gc.parent, "invalid irq type\n");
return -EINVAL;
}
if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_EVTYP, gpio);
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_EVTYP, gpio);
irq_set_handler_locked(d, handle_level_irq);
} else if (type & (IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_EDGE_RISING
| IRQ_TYPE_EDGE_FALLING)) {
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_EVTYP, gpio);
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_EVTYP, gpio);
irq_set_handler_locked(d, handle_edge_irq);
}
@ -283,7 +278,7 @@ static void npcmgpio_irq_ack(struct irq_data *d)
struct npcm7xx_gpio *bank = gpiochip_get_data(gc);
unsigned int gpio = irqd_to_hwirq(d);
dev_dbg(bank->gc.parent, "irq_ack: %u.%u\n", gpio, d->irq);
dev_dbg(bank->chip.gc.parent, "irq_ack: %u.%u\n", gpio, d->irq);
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVST);
}
@ -295,7 +290,7 @@ static void npcmgpio_irq_mask(struct irq_data *d)
unsigned int gpio = irqd_to_hwirq(d);
/* Clear events */
dev_dbg(bank->gc.parent, "irq_mask: %u.%u\n", gpio, d->irq);
dev_dbg(bank->chip.gc.parent, "irq_mask: %u.%u\n", gpio, d->irq);
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVENC);
gpiochip_disable_irq(gc, gpio);
}
@ -309,7 +304,7 @@ static void npcmgpio_irq_unmask(struct irq_data *d)
/* Enable events */
gpiochip_enable_irq(gc, gpio);
dev_dbg(bank->gc.parent, "irq_unmask: %u.%u\n", gpio, d->irq);
dev_dbg(bank->chip.gc.parent, "irq_unmask: %u.%u\n", gpio, d->irq);
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVENS);
}
@ -1423,7 +1418,7 @@ static int npcm7xx_get_slew_rate(struct npcm7xx_gpio *bank,
struct regmap *gcr_regmap, unsigned int pin)
{
u32 val;
int gpio = (pin % bank->gc.ngpio);
int gpio = (pin % bank->chip.gc.ngpio);
unsigned long pinmask = BIT(gpio);
if (pincfg[pin].flag & SLEW)
@ -1443,16 +1438,16 @@ static int npcm7xx_set_slew_rate(struct npcm7xx_gpio *bank,
struct regmap *gcr_regmap, unsigned int pin,
int arg)
{
int gpio = BIT(pin % bank->gc.ngpio);
int gpio = BIT(pin % bank->chip.gc.ngpio);
if (pincfg[pin].flag & SLEW) {
switch (arg) {
case 0:
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_OSRC,
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_OSRC,
gpio);
return 0;
case 1:
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_OSRC,
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_OSRC,
gpio);
return 0;
default:
@ -1485,7 +1480,7 @@ static int npcm7xx_get_drive_strength(struct pinctrl_dev *pctldev,
struct npcm7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev);
struct npcm7xx_gpio *bank =
&npcm->gpio_bank[pin / NPCM7XX_GPIO_PER_BANK];
int gpio = (pin % bank->gc.ngpio);
int gpio = (pin % bank->chip.gc.ngpio);
unsigned long pinmask = BIT(gpio);
u32 ds = 0;
int flg, val;
@ -1496,7 +1491,7 @@ static int npcm7xx_get_drive_strength(struct pinctrl_dev *pctldev,
val = ioread32(bank->base + NPCM7XX_GP_N_ODSC)
& pinmask;
ds = val ? DSHI(flg) : DSLO(flg);
dev_dbg(bank->gc.parent,
dev_dbg(bank->chip.gc.parent,
"pin %d strength %d = %d\n", pin, val, ds);
return ds;
}
@ -1511,20 +1506,20 @@ static int npcm7xx_set_drive_strength(struct npcm7xx_pinctrl *npcm,
int v;
struct npcm7xx_gpio *bank =
&npcm->gpio_bank[pin / NPCM7XX_GPIO_PER_BANK];
int gpio = BIT(pin % bank->gc.ngpio);
int gpio = BIT(pin % bank->chip.gc.ngpio);
v = (pincfg[pin].flag & DRIVE_STRENGTH_MASK);
if (!nval || !v)
return -ENOTSUPP;
if (DSLO(v) == nval) {
dev_dbg(bank->gc.parent,
dev_dbg(bank->chip.gc.parent,
"setting pin %d to low strength [%d]\n", pin, nval);
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_ODSC, gpio);
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_ODSC, gpio);
return 0;
} else if (DSHI(v) == nval) {
dev_dbg(bank->gc.parent,
dev_dbg(bank->chip.gc.parent,
"setting pin %d to high strength [%d]\n", pin, nval);
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_ODSC, gpio);
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_ODSC, gpio);
return 0;
}
@ -1657,9 +1652,9 @@ static int npcm_gpio_set_direction(struct pinctrl_dev *pctldev,
struct npcm7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev);
struct npcm7xx_gpio *bank =
&npcm->gpio_bank[offset / NPCM7XX_GPIO_PER_BANK];
int gpio = BIT(offset % bank->gc.ngpio);
int gpio = BIT(offset % bank->chip.gc.ngpio);
dev_dbg(bank->gc.parent, "GPIO Set Direction: %d = %d\n", offset,
dev_dbg(bank->chip.gc.parent, "GPIO Set Direction: %d = %d\n", offset,
input);
if (input)
iowrite32(gpio, bank->base + NPCM7XX_GP_N_OEC);
@ -1687,7 +1682,7 @@ static int npcm7xx_config_get(struct pinctrl_dev *pctldev, unsigned int pin,
struct npcm7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev);
struct npcm7xx_gpio *bank =
&npcm->gpio_bank[pin / NPCM7XX_GPIO_PER_BANK];
int gpio = (pin % bank->gc.ngpio);
int gpio = (pin % bank->chip.gc.ngpio);
unsigned long pinmask = BIT(gpio);
u32 ie, oe, pu, pd;
int rc = 0;
@ -1750,38 +1745,38 @@ static int npcm7xx_config_set_one(struct npcm7xx_pinctrl *npcm,
u16 arg = pinconf_to_config_argument(config);
struct npcm7xx_gpio *bank =
&npcm->gpio_bank[pin / NPCM7XX_GPIO_PER_BANK];
int gpio = BIT(pin % bank->gc.ngpio);
int gpio = BIT(pin % bank->chip.gc.ngpio);
dev_dbg(bank->gc.parent, "param=%d %d[GPIO]\n", param, pin);
dev_dbg(bank->chip.gc.parent, "param=%d %d[GPIO]\n", param, pin);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_PU, gpio);
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_PD, gpio);
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_PU, gpio);
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_PD, gpio);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_PU, gpio);
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_PD, gpio);
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_PU, gpio);
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_PD, gpio);
break;
case PIN_CONFIG_BIAS_PULL_UP:
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_PD, gpio);
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_PU, gpio);
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_PD, gpio);
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_PU, gpio);
break;
case PIN_CONFIG_INPUT_ENABLE:
iowrite32(gpio, bank->base + NPCM7XX_GP_N_OEC);
bank->direction_input(&bank->gc, pin % bank->gc.ngpio);
bank->direction_input(&bank->chip.gc, pin % bank->chip.gc.ngpio);
break;
case PIN_CONFIG_OUTPUT:
bank->direction_output(&bank->gc, pin % bank->gc.ngpio, arg);
bank->direction_output(&bank->chip.gc, pin % bank->chip.gc.ngpio, arg);
iowrite32(gpio, bank->base + NPCM7XX_GP_N_OES);
break;
case PIN_CONFIG_DRIVE_PUSH_PULL:
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_OTYP, gpio);
npcm_gpio_clr(&bank->chip, bank->base + NPCM7XX_GP_N_OTYP, gpio);
break;
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_OTYP, gpio);
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_OTYP, gpio);
break;
case PIN_CONFIG_INPUT_DEBOUNCE:
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_DBNC, gpio);
npcm_gpio_set(&bank->chip, bank->base + NPCM7XX_GP_N_DBNC, gpio);
break;
case PIN_CONFIG_SLEW_RATE:
return npcm7xx_set_slew_rate(bank, npcm->gcr_regmap, pin, arg);
@ -1829,6 +1824,7 @@ static const struct pinctrl_desc npcm7xx_pinctrl_desc = {
static int npcm7xx_gpio_of(struct npcm7xx_pinctrl *pctrl)
{
struct gpio_generic_chip_config config;
int ret = -ENXIO;
struct device *dev = pctrl->dev;
struct fwnode_reference_args args;
@ -1840,15 +1836,18 @@ static int npcm7xx_gpio_of(struct npcm7xx_pinctrl *pctrl)
if (!pctrl->gpio_bank[id].base)
return -EINVAL;
ret = bgpio_init(&pctrl->gpio_bank[id].gc, dev, 4,
pctrl->gpio_bank[id].base + NPCM7XX_GP_N_DIN,
pctrl->gpio_bank[id].base + NPCM7XX_GP_N_DOUT,
NULL,
NULL,
pctrl->gpio_bank[id].base + NPCM7XX_GP_N_IEM,
BGPIOF_READ_OUTPUT_REG_SET);
config = (typeof(config)){
.dev = dev,
.sz = 4,
.dat = pctrl->gpio_bank[id].base + NPCM7XX_GP_N_DIN,
.set = pctrl->gpio_bank[id].base + NPCM7XX_GP_N_DOUT,
.dirin = pctrl->gpio_bank[id].base + NPCM7XX_GP_N_IEM,
.flags = GPIO_GENERIC_READ_OUTPUT_REG_SET,
};
ret = gpio_generic_chip_init(&pctrl->gpio_bank[id].chip, &config);
if (ret) {
dev_err(dev, "bgpio_init() failed\n");
dev_err(dev, "failed to initialize the generic GPIO chip\n");
return ret;
}
@ -1866,23 +1865,23 @@ static int npcm7xx_gpio_of(struct npcm7xx_pinctrl *pctrl)
pctrl->gpio_bank[id].irq = ret;
pctrl->gpio_bank[id].irqbase = id * NPCM7XX_GPIO_PER_BANK;
pctrl->gpio_bank[id].pinctrl_id = args.args[0];
pctrl->gpio_bank[id].gc.base = args.args[1];
pctrl->gpio_bank[id].gc.ngpio = args.args[2];
pctrl->gpio_bank[id].gc.owner = THIS_MODULE;
pctrl->gpio_bank[id].gc.parent = dev;
pctrl->gpio_bank[id].gc.fwnode = child;
pctrl->gpio_bank[id].gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", child);
if (pctrl->gpio_bank[id].gc.label == NULL)
pctrl->gpio_bank[id].chip.gc.base = args.args[1];
pctrl->gpio_bank[id].chip.gc.ngpio = args.args[2];
pctrl->gpio_bank[id].chip.gc.owner = THIS_MODULE;
pctrl->gpio_bank[id].chip.gc.parent = dev;
pctrl->gpio_bank[id].chip.gc.fwnode = child;
pctrl->gpio_bank[id].chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", child);
if (pctrl->gpio_bank[id].chip.gc.label == NULL)
return -ENOMEM;
pctrl->gpio_bank[id].gc.dbg_show = npcmgpio_dbg_show;
pctrl->gpio_bank[id].direction_input = pctrl->gpio_bank[id].gc.direction_input;
pctrl->gpio_bank[id].gc.direction_input = npcmgpio_direction_input;
pctrl->gpio_bank[id].direction_output = pctrl->gpio_bank[id].gc.direction_output;
pctrl->gpio_bank[id].gc.direction_output = npcmgpio_direction_output;
pctrl->gpio_bank[id].request = pctrl->gpio_bank[id].gc.request;
pctrl->gpio_bank[id].gc.request = npcmgpio_gpio_request;
pctrl->gpio_bank[id].gc.free = pinctrl_gpio_free;
pctrl->gpio_bank[id].chip.gc.dbg_show = npcmgpio_dbg_show;
pctrl->gpio_bank[id].direction_input = pctrl->gpio_bank[id].chip.gc.direction_input;
pctrl->gpio_bank[id].chip.gc.direction_input = npcmgpio_direction_input;
pctrl->gpio_bank[id].direction_output = pctrl->gpio_bank[id].chip.gc.direction_output;
pctrl->gpio_bank[id].chip.gc.direction_output = npcmgpio_direction_output;
pctrl->gpio_bank[id].request = pctrl->gpio_bank[id].chip.gc.request;
pctrl->gpio_bank[id].chip.gc.request = npcmgpio_gpio_request;
pctrl->gpio_bank[id].chip.gc.free = pinctrl_gpio_free;
id++;
}
@ -1897,7 +1896,7 @@ static int npcm7xx_gpio_register(struct npcm7xx_pinctrl *pctrl)
for (id = 0 ; id < pctrl->bank_num ; id++) {
struct gpio_irq_chip *girq;
girq = &pctrl->gpio_bank[id].gc.irq;
girq = &pctrl->gpio_bank[id].chip.gc.irq;
gpio_irq_chip_set_chip(girq, &npcmgpio_irqchip);
girq->parent_handler = npcmgpio_irq_handler;
girq->num_parents = 1;
@ -1912,21 +1911,21 @@ static int npcm7xx_gpio_register(struct npcm7xx_pinctrl *pctrl)
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_level_irq;
ret = devm_gpiochip_add_data(pctrl->dev,
&pctrl->gpio_bank[id].gc,
&pctrl->gpio_bank[id].chip.gc,
&pctrl->gpio_bank[id]);
if (ret) {
dev_err(pctrl->dev, "Failed to add GPIO chip %u\n", id);
goto err_register;
}
ret = gpiochip_add_pin_range(&pctrl->gpio_bank[id].gc,
ret = gpiochip_add_pin_range(&pctrl->gpio_bank[id].chip.gc,
dev_name(pctrl->dev),
pctrl->gpio_bank[id].pinctrl_id,
pctrl->gpio_bank[id].gc.base,
pctrl->gpio_bank[id].gc.ngpio);
pctrl->gpio_bank[id].chip.gc.base,
pctrl->gpio_bank[id].chip.gc.ngpio);
if (ret < 0) {
dev_err(pctrl->dev, "Failed to add GPIO bank %u\n", id);
gpiochip_remove(&pctrl->gpio_bank[id].gc);
gpiochip_remove(&pctrl->gpio_bank[id].chip.gc);
goto err_register;
}
}
@ -1935,7 +1934,7 @@ static int npcm7xx_gpio_register(struct npcm7xx_pinctrl *pctrl)
err_register:
for (; id > 0; id--)
gpiochip_remove(&pctrl->gpio_bank[id - 1].gc);
gpiochip_remove(&pctrl->gpio_bank[id - 1].chip.gc);
return ret;
}

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