gpio updates for v6.19-rc1

GPIO core:
 - add proper support for shared GPIOs that's aiming to replace the
   current sharing mechanism (which provides no synchronization ot enable
   counting) and enable it for Qualcomm platforms
 - improve the software node GPIO lookup by using the fwnode
   representation instead of the software node's name which was prone to
   bugs (GPIO controllers don't have to use the software node's name as
   their kernel label)
 - remove the last user of legacy-of-mm-gpiochip.h and drop the header
 - move closer to removing the legacy gpio_request_one() routine
 - rename some symbols for consistency
 - shrink GPIO printk() helpers by reusing existing code
 - remove some redundant kernel messages
 - use min() instead of min_t() in GPIO ACPI code
 - use system_percpu_wq instead of system_wq in GPIO character device code
 
 New drivers:
 - add a driver for the QIXIS FPGA GPIO controller
 
 Driver improvements:
 - use modernized variants of power management macros across a wide array
   of drivers in order to avoid having to use the __maybe_unused attribute
 - convert gpio-elkhartlake and reset-gpio to using the auxiliary bus
   instead of the platform bus as they are not really described in
   firmware
 - use lock guards and update symbol prefixes in gpio-mmio
 - support the bryx radio interface kit in gpio-mpsse + refactor the
   driver
 - use software nodes for configuring the reset-gpio driver, including
   setting up the reference to the shared "reset" pin
 - check and propagate the return value of gpiod_set_value() to user-space
   in gpio-virtuser (this was previously not possible as this function
   returned void)
 - extend the gpio-regmap helper with more features (bypass cache for
   aliased inputs, force writes for aliased data registers, add a new
   configuration parameter)
 - remove unneeded includes from gpio-aspeed and gpio-latch
 - add support for Tegra410 to gpio-tegra186
 - replace PCI-specific PM with generic device-level PM in gpio-bt8xx
 - use dynamic GPIO range allocation in gpio-loongson-64bit
 - improve handling of level-triggered interrupts in gpio-pca953x
 - add suspend/resume support to gpio-fxl6408
 - add support for more models to gpio-menz127
 - optimize gpio-mvebu interrupt handling by avoiding unnecessary calls
   to mvebu_gpio_irq_handler()
 - make locking more consistent in gpio-grgpio
 
 Device-tree bindings:
 - document new NXP and Microchip models
 
 Documentation:
 - add a comprehensive compatibility and feature list for gpio-pca953x,
   which is a great addition as it's probably the most commonly used GPIO
   expander driver
 - kernel-doc tweaks
 
 Late fixes:
 - use BYTE_CTRL_MODE for 2K2000/3000 models in gpio-loongson
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEkeUTLeW1Rh17omX8BZ0uy/82hMMFAmktpZkACgkQBZ0uy/82
 hMPRNg/+PEhBT1Sm9GZDL2c3urSfEyIIA97nxvV6rad75Xfj8IVy+orvyhJHJgni
 ksjlvLUQLYQ1j82TlCIZDlj3dAmk3rkb6wwbcUubOb7G/xkvWHQztrnmDk4bvSdr
 0vR/dEGOUDRLJxPjeQdFzEoDqLrCqy5QwugH3hd6NASBy+n/g7j2Gb4Pc8IA14wL
 ogkBlP8nXVv2w7GMFESjNldWvxFnOasGuzIfM7mIBDBSjLzEANcuH/E5dNXPFIO7
 kNN8eSnSfMGH0Z0DjdU9tkaAiLypw9FHqSwU6JKNjE/nzzcGV3OzDMM2vgKLETin
 z9tNPBtvXOSBuFbxydWUgX20AmNo5ZEfqGXCwinTHOLwABGZPigi7Ogwxeox0SZ4
 OKm37GUtZlO+dJC8bWUA82l9CwM9GDcxn7/Fv4s4vLaULUfeZHXn1OP4JZ4WmuZi
 J7D5lMIiXKI9EBH95AX3c/r5drZQh1T4Rr7GJ4GG9uceYGTzsE65idI0KXTT1mbx
 nMfQYvNSHGk123rN1bLQov1jZE7foXE9mQ+MzMNSWI3C1WdCGRBorqN8+wMrBU29
 OLS66UorNwSCIl0XY+lIgdiW6OXBTc65jF1v+EdSZaFknEYHQm9TIxQNH+tQDYfz
 irl2dhxz/iaekxO7p516iY3NeqO9Af4SfITVmAX4xDvak3tcYsU=
 =Mw+x
 -----END PGP SIGNATURE-----

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

Pull gpio updates from Bartosz Golaszewski:
 "There's one new driver, lots of various updates to existing ones, some
  refactoring support for new models and misc tweaks and fixes.

  The biggest new feature in GPIO core is adding support for managed,
  enable-counted sharing of GPIO pins, something that - until now - was
  only hacked around with the GPIOD_FLAGS_BIT_NONEXCLUSIVE request flag
  which basically allowed drivers to "fight it out" for the descriptor
  and provided no synchronization. It was enabled on Qualcomm platforms
  (and thus is enabled on arm64 defconfig) and I plan on removing
  GPIOD_FLAGS_BIT_NONEXCLUSIVE once all drivers using it are switched to
  the new mechanism.

  GPIO core:
   - add proper support for shared GPIOs that's aiming to replace the
     current sharing mechanism (which provides no synchronization ot
     enable counting) and enable it for Qualcomm platforms
   - improve the software node GPIO lookup by using the fwnode
     representation instead of the software node's name which was prone
     to bugs (GPIO controllers don't have to use the software node's
     name as their kernel label)
   - remove the last user of legacy-of-mm-gpiochip.h and drop the header
   - move closer to removing the legacy gpio_request_one() routine
   - rename some symbols for consistency
   - shrink GPIO printk() helpers by reusing existing code
   - remove some redundant kernel messages
   - use min() instead of min_t() in GPIO ACPI code
   - use system_percpu_wq instead of system_wq in GPIO character device
     code

  New drivers:
   - add a driver for the QIXIS FPGA GPIO controller

  Driver improvements:
   - use modernized variants of power management macros across a wide
     array of drivers in order to avoid having to use the __maybe_unused
     attribute
   - convert gpio-elkhartlake and reset-gpio to using the auxiliary bus
     instead of the platform bus as they are not really described in
     firmware
   - use lock guards and update symbol prefixes in gpio-mmio
   - support the bryx radio interface kit in gpio-mpsse + refactor the
     driver
   - use software nodes for configuring the reset-gpio driver, including
     setting up the reference to the shared "reset" pin
   - check and propagate the return value of gpiod_set_value() to
     user-space in gpio-virtuser (this was previously not possible as
     this function returned void)
   - extend the gpio-regmap helper with more features (bypass cache for
     aliased inputs, force writes for aliased data registers, add a new
     configuration parameter)
   - remove unneeded includes from gpio-aspeed and gpio-latch
   - add support for Tegra410 to gpio-tegra186
   - replace PCI-specific PM with generic device-level PM in gpio-bt8xx
   - use dynamic GPIO range allocation in gpio-loongson-64bit
   - improve handling of level-triggered interrupts in gpio-pca953x
   - add suspend/resume support to gpio-fxl6408
   - add support for more models to gpio-menz127
   - optimize gpio-mvebu interrupt handling by avoiding unnecessary
     calls to mvebu_gpio_irq_handler()
   - make locking more consistent in gpio-grgpio

  Device-tree bindings:
   - document new NXP and Microchip models

  Documentation:
   - add a comprehensive compatibility and feature list for
     gpio-pca953x, which is a great addition as it's probably the most
     commonly used GPIO expander driver
   - kernel-doc tweaks

  Late fixes:
   - use BYTE_CTRL_MODE for 2K2000/3000 models in gpio-loongson"

* tag 'gpio-updates-for-v6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (80 commits)
  gpio: loongson: Switch 2K2000/3000 GPIO to BYTE_CTRL_MODE
  gpio: regmap: fix kernel-doc notation
  gpio: shared: fix a deadlock
  gpio: shared-proxy: set suppress_bind_attrs
  gpio: shared: ignore GPIO hogs when traversing the device tree
  gpio: shared: ignore special __symbols__ node when traversing device tree
  gpio: shared: handle the reset-gpios corner case
  gpio: zynq: Use modern PM macros
  gpio: xilinx: Use modern PM macros
  gpio: xgene: Use modern PM macros
  gpio: uniphier: Use modern PM macros
  gpio: tqmx86: Use modern PM macros
  gpio: pch: Use modern PM macros
  gpio: omap: Use modern PM macros
  gpio: msc313: Use modern PM macros
  gpio: mlxbf2: Use modern PM macros
  gpio: ml-ioh: Use modern PM macros
  gpio: pl061: Use modern PM macros
  gpio: htc-egpio: Use modern PM macros
  gpio: brcmstb: Use modern PM macros
  ...
This commit is contained in:
Linus Torvalds 2025-12-04 12:33:38 -08:00
commit 500920fa76
65 changed files with 1939 additions and 921 deletions

View File

@ -11,7 +11,10 @@ maintainers:
properties: properties:
compatible: compatible:
items: oneOf:
- items:
- const: microchip,pic64gx-gpio
- const: microchip,mpfs-gpio
- enum: - enum:
- microchip,mpfs-gpio - microchip,mpfs-gpio
- microchip,coregpio-rtl-v3 - microchip,coregpio-rtl-v3

View File

@ -22,6 +22,8 @@ properties:
- cznic,moxtet-gpio - cznic,moxtet-gpio
- dlg,slg7xl45106 - dlg,slg7xl45106
- fcs,fxl6408 - fcs,fxl6408
- fsl,ls1046aqds-fpga-gpio-stat-pres2
- fsl,lx2160ardb-fpga-gpio-sfp
- gateworks,pld-gpio - gateworks,pld-gpio
- ibm,ppc4xx-gpio - ibm,ppc4xx-gpio
- loongson,ls1x-gpio - loongson,ls1x-gpio

View File

@ -15,6 +15,7 @@ Contents:
legacy-boards legacy-boards
drivers-on-gpio drivers-on-gpio
bt8xxgpio bt8xxgpio
pca953x
Core Core
==== ====

View File

@ -0,0 +1,552 @@
============================================
PCA953x I²C GPIO expander compatibility list
============================================
:Author: Levente Révész <levente.revesz@eilabs.com>
I went through all the datasheets and created this note listing
chip functions and register layouts.
Overview of chips
=================
Chips with the basic 4 registers
--------------------------------
These chips have 4 register banks: input, output, invert and direction.
Each of these banks contains (lines/8) registers, one for each GPIO port.
Banks offset is always a power of 2:
- 4 lines -> bank offset is 1
- 8 lines -> bank offset is 1
- 16 lines -> bank offset is 2
- 24 lines -> bank offset is 4
- 32 lines -> bank offset is 4
- 40 lines -> bank offset is 8
For example, register layout of GPIO expander with 24 lines:
+------+-----------------+--------+
| addr | function | bank |
+======+=================+========+
| 00 | input port0 | |
+------+-----------------+ |
| 01 | input port1 | bank 0 |
+------+-----------------+ |
| 02 | input port2 | |
+------+-----------------+--------+
| 03 | n/a | |
+------+-----------------+--------+
| 04 | output port0 | |
+------+-----------------+ |
| 05 | output port1 | bank 1 |
+------+-----------------+ |
| 06 | output port2 | |
+------+-----------------+--------+
| 07 | n/a | |
+------+-----------------+--------+
| 08 | invert port0 | |
+------+-----------------+ |
| 09 | invert port1 | bank 2 |
+------+-----------------+ |
| 0A | invert port2 | |
+------+-----------------+--------+
| 0B | n/a | |
+------+-----------------+--------+
| 0C | direction port0 | |
+------+-----------------+ |
| 0D | direction port1 | bank 3 |
+------+-----------------+ |
| 0E | direction port2 | |
+------+-----------------+--------+
| 0F | n/a | |
+------+-----------------+--------+
.. note::
This is followed by all supported chips, except by pcal6534.
The table below shows the offsets for each of the compatible chips:
========== ===== ========= ===== ====== ====== =========
compatible lines interrupt input output invert direction
========== ===== ========= ===== ====== ====== =========
pca9536 4 no 00 01 02 03
pca9537 4 yes 00 01 02 03
pca6408 8 yes 00 01 02 03
tca6408 8 yes 00 01 02 03
pca9534 8 yes 00 01 02 03
pca9538 8 yes 00 01 02 03
pca9554 8 yes 00 01 02 03
tca9554 8 yes 00 01 02 03
pca9556 8 no 00 01 02 03
pca9557 8 no 00 01 02 03
pca6107 8 yes 00 01 02 03
pca6416 16 yes 00 02 04 06
tca6416 16 yes 00 02 04 06
pca9535 16 yes 00 02 04 06
pca9539 16 yes 00 02 04 06
tca9539 16 yes 00 02 04 06
pca9555 16 yes 00 02 04 06
max7318 16 yes 00 02 04 06
tca6424 24 yes 00 04 08 0C
========== ===== ========= ===== ====== ====== =========
Chips with additional timeout_en register
-----------------------------------------
These Maxim chips have a bus timeout function which can be enabled in
the timeout_en register. This is present in only two chips. Defaults to
timeout disabled.
========== ===== ========= ===== ====== ====== ========= ==========
compatible lines interrupt input output invert direction timeout_en
========== ===== ========= ===== ====== ====== ========= ==========
max7310 8 no 00 01 02 03 04
max7312 16 yes 00 02 04 06 08
========== ===== ========= ===== ====== ====== ========= ==========
Chips with additional int_mask register
---------------------------------------
These chips have an interrupt mask register in addition to the 4 basic
registers. The interrupt masks default to all interrupts disabled. To
use interrupts with these chips, the driver has to set the int_mask
register.
========== ===== ========= ===== ====== ====== ========= ========
compatible lines interrupt input output invert direction int_mask
========== ===== ========= ===== ====== ====== ========= ========
pca9505 40 yes 00 08 10 18 20
pca9506 40 yes 00 08 10 18 20
========== ===== ========= ===== ====== ====== ========= ========
Chips with additional int_mask and out_conf registers
-----------------------------------------------------
This chip has an interrupt mask register, and an output port
configuration register, which can select between push-pull and
open-drain modes. Each bit controls two lines. Both of these registers
are present in PCAL chips as well, albeit the out_conf works
differently.
========== ===== ========= ===== ====== ====== ========= ======== ========
compatible lines interrupt input output invert direction int_mask out_conf
========== ===== ========= ===== ====== ====== ========= ======== ========
pca9698 40 yes 00 08 10 18 20 28
========== ===== ========= ===== ====== ====== ========= ======== ========
pca9698 also has a "master output" register for setting all outputs per
port to the same value simultaneously, and a chip specific mode register
for various additional chip settings.
========== ============= ====
compatible master_output mode
========== ============= ====
pca9698 29 2A
========== ============= ====
Chips with LED blink and intensity control
------------------------------------------
These Maxim chips have no invert register.
They have two sets of output registers (output0 and output1). An internal
timer alternates the effective output between the values set in these
registers, if blink mode is enabled in the blink register. The
master_intensity register and the intensity registers together define
the PWM intensity value for each pair of outputs.
These chips can be used as simple GPIO expanders if the driver handles the
input, output0 and direction registers.
========== ===== ========= ===== ======= ========= ======= ================ ===== =========
compatible lines interrupt input output0 direction output1 master_intensity blink intensity
========== ===== ========= ===== ======= ========= ======= ================ ===== =========
max7315 8 yes 00 01 03 09 0E 0F 10
max7313 16 yes 00 02 06 0A 0E 0F 10
========== ===== ========= ===== ======= ========= ======= ================ ===== =========
Basic PCAL chips
----------------
========== ===== ========= ===== ====== ====== =========
compatible lines interrupt input output invert direction
========== ===== ========= ===== ====== ====== =========
pcal6408 8 yes 00 01 02 03
pcal9554b 8 yes 00 01 02 03
pcal6416 16 yes 00 02 04 06
pcal9535 16 yes 00 02 04 06
pcal9555a 16 yes 00 02 04 06
========== ===== ========= ===== ====== ====== =========
These chips have several additional features:
1. output drive strength setting (out_strength)
2. input latch (in_latch)
3. pull-up/pull-down (pull_in, pull_sel)
4. push-pull/open-drain outputs (out_conf)
5. interrupt mask and interrupt status (int_mask, int_status)
========== ============ ======== ======= ======== ======== ========== ========
compatible out_strength in_latch pull_en pull_sel int_mask int_status out_conf
========== ============ ======== ======= ======== ======== ========== ========
pcal6408 40 42 43 44 45 46 4F
pcal9554b 40 42 43 44 45 46 4F
pcal6416 40 44 46 48 4A 4C 4F
pcal9535 40 44 46 48 4A 4C 4F
pcal9555a 40 44 46 48 4A 4C 4F
========== ============ ======== ======= ======== ======== ========== ========
Currently the driver has support for the input latch, pull-up/pull-down
and uses int_mask and int_status for interrupts.
PCAL chips with extended interrupt and output configuration functions
---------------------------------------------------------------------
========== ===== ========= ===== ====== ====== =========
compatible lines interrupt input output invert direction
========== ===== ========= ===== ====== ====== =========
pcal6524 24 yes 00 04 08 0C
pcal6534 34 yes 00 05 0A 0F
========== ===== ========= ===== ====== ====== =========
These chips have the full PCAL register set, plus the following functions:
1. interrupt event selection: level, rising, falling, any edge
2. clear interrupt status per line
3. read input without clearing interrupt status
4. individual output config (push-pull/open-drain) per output line
5. debounce inputs
========== ============ ======== ======= ======== ======== ========== ========
compatible out_strength in_latch pull_en pull_sel int_mask int_status out_conf
========== ============ ======== ======= ======== ======== ========== ========
pcal6524 40 48 4C 50 54 58 5C
pcal6534 30 3A 3F 44 49 4E 53
========== ============ ======== ======= ======== ======== ========== ========
========== ======== ========= ============ ============== ======== ==============
compatible int_edge int_clear input_status indiv_out_conf debounce debounce_count
========== ======== ========= ============ ============== ======== ==============
pcal6524 60 68 6C 70 74 76
pcal6534 54 5E 63 68 6D 6F
========== ======== ========= ============ ============== ======== ==============
As can be seen in the table above, pcal6534 does not follow the usual
bank spacing rule. Its banks are closely packed instead.
PCA957X chips with a completely different register layout
---------------------------------------------------------
These chips have the basic 4 registers, but at unusual addresses.
Additionally, they have:
1. pull-up/pull-down (pull_sel)
2. a global pull enable, defaults to disabled (config)
3. interrupt mask, interrupt status (int_mask, int_status)
========== ===== ========= ===== ====== ====== ======== ========= ====== ======== ==========
compatible lines interrupt input invert config pull_sel direction output int_mask int_status
========== ===== ========= ===== ====== ====== ======== ========= ====== ======== ==========
pca9574 8 yes 00 01 02 03 04 05 06 07
pca9575 16 yes 00 02 04 06 08 0A 0C 0E
========== ===== ========= ===== ====== ====== ======== ========= ====== ======== ==========
Currently the driver supports none of the advanced features.
XRA1202
-------
Basic 4 registers, plus advanced features:
1. interrupt mask, defaults to interrupts disabled
2. interrupt status
3. interrupt event selection, level, rising, falling, any edge
(int_mask, rising_mask, falling_mask)
4. pull-up (no pull-down)
5. tri-state
6. debounce
========== ===== ========= ===== ====== ====== ========= =========
compatible lines interrupt input output invert direction pullup_en
========== ===== ========= ===== ====== ====== ========= =========
xra1202 8 yes 00 01 02 03 04
========== ===== ========= ===== ====== ====== ========= =========
========== ======== ======== ========== =========== ============ ========
compatible int_mask tristate int_status rising_mask falling_mask debounce
========== ======== ======== ========== =========== ============ ========
xra1202 05 06 07 08 09 0A
========== ======== ======== ========== =========== ============ ========
Overview of functions
=====================
This section lists chip functions that are supported by the driver
already, or are at least common in multiple chips.
Input, Output, Invert, Direction
--------------------------------
The basic 4 GPIO functions are present in all but one chip category, i.e.
`Chips with LED blink and intensity control`_ are missing the invert
register.
3 different layouts are used for these registers:
1. banks 0, 1, 2, 3 with bank offsets of 2^n
- all other chips
2. banks 0, 1, 2, 3 with closely packed banks
- pcal6534
3. banks 0, 5, 1, 4 with bank offsets of 2^n
- pca9574
- pca9575
Interrupts
----------
Only an interrupt mask register
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The same layout is used for all of these:
1. bank 5 with bank offsets of 2^n
- pca9505
- pca9506
- pca9698
Interrupt mask and interrupt status registers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These work the same way in all of the chips: mask and status have
one bit per line, 1 in the mask means interrupt enabled.
Layouts:
1. base offset 0x40, bank 5 and bank 6, bank offsets of 2^n
- pcal6408
- pcal6416
- pcal9535
- pcal9554b
- pcal9555a
- pcal6524
2. base offset 0x30, bank 5 and 6, closely packed banks
- pcal6534
3. bank 6 and 7, bank offsets of 2^n
- pca9574
- pca9575
4. bank 5 and 7, bank offsets of 2^n
- xra1202
Interrupt on specific edges
~~~~~~~~~~~~~~~~~~~~~~~~~~~
`PCAL chips with extended interrupt and output configuration functions`_
have an int_edge register. This contains 2 bits per line, one of 4 events
can be selected for each line:
0: level, 1: rising edge, 2: falling edge, 3: any edge
Layouts:
1. base offset 0x40, bank 7, bank offsets of 2^n
- pcal6524
2. base offset 0x30, bank 7 + offset 0x01, closely packed banks
(out_conf is 1 byte, not (lines/8) bytes, hence the 0x01 offset)
- pcal6534
`XRA1202`_ chips have a different mechanism for the same thing: they have
a rising mask and a falling mask, with one bit per line.
Layout:
1. bank 5, bank offsets of 2^n
Input latch
-----------
Only `Basic PCAL chips`_ and
`PCAL chips with extended interrupt and output configuration functions`_
have this function. When the latch is enabled, the interrupt is not cleared
until the input port is read. When the latch is disabled, the interrupt
is cleared even if the input register is not read, if the input pin returns
to the logic value it had before generating the interrupt. Defaults to latch
disabled.
Currently the driver enables the latch for each line with interrupt
enabled.
1. base offset 0x40, bank 2, bank offsets of 2^n
- pcal6408
- pcal6416
- pcal9535
- pcal9554b
- pcal9555a
- pcal6524
2. base offset 0x30, bank 2, closely packed banks
- pcal6534
Pull-up and pull-down
---------------------
`Basic PCAL chips`_ and
`PCAL chips with extended interrupt and output configuration functions`_
use the same mechanism: their pull_en register enables the pull-up or pull-down
function, and their pull_sel register chooses the direction. They all use one
bit per line.
0: pull-down, 1: pull-up
Layouts:
1. base offset 0x40, bank 3 (en) and 4 (sel), bank offsets of 2^n
- pcal6408
- pcal6416
- pcal9535
- pcal9554b
- pcal9555a
- pcal6524
2. base offset 0x30, bank 3 (en) and 4 (sel), closely packed banks
- pcal6534
`PCA957X chips with a completely different register layout`_ have a pull_sel
register with one bit per line, and a global pull_en bit in their config
register.
Layout:
1. bank 2 (config), bank 3 (sel), bank offsets of 2^n
- pca9574
- pca9575
`XRA1202`_ chips can only pull-up. They have a pullup_en register.
Layout:
1. bank 4, bank offsets of 2^n
- xra1202
Push-pull and open-drain
------------------------
`Chips with additional int_mask and out_conf registers`_ have this function,
but only for select IO ports. Register has 1 bit per 2 lines. In pca9698,
only port0 and port1 have this function.
0: open-drain, 1: push-pull
Layout:
1. base offset 5*bankoffset
- pca9698
`Basic PCAL chips`_ have 1 bit per port in one single out_conf register.
Only whole ports can be configured.
0: push-pull, 1: open-drain
Layout:
1. base offset 0x4F
- pcal6408
- pcal6416
- pcal9535
- pcal9554b
- pcal9555a
`PCAL chips with extended interrupt and output configuration functions`_
can set this for each line individually. They have the same per-port out_conf
register as `Basic PCAL chips`_, but they also have an indiv_out_conf register
with one bit per line, which inverts the effect of the port-wise setting.
0: push-pull, 1: open-drain
Layouts:
1. base offset 0x40 + 7*bankoffset (out_conf),
base offset 0x60, bank 4 (indiv_out_conf) with bank offset of 2^n
- pcal6524
2. base offset 0x30 + 7*banksize (out_conf),
base offset 0x54, bank 4 (indiv_out_conf), closely packed banks
- pcal6534
This function is currently not supported by the driver.
Output drive strength
---------------------
Only PCAL chips have this function. 2 bits per line.
==== ==============
bits drive strength
==== ==============
00 0.25x
01 0.50x
10 0.75x
11 1.00x
==== ==============
1. base offset 0x40, bank 0 and 1, bank offsets of 2^n
- pcal6408
- pcal6416
- pcal9535
- pcal9554b
- pcal9555a
- pcal6524
2. base offset 0x30, bank 0 and 1, closely packed banks
- pcal6534
Currently not supported by the driver.
Datasheets
==========
- MAX7310: https://datasheets.maximintegrated.com/en/ds/MAX7310.pdf
- MAX7312: https://datasheets.maximintegrated.com/en/ds/MAX7312.pdf
- MAX7313: https://datasheets.maximintegrated.com/en/ds/MAX7313.pdf
- MAX7315: https://datasheets.maximintegrated.com/en/ds/MAX7315.pdf
- MAX7318: https://datasheets.maximintegrated.com/en/ds/MAX7318.pdf
- PCA6107: https://pdf1.alldatasheet.com/datasheet-pdf/view/161780/TI/PCA6107.html
- PCA6408A: https://www.nxp.com/docs/en/data-sheet/PCA6408A.pdf
- PCA6416A: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
- PCA9505: https://www.nxp.com/docs/en/data-sheet/PCA9505_9506.pdf
- PCA9505: https://www.nxp.com/docs/en/data-sheet/PCA9505_9506.pdf
- PCA9534: https://www.nxp.com/docs/en/data-sheet/PCA9534.pdf
- PCA9535: https://www.nxp.com/docs/en/data-sheet/PCA9535_PCA9535C.pdf
- PCA9536: https://www.nxp.com/docs/en/data-sheet/PCA9536.pdf
- PCA9537: https://www.nxp.com/docs/en/data-sheet/PCA9537.pdf
- PCA9538: https://www.nxp.com/docs/en/data-sheet/PCA9538.pdf
- PCA9539: https://www.nxp.com/docs/en/data-sheet/PCA9539_PCA9539R.pdf
- PCA9554: https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf
- PCA9555: https://www.nxp.com/docs/en/data-sheet/PCA9555.pdf
- PCA9556: https://www.nxp.com/docs/en/data-sheet/PCA9556.pdf
- PCA9557: https://www.nxp.com/docs/en/data-sheet/PCA9557.pdf
- PCA9574: https://www.nxp.com/docs/en/data-sheet/PCA9574.pdf
- PCA9575: https://www.nxp.com/docs/en/data-sheet/PCA9575.pdf
- PCA9698: https://www.nxp.com/docs/en/data-sheet/PCA9698.pdf
- PCAL6408A: https://www.nxp.com/docs/en/data-sheet/PCAL6408A.pdf
- PCAL6416A: https://www.nxp.com/docs/en/data-sheet/PCAL6416A.pdf
- PCAL6524: https://www.nxp.com/docs/en/data-sheet/PCAL6524.pdf
- PCAL6534: https://www.nxp.com/docs/en/data-sheet/PCAL6534.pdf
- PCAL9535A: https://www.nxp.com/docs/en/data-sheet/PCAL9535A.pdf
- PCAL9554B: https://www.nxp.com/docs/en/data-sheet/PCAL9554B_PCAL9554C.pdf
- PCAL9555A: https://www.nxp.com/docs/en/data-sheet/PCAL9555A.pdf
- TCA6408A: https://www.ti.com/lit/gpn/tca6408a
- TCA6416: https://www.ti.com/lit/gpn/tca6416
- TCA6424: https://www.ti.com/lit/gpn/tca6424
- TCA9539: https://www.ti.com/lit/gpn/tca9539
- TCA9554: https://www.ti.com/lit/gpn/tca9554
- XRA1202: https://assets.maxlinear.com/web/documents/xra1202_1202p_101_042213.pdf

View File

@ -12548,6 +12548,13 @@ F: drivers/gpu/drm/xe/
F: include/drm/intel/ F: include/drm/intel/
F: include/uapi/drm/xe_drm.h F: include/uapi/drm/xe_drm.h
INTEL ELKHART LAKE PSE I/O DRIVER
M: Raag Jadav <raag.jadav@intel.com>
L: platform-driver-x86@vger.kernel.org
S: Supported
F: drivers/platform/x86/intel/ehl_pse_io.c
F: include/linux/ehl_pse_io_aux.h
INTEL ETHERNET DRIVERS INTEL ETHERNET DRIVERS
M: Tony Nguyen <anthony.l.nguyen@intel.com> M: Tony Nguyen <anthony.l.nguyen@intel.com>
M: Przemek Kitszel <przemyslaw.kitszel@intel.com> M: Przemek Kitszel <przemyslaw.kitszel@intel.com>

View File

@ -316,6 +316,7 @@ config ARCH_QCOM
select GPIOLIB select GPIOLIB
select PINCTRL select PINCTRL
select HAVE_PWRCTRL if PCI select HAVE_PWRCTRL if PCI
select HAVE_SHARED_GPIOS
help help
This enables support for the ARMv8 based Qualcomm chipsets. This enables support for the ARMv8 based Qualcomm chipsets.

View File

@ -535,14 +535,29 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
ref_array = prop->pointer; ref_array = prop->pointer;
ref = &ref_array[index]; ref = &ref_array[index];
refnode = software_node_fwnode(ref->node); /*
* A software node can reference other software nodes or firmware
* nodes (which are the abstraction layer sitting on top of them).
* This is done to ensure we can create references to static software
* nodes before they're registered with the firmware node framework.
* At the time the reference is being resolved, we expect the swnodes
* in question to already have been registered and to be backed by
* a firmware node. This is why we use the fwnode API below to read the
* relevant properties and bump the reference count.
*/
if (ref->swnode)
refnode = software_node_fwnode(ref->swnode);
else if (ref->fwnode)
refnode = ref->fwnode;
else
return -EINVAL;
if (!refnode) if (!refnode)
return -ENOENT; return -ENOENT;
if (nargs_prop) { if (nargs_prop) {
error = property_entry_read_int_array(ref->node->properties, error = fwnode_property_read_u32(refnode, nargs_prop, &nargs_prop_val);
nargs_prop, sizeof(u32),
&nargs_prop_val, 1);
if (error) if (error)
return error; return error;
@ -555,7 +570,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
if (!args) if (!args)
return 0; return 0;
args->fwnode = software_node_get(refnode); args->fwnode = fwnode_handle_get(refnode);
args->nargs = nargs; args->nargs = nargs;
for (i = 0; i < nargs; i++) for (i = 0; i < nargs; i++)
@ -635,7 +650,10 @@ software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
ref = prop->pointer; ref = prop->pointer;
return software_node_get(software_node_fwnode(ref[0].node)); if (!ref->swnode)
return NULL;
return software_node_get(software_node_fwnode(ref->swnode));
} }
static struct fwnode_handle * static struct fwnode_handle *

View File

@ -45,14 +45,6 @@ config GPIOLIB_IRQCHIP
select IRQ_DOMAIN select IRQ_DOMAIN
bool bool
config OF_GPIO_MM_GPIOCHIP
bool
help
This adds support for the legacy 'struct of_mm_gpio_chip' interface
from PowerPC. Existing drivers using this interface need to select
this symbol, but new drivers should use the generic gpio-regmap
infrastructure instead.
config GPIO_SHARED config GPIO_SHARED
def_bool y def_bool y
depends on HAVE_SHARED_GPIOS || COMPILE_TEST depends on HAVE_SHARED_GPIOS || COMPILE_TEST
@ -484,7 +476,6 @@ config GPIO_MENZ127
config GPIO_MM_LANTIQ config GPIO_MM_LANTIQ
bool "Lantiq Memory mapped GPIOs" bool "Lantiq Memory mapped GPIOs"
depends on LANTIQ && SOC_XWAY depends on LANTIQ && SOC_XWAY
select OF_GPIO_MM_GPIOCHIP
help help
This enables support for memory mapped GPIOs on the External Bus Unit This enables support for memory mapped GPIOs on the External Bus Unit
(EBU) found on Lantiq SoCs. The GPIOs are output only as they are (EBU) found on Lantiq SoCs. The GPIOs are output only as they are
@ -1421,7 +1412,7 @@ config HTC_EGPIO
config GPIO_ELKHARTLAKE config GPIO_ELKHARTLAKE
tristate "Intel Elkhart Lake PSE GPIO support" tristate "Intel Elkhart Lake PSE GPIO support"
depends on X86 || COMPILE_TEST depends on INTEL_EHL_PSE_IO
select GPIO_TANGIER select GPIO_TANGIER
help help
Select this option to enable GPIO support for Intel Elkhart Lake Select this option to enable GPIO support for Intel Elkhart Lake
@ -1573,6 +1564,15 @@ config GPIO_PMIC_EIC_SPRD
help help
Say yes here to support Spreadtrum PMIC EIC device. Say yes here to support Spreadtrum PMIC EIC device.
config GPIO_QIXIS_FPGA
tristate "NXP QIXIS FPGA GPIO support"
depends on MFD_SIMPLE_MFD_I2C || COMPILE_TEST
select GPIO_REGMAP
help
This enables support for the GPIOs found in the QIXIS FPGA which is
integrated on some NXP Layerscape boards such as LX2160ARDB and
LS1046AQDS.
config GPIO_RC5T583 config GPIO_RC5T583
bool "RICOH RC5T583 GPIO" bool "RICOH RC5T583 GPIO"
depends on MFD_RC5T583 depends on MFD_RC5T583

View File

@ -147,6 +147,7 @@ obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o
obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
obj-$(CONFIG_GPIO_QIXIS_FPGA) += gpio-qixis-fpga.o
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o

View File

@ -86,17 +86,6 @@ Work items:
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Get rid of <linux/gpio/legacy-of-mm-gpiochip.h>
Work items:
- Get rid of struct of_mm_gpio_chip altogether: use the generic MMIO
GPIO for all current users (see below). Delete struct of_mm_gpio_chip,
to_of_mm_gpio_chip(), of_mm_gpiochip_add_data(), of_mm_gpiochip_remove(),
CONFIG_OF_GPIO_MM_GPIOCHIP from the kernel.
-------------------------------------------------------------------------------
Collect drivers Collect drivers
Collect GPIO drivers from arch/* and other places that should be placed Collect GPIO drivers from arch/* and other places that should be placed

View File

@ -24,12 +24,11 @@
/* /*
* These two headers aren't meant to be used by GPIO drivers. We need * These two headers aren't meant to be used by GPIO drivers. We need
* them in order to access gpio_chip_hwgpio() which we need to implement * them in order to access gpiod_hwgpio() which we need to implement
* the aspeed specific API which allows the coprocessor to request * the aspeed specific API which allows the coprocessor to request
* access to some GPIOs and to arbitrate between coprocessor and ARM. * access to some GPIOs and to arbitrate between coprocessor and ARM.
*/ */
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include "gpiolib.h"
/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ /* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
@ -942,7 +941,7 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc,
{ {
struct gpio_chip *chip = gpiod_to_chip(desc); struct gpio_chip *chip = gpiod_to_chip(desc);
struct aspeed_gpio *gpio = gpiochip_get_data(chip); struct aspeed_gpio *gpio = gpiochip_get_data(chip);
int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); int rc = 0, bindex, offset = gpiod_hwgpio(desc);
const struct aspeed_gpio_bank *bank = to_bank(offset); const struct aspeed_gpio_bank *bank = to_bank(offset);
if (!aspeed_gpio_support_copro(gpio)) if (!aspeed_gpio_support_copro(gpio))
@ -987,7 +986,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc)
{ {
struct gpio_chip *chip = gpiod_to_chip(desc); struct gpio_chip *chip = gpiod_to_chip(desc);
struct aspeed_gpio *gpio = gpiochip_get_data(chip); struct aspeed_gpio *gpio = gpiochip_get_data(chip);
int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); int rc = 0, bindex, offset = gpiod_hwgpio(desc);
if (!aspeed_gpio_support_copro(gpio)) if (!aspeed_gpio_support_copro(gpio))
return -EOPNOTSUPP; return -EOPNOTSUPP;

View File

@ -533,7 +533,6 @@ static void brcmstb_gpio_shutdown(struct platform_device *pdev)
brcmstb_gpio_quiesce(&pdev->dev, false); brcmstb_gpio_quiesce(&pdev->dev, false);
} }
#ifdef CONFIG_PM_SLEEP
static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv, static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv,
struct brcmstb_gpio_bank *bank) struct brcmstb_gpio_bank *bank)
{ {
@ -572,14 +571,9 @@ static int brcmstb_gpio_resume(struct device *dev)
return 0; return 0;
} }
#else
#define brcmstb_gpio_suspend NULL
#define brcmstb_gpio_resume NULL
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops brcmstb_gpio_pm_ops = { static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
.suspend_noirq = brcmstb_gpio_suspend, .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend),
.resume_noirq = brcmstb_gpio_resume, .resume_noirq = pm_sleep_ptr(brcmstb_gpio_resume),
}; };
static int brcmstb_gpio_probe(struct platform_device *pdev) static int brcmstb_gpio_probe(struct platform_device *pdev)
@ -755,7 +749,7 @@ static struct platform_driver brcmstb_gpio_driver = {
.driver = { .driver = {
.name = "brcmstb-gpio", .name = "brcmstb-gpio",
.of_match_table = brcmstb_gpio_of_match, .of_match_table = brcmstb_gpio_of_match,
.pm = &brcmstb_gpio_pm_ops, .pm = pm_sleep_ptr(&brcmstb_gpio_pm_ops),
}, },
.probe = brcmstb_gpio_probe, .probe = brcmstb_gpio_probe,
.remove = brcmstb_gpio_remove, .remove = brcmstb_gpio_remove,

View File

@ -52,10 +52,8 @@ struct bt8xxgpio {
struct pci_dev *pdev; struct pci_dev *pdev;
struct gpio_chip gpio; struct gpio_chip gpio;
#ifdef CONFIG_PM
u32 saved_outen; u32 saved_outen;
u32 saved_data; u32 saved_data;
#endif
}; };
#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) #define bgwrite(dat, adr) writel((dat), bg->mmio+(adr))
@ -224,9 +222,10 @@ static void bt8xxgpio_remove(struct pci_dev *pdev)
pci_disable_device(pdev); pci_disable_device(pdev);
} }
#ifdef CONFIG_PM
static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) static int bt8xxgpio_suspend(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev);
struct bt8xxgpio *bg = pci_get_drvdata(pdev); struct bt8xxgpio *bg = pci_get_drvdata(pdev);
scoped_guard(spinlock_irqsave, &bg->lock) { scoped_guard(spinlock_irqsave, &bg->lock) {
@ -238,23 +237,13 @@ static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state)
bgwrite(0x0, BT848_GPIO_OUT_EN); bgwrite(0x0, BT848_GPIO_OUT_EN);
} }
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0; return 0;
} }
static int bt8xxgpio_resume(struct pci_dev *pdev) static int bt8xxgpio_resume(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev);
struct bt8xxgpio *bg = pci_get_drvdata(pdev); struct bt8xxgpio *bg = pci_get_drvdata(pdev);
int err;
pci_set_power_state(pdev, PCI_D0);
err = pci_enable_device(pdev);
if (err)
return err;
pci_restore_state(pdev);
guard(spinlock_irqsave)(&bg->lock); guard(spinlock_irqsave)(&bg->lock);
@ -267,10 +256,8 @@ static int bt8xxgpio_resume(struct pci_dev *pdev)
return 0; return 0;
} }
#else
#define bt8xxgpio_suspend NULL static DEFINE_SIMPLE_DEV_PM_OPS(bt8xxgpio_pm_ops, bt8xxgpio_suspend, bt8xxgpio_resume);
#define bt8xxgpio_resume NULL
#endif /* CONFIG_PM */
static const struct pci_device_id bt8xxgpio_pci_tbl[] = { static const struct pci_device_id bt8xxgpio_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) },
@ -286,8 +273,7 @@ static struct pci_driver bt8xxgpio_pci_driver = {
.id_table = bt8xxgpio_pci_tbl, .id_table = bt8xxgpio_pci_tbl,
.probe = bt8xxgpio_probe, .probe = bt8xxgpio_probe,
.remove = bt8xxgpio_remove, .remove = bt8xxgpio_remove,
.suspend = bt8xxgpio_suspend, .driver.pm = &bt8xxgpio_pm_ops,
.resume = bt8xxgpio_resume,
}; };
module_pci_driver(bt8xxgpio_pci_driver); module_pci_driver(bt8xxgpio_pci_driver);

View File

@ -79,7 +79,6 @@ struct dwapb_platform_data {
unsigned int nports; unsigned int nports;
}; };
#ifdef CONFIG_PM_SLEEP
/* Store GPIO context across system-wide suspend/resume transitions */ /* Store GPIO context across system-wide suspend/resume transitions */
struct dwapb_context { struct dwapb_context {
u32 data; u32 data;
@ -92,7 +91,6 @@ struct dwapb_context {
u32 int_deb; u32 int_deb;
u32 wake_en; u32 wake_en;
}; };
#endif
struct dwapb_gpio_port_irqchip { struct dwapb_gpio_port_irqchip {
unsigned int nr_irqs; unsigned int nr_irqs;
@ -103,9 +101,7 @@ struct dwapb_gpio_port {
struct gpio_generic_chip chip; struct gpio_generic_chip chip;
struct dwapb_gpio_port_irqchip *pirq; struct dwapb_gpio_port_irqchip *pirq;
struct dwapb_gpio *gpio; struct dwapb_gpio *gpio;
#ifdef CONFIG_PM_SLEEP
struct dwapb_context *ctx; struct dwapb_context *ctx;
#endif
unsigned int idx; unsigned int idx;
}; };
@ -363,7 +359,6 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
{ {
struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
@ -378,9 +373,6 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
return 0; return 0;
} }
#else
#define dwapb_irq_set_wake NULL
#endif
static const struct irq_chip dwapb_irq_chip = { static const struct irq_chip dwapb_irq_chip = {
.name = DWAPB_DRIVER_NAME, .name = DWAPB_DRIVER_NAME,
@ -390,7 +382,7 @@ static const struct irq_chip dwapb_irq_chip = {
.irq_set_type = dwapb_irq_set_type, .irq_set_type = dwapb_irq_set_type,
.irq_enable = dwapb_irq_enable, .irq_enable = dwapb_irq_enable,
.irq_disable = dwapb_irq_disable, .irq_disable = dwapb_irq_disable,
.irq_set_wake = dwapb_irq_set_wake, .irq_set_wake = pm_sleep_ptr(dwapb_irq_set_wake),
.flags = IRQCHIP_IMMUTABLE, .flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS, GPIOCHIP_IRQ_RESOURCE_HELPERS,
}; };
@ -759,7 +751,6 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int dwapb_gpio_suspend(struct device *dev) static int dwapb_gpio_suspend(struct device *dev)
{ {
struct dwapb_gpio *gpio = dev_get_drvdata(dev); struct dwapb_gpio *gpio = dev_get_drvdata(dev);
@ -844,15 +835,14 @@ static int dwapb_gpio_resume(struct device *dev)
return 0; return 0;
} }
#endif
static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend, static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops,
dwapb_gpio_resume); dwapb_gpio_suspend, dwapb_gpio_resume);
static struct platform_driver dwapb_gpio_driver = { static struct platform_driver dwapb_gpio_driver = {
.driver = { .driver = {
.name = DWAPB_DRIVER_NAME, .name = DWAPB_DRIVER_NAME,
.pm = &dwapb_gpio_pm_ops, .pm = pm_sleep_ptr(&dwapb_gpio_pm_ops),
.of_match_table = dwapb_of_match, .of_match_table = dwapb_of_match,
.acpi_match_table = dwapb_acpi_match, .acpi_match_table = dwapb_acpi_match,
}, },

View File

@ -2,43 +2,46 @@
/* /*
* Intel Elkhart Lake PSE GPIO driver * Intel Elkhart Lake PSE GPIO driver
* *
* Copyright (c) 2023 Intel Corporation. * Copyright (c) 2023, 2025 Intel Corporation.
* *
* Authors: Pandith N <pandith.n@intel.com> * Authors: Pandith N <pandith.n@intel.com>
* Raag Jadav <raag.jadav@intel.com> * Raag Jadav <raag.jadav@intel.com>
*/ */
#include <linux/auxiliary_bus.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/ehl_pse_io_aux.h>
#include "gpio-tangier.h" #include "gpio-tangier.h"
/* Each Intel EHL PSE GPIO Controller has 30 GPIO pins */ /* Each Intel EHL PSE GPIO Controller has 30 GPIO pins */
#define EHL_PSE_NGPIO 30 #define EHL_PSE_NGPIO 30
static int ehl_gpio_probe(struct platform_device *pdev) static int ehl_gpio_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id)
{ {
struct device *dev = &pdev->dev; struct device *dev = &adev->dev;
struct ehl_pse_io_data *data;
struct tng_gpio *priv; struct tng_gpio *priv;
int irq, ret; int ret;
irq = platform_get_irq(pdev, 0); data = dev_get_platdata(dev);
if (irq < 0) if (!data)
return irq; return -ENODATA;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
priv->reg_base = devm_platform_ioremap_resource(pdev, 0); priv->reg_base = devm_ioremap_resource(dev, &data->mem);
if (IS_ERR(priv->reg_base)) if (IS_ERR(priv->reg_base))
return PTR_ERR(priv->reg_base); return PTR_ERR(priv->reg_base);
priv->dev = dev; priv->dev = dev;
priv->irq = irq; priv->irq = data->irq;
priv->info.base = -1; priv->info.base = -1;
priv->info.ngpio = EHL_PSE_NGPIO; priv->info.ngpio = EHL_PSE_NGPIO;
@ -51,25 +54,24 @@ static int ehl_gpio_probe(struct platform_device *pdev)
if (ret) if (ret)
return dev_err_probe(dev, ret, "tng_gpio_probe error\n"); return dev_err_probe(dev, ret, "tng_gpio_probe error\n");
platform_set_drvdata(pdev, priv); auxiliary_set_drvdata(adev, priv);
return 0; return 0;
} }
static const struct platform_device_id ehl_gpio_ids[] = { static const struct auxiliary_device_id ehl_gpio_ids[] = {
{ "gpio-elkhartlake" }, { EHL_PSE_IO_NAME "." EHL_PSE_GPIO_NAME },
{ } { }
}; };
MODULE_DEVICE_TABLE(platform, ehl_gpio_ids); MODULE_DEVICE_TABLE(auxiliary, ehl_gpio_ids);
static struct platform_driver ehl_gpio_driver = { static struct auxiliary_driver ehl_gpio_driver = {
.driver = { .driver = {
.name = "gpio-elkhartlake",
.pm = pm_sleep_ptr(&tng_gpio_pm_ops), .pm = pm_sleep_ptr(&tng_gpio_pm_ops),
}, },
.probe = ehl_gpio_probe, .probe = ehl_gpio_probe,
.id_table = ehl_gpio_ids, .id_table = ehl_gpio_ids,
}; };
module_platform_driver(ehl_gpio_driver); module_auxiliary_driver(ehl_gpio_driver);
MODULE_AUTHOR("Pandith N <pandith.n@intel.com>"); MODULE_AUTHOR("Pandith N <pandith.n@intel.com>");
MODULE_AUTHOR("Raag Jadav <raag.jadav@intel.com>"); MODULE_AUTHOR("Raag Jadav <raag.jadav@intel.com>");

View File

@ -123,6 +123,8 @@ static int fxl6408_probe(struct i2c_client *client)
if (ret) if (ret)
return ret; return ret;
i2c_set_clientdata(client, gpio_config.regmap);
/* Disable High-Z of outputs, so that our OUTPUT updates actually take effect. */ /* Disable High-Z of outputs, so that our OUTPUT updates actually take effect. */
ret = regmap_write(gpio_config.regmap, FXL6408_REG_OUTPUT_HIGH_Z, 0); ret = regmap_write(gpio_config.regmap, FXL6408_REG_OUTPUT_HIGH_Z, 0);
if (ret) if (ret)
@ -131,6 +133,16 @@ static int fxl6408_probe(struct i2c_client *client)
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
} }
static int fxl6408_resume(struct device *dev)
{
struct regmap *regmap = dev_get_drvdata(dev);
regcache_mark_dirty(regmap);
return regcache_sync(regmap);
}
static DEFINE_SIMPLE_DEV_PM_OPS(fxl6408_pm_ops, NULL, fxl6408_resume);
static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = { static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = {
{ .compatible = "fcs,fxl6408" }, { .compatible = "fcs,fxl6408" },
{ } { }
@ -146,6 +158,7 @@ MODULE_DEVICE_TABLE(i2c, fxl6408_id);
static struct i2c_driver fxl6408_driver = { static struct i2c_driver fxl6408_driver = {
.driver = { .driver = {
.name = "fxl6408", .name = "fxl6408",
.pm = pm_sleep_ptr(&fxl6408_pm_ops),
.of_match_table = fxl6408_dt_ids, .of_match_table = fxl6408_dt_ids,
}, },
.probe = fxl6408_probe, .probe = fxl6408_probe,

View File

@ -46,7 +46,7 @@
/* Structure for an irq of the core - called an underlying irq */ /* Structure for an irq of the core - called an underlying irq */
struct grgpio_uirq { struct grgpio_uirq {
u8 refcnt; /* Reference counter to manage requesting/freeing of uirq */ atomic_t refcnt; /* Reference counter to manage requesting/freeing of uirq */
u8 uirq; /* Underlying irq of the gpio driver */ u8 uirq; /* Underlying irq of the gpio driver */
}; };
@ -242,30 +242,22 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
irq, offset); irq, offset);
gpio_generic_chip_lock_irqsave(&priv->chip, flags); gpio_generic_chip_lock_irqsave(&priv->chip, flags);
/* Request underlying irq if not already requested */
lirq->irq = irq; lirq->irq = irq;
uirq = &priv->uirqs[lirq->index]; uirq = &priv->uirqs[lirq->index];
if (uirq->refcnt == 0) { gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
/*
* FIXME: This is not how locking works at all, you can't just /* Request underlying irq if not already requested */
* release the lock for a moment to do something that can't if (atomic_fetch_add(1, &uirq->refcnt) == 0) {
* sleep...
*/
gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, ret = request_irq(uirq->uirq, grgpio_irq_handler, 0,
dev_name(priv->dev), priv); dev_name(priv->dev), priv);
if (ret) { if (ret) {
dev_err(priv->dev, dev_err(priv->dev,
"Could not request underlying irq %d\n", "Could not request underlying irq %d\n",
uirq->uirq); uirq->uirq);
atomic_dec(&uirq->refcnt); /* rollback */
return ret; return ret;
} }
gpio_generic_chip_lock_irqsave(&priv->chip, flags);
} }
uirq->refcnt++;
gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
/* Setup irq */ /* Setup irq */
irq_set_chip_data(irq, priv); irq_set_chip_data(irq, priv);
@ -306,8 +298,7 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
if (index >= 0) { if (index >= 0) {
uirq = &priv->uirqs[lirq->index]; uirq = &priv->uirqs[lirq->index];
uirq->refcnt--; if (atomic_dec_and_test(&uirq->refcnt)) {
if (uirq->refcnt == 0) {
gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
free_irq(uirq->uirq, priv); free_irq(uirq->uirq, priv);
return; return;
@ -434,6 +425,7 @@ static int grgpio_probe(struct platform_device *ofdev)
continue; continue;
} }
priv->uirqs[lirq->index].uirq = ret; priv->uirqs[lirq->index].uirq = ret;
atomic_set(&priv->uirqs[lirq->index].refcnt, 0);
} }
} }

View File

@ -364,21 +364,20 @@ static int __init egpio_probe(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM static int egpio_suspend(struct device *dev)
static int egpio_suspend(struct platform_device *pdev, pm_message_t state)
{ {
struct egpio_info *ei = platform_get_drvdata(pdev); struct egpio_info *ei = dev_get_drvdata(dev);
if (ei->chained_irq && device_may_wakeup(&pdev->dev)) if (ei->chained_irq && device_may_wakeup(dev))
enable_irq_wake(ei->chained_irq); enable_irq_wake(ei->chained_irq);
return 0; return 0;
} }
static int egpio_resume(struct platform_device *pdev) static int egpio_resume(struct device *dev)
{ {
struct egpio_info *ei = platform_get_drvdata(pdev); struct egpio_info *ei = dev_get_drvdata(dev);
if (ei->chained_irq && device_may_wakeup(&pdev->dev)) if (ei->chained_irq && device_may_wakeup(dev))
disable_irq_wake(ei->chained_irq); disable_irq_wake(ei->chained_irq);
/* Update registers from the cache, in case /* Update registers from the cache, in case
@ -386,19 +385,15 @@ static int egpio_resume(struct platform_device *pdev)
egpio_write_cache(ei); egpio_write_cache(ei);
return 0; return 0;
} }
#else
#define egpio_suspend NULL
#define egpio_resume NULL
#endif
static DEFINE_SIMPLE_DEV_PM_OPS(egpio_pm_ops, egpio_suspend, egpio_resume);
static struct platform_driver egpio_driver = { static struct platform_driver egpio_driver = {
.driver = { .driver = {
.name = "htc-egpio", .name = "htc-egpio",
.suppress_bind_attrs = true, .suppress_bind_attrs = true,
.pm = pm_sleep_ptr(&egpio_pm_ops),
}, },
.suspend = egpio_suspend,
.resume = egpio_resume,
}; };
static int __init egpio_init(void) static int __init egpio_init(void)

View File

@ -48,8 +48,6 @@
#include <linux/property.h> #include <linux/property.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "gpiolib.h"
struct gpio_latch_priv { struct gpio_latch_priv {
struct gpio_chip gc; struct gpio_chip gc;
struct gpio_descs *clk_gpios; struct gpio_descs *clk_gpios;

View File

@ -312,6 +312,7 @@ static int loongson_gpio_init(struct platform_device *pdev, struct loongson_gpio
lgpio->chip.gc.direction_output = loongson_gpio_direction_output; lgpio->chip.gc.direction_output = loongson_gpio_direction_output;
lgpio->chip.gc.set = loongson_gpio_set; lgpio->chip.gc.set = loongson_gpio_set;
lgpio->chip.gc.parent = &pdev->dev; lgpio->chip.gc.parent = &pdev->dev;
lgpio->chip.gc.base = -1;
spin_lock_init(&lgpio->lock); spin_lock_init(&lgpio->lock);
} }
@ -407,11 +408,11 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data0 = {
static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = { static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = {
.label = "ls2k2000_gpio", .label = "ls2k2000_gpio",
.mode = BIT_CTRL_MODE, .mode = BYTE_CTRL_MODE,
.conf_offset = 0x0, .conf_offset = 0x800,
.in_offset = 0x20, .in_offset = 0xa00,
.out_offset = 0x10, .out_offset = 0x900,
.inten_offset = 0x30, .inten_offset = 0xb00,
}; };
static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = { static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = {

View File

@ -24,6 +24,11 @@
#define MEN_Z127_ODER 0x1C #define MEN_Z127_ODER 0x1C
#define GPIO_TO_DBCNT_REG(gpio) ((gpio * 4) + 0x80) #define GPIO_TO_DBCNT_REG(gpio) ((gpio * 4) + 0x80)
/* MEN Z127 supported model ids*/
#define MEN_Z127_ID 0x7f
#define MEN_Z034_ID 0x22
#define MEN_Z037_ID 0x25
#define MEN_Z127_DB_MIN_US 50 #define MEN_Z127_DB_MIN_US 50
/* 16 bit compare register. Each bit represents 50us */ /* 16 bit compare register. Each bit represents 50us */
#define MEN_Z127_DB_MAX_US (0xffff * MEN_Z127_DB_MIN_US) #define MEN_Z127_DB_MAX_US (0xffff * MEN_Z127_DB_MIN_US)
@ -140,6 +145,7 @@ static int men_z127_probe(struct mcb_device *mdev,
struct men_z127_gpio *men_z127_gpio; struct men_z127_gpio *men_z127_gpio;
struct device *dev = &mdev->dev; struct device *dev = &mdev->dev;
int ret; int ret;
unsigned long sz;
men_z127_gpio = devm_kzalloc(dev, sizeof(struct men_z127_gpio), men_z127_gpio = devm_kzalloc(dev, sizeof(struct men_z127_gpio),
GFP_KERNEL); GFP_KERNEL);
@ -163,9 +169,21 @@ static int men_z127_probe(struct mcb_device *mdev,
mcb_set_drvdata(mdev, men_z127_gpio); mcb_set_drvdata(mdev, men_z127_gpio);
switch (mdev->id) {
case MEN_Z127_ID:
sz = 4;
break;
case MEN_Z034_ID:
case MEN_Z037_ID:
sz = 1;
break;
default:
return dev_err_probe(&mdev->dev, -EINVAL, "no size found for id %d", mdev->id);
}
config = (struct gpio_generic_chip_config) { config = (struct gpio_generic_chip_config) {
.dev = &mdev->dev, .dev = &mdev->dev,
.sz = 4, .sz = sz,
.dat = men_z127_gpio->reg_base + MEN_Z127_PSR, .dat = men_z127_gpio->reg_base + MEN_Z127_PSR,
.set = men_z127_gpio->reg_base + MEN_Z127_CTRL, .set = men_z127_gpio->reg_base + MEN_Z127_CTRL,
.dirout = men_z127_gpio->reg_base + MEN_Z127_GPIODR, .dirout = men_z127_gpio->reg_base + MEN_Z127_GPIODR,
@ -186,7 +204,9 @@ static int men_z127_probe(struct mcb_device *mdev,
} }
static const struct mcb_device_id men_z127_ids[] = { static const struct mcb_device_id men_z127_ids[] = {
{ .device = 0x7f }, { .device = MEN_Z127_ID },
{ .device = MEN_Z034_ID },
{ .device = MEN_Z037_ID },
{ } { }
}; };
MODULE_DEVICE_TABLE(mcb, men_z127_ids); MODULE_DEVICE_TABLE(mcb, men_z127_ids);
@ -201,7 +221,7 @@ static struct mcb_driver men_z127_driver = {
module_mcb_driver(men_z127_driver); module_mcb_driver(men_z127_driver);
MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
MODULE_DESCRIPTION("MEN 16z127 GPIO Controller"); MODULE_DESCRIPTION("MEN GPIO Controller");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("mcb:16z127"); MODULE_ALIAS("mcb:16z127");
MODULE_IMPORT_NS("MCB"); MODULE_IMPORT_NS("MCB");

View File

@ -160,7 +160,7 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
/* /*
* Save register configuration and disable interrupts. * Save register configuration and disable interrupts.
*/ */
static void __maybe_unused ioh_gpio_save_reg_conf(struct ioh_gpio *chip) static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
{ {
int i; int i;
@ -186,7 +186,7 @@ static void __maybe_unused ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
/* /*
* This function restores the register configuration of the GPIO device. * This function restores the register configuration of the GPIO device.
*/ */
static void __maybe_unused ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
{ {
int i; int i;
@ -479,7 +479,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev,
return 0; return 0;
} }
static int __maybe_unused ioh_gpio_suspend(struct device *dev) static int ioh_gpio_suspend(struct device *dev)
{ {
struct ioh_gpio *chip = dev_get_drvdata(dev); struct ioh_gpio *chip = dev_get_drvdata(dev);
unsigned long flags; unsigned long flags;
@ -491,7 +491,7 @@ static int __maybe_unused ioh_gpio_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused ioh_gpio_resume(struct device *dev) static int ioh_gpio_resume(struct device *dev)
{ {
struct ioh_gpio *chip = dev_get_drvdata(dev); struct ioh_gpio *chip = dev_get_drvdata(dev);
unsigned long flags; unsigned long flags;
@ -505,7 +505,7 @@ static int __maybe_unused ioh_gpio_resume(struct device *dev)
return 0; return 0;
} }
static SIMPLE_DEV_PM_OPS(ioh_gpio_pm_ops, ioh_gpio_suspend, ioh_gpio_resume); static DEFINE_SIMPLE_DEV_PM_OPS(ioh_gpio_pm_ops, ioh_gpio_suspend, ioh_gpio_resume);
static const struct pci_device_id ioh_gpio_pcidev_id[] = { static const struct pci_device_id ioh_gpio_pcidev_id[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) }, { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) },
@ -518,7 +518,7 @@ static struct pci_driver ioh_gpio_driver = {
.id_table = ioh_gpio_pcidev_id, .id_table = ioh_gpio_pcidev_id,
.probe = ioh_gpio_probe, .probe = ioh_gpio_probe,
.driver = { .driver = {
.pm = &ioh_gpio_pm_ops, .pm = pm_sleep_ptr(&ioh_gpio_pm_ops),
}, },
}; };

View File

@ -424,7 +424,7 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev) static int mlxbf2_gpio_suspend(struct device *dev)
{ {
struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev); struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev);
@ -436,7 +436,7 @@ static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused mlxbf2_gpio_resume(struct device *dev) static int mlxbf2_gpio_resume(struct device *dev)
{ {
struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev); struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev);
@ -447,7 +447,7 @@ static int __maybe_unused mlxbf2_gpio_resume(struct device *dev)
return 0; return 0;
} }
static SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume); static DEFINE_SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume);
static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = { static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = {
{ "MLNXBF22", 0 }, { "MLNXBF22", 0 },
@ -459,7 +459,7 @@ static struct platform_driver mlxbf2_gpio_driver = {
.driver = { .driver = {
.name = "mlxbf2_gpio", .name = "mlxbf2_gpio",
.acpi_match_table = mlxbf2_gpio_acpi_match, .acpi_match_table = mlxbf2_gpio_acpi_match,
.pm = &mlxbf2_pm_ops, .pm = pm_sleep_ptr(&mlxbf2_pm_ops),
}, },
.probe = mlxbf2_gpio_probe, .probe = mlxbf2_gpio_probe,
}; };

View File

@ -10,7 +10,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/gpio/legacy-of-mm-gpiochip.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -27,7 +26,8 @@
#define LTQ_EBU_WP 0x80000000 /* write protect bit */ #define LTQ_EBU_WP 0x80000000 /* write protect bit */
struct ltq_mm { struct ltq_mm {
struct of_mm_gpio_chip mmchip; struct gpio_chip gc;
void __iomem *regs;
u16 shadow; /* shadow the latches state */ u16 shadow; /* shadow the latches state */
}; };
@ -44,7 +44,7 @@ static void ltq_mm_apply(struct ltq_mm *chip)
spin_lock_irqsave(&ebu_lock, flags); spin_lock_irqsave(&ebu_lock, flags);
ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1); ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1);
__raw_writew(chip->shadow, chip->mmchip.regs); __raw_writew(chip->shadow, chip->regs);
ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1); ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1);
spin_unlock_irqrestore(&ebu_lock, flags); spin_unlock_irqrestore(&ebu_lock, flags);
} }
@ -52,8 +52,8 @@ static void ltq_mm_apply(struct ltq_mm *chip)
/** /**
* ltq_mm_set() - gpio_chip->set - set gpios. * ltq_mm_set() - gpio_chip->set - set gpios.
* @gc: Pointer to gpio_chip device structure. * @gc: Pointer to gpio_chip device structure.
* @gpio: GPIO signal number. * @offset: GPIO signal number.
* @val: Value to be written to specified signal. * @value: Value to be written to specified signal.
* *
* Set the shadow value and call ltq_mm_apply. Always returns 0. * Set the shadow value and call ltq_mm_apply. Always returns 0.
*/ */
@ -73,8 +73,8 @@ static int ltq_mm_set(struct gpio_chip *gc, unsigned int offset, int value)
/** /**
* ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction. * ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction.
* @gc: Pointer to gpio_chip device structure. * @gc: Pointer to gpio_chip device structure.
* @gpio: GPIO signal number. * @offset: GPIO signal number.
* @val: Value to be written to specified signal. * @value: Value to be written to specified signal.
* *
* Same as ltq_mm_set, always returns 0. * Same as ltq_mm_set, always returns 0.
*/ */
@ -85,21 +85,21 @@ static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value)
/** /**
* ltq_mm_save_regs() - Set initial values of GPIO pins * ltq_mm_save_regs() - Set initial values of GPIO pins
* @mm_gc: pointer to memory mapped GPIO chip structure * @chip: Pointer to our private data structure.
*/ */
static void ltq_mm_save_regs(struct of_mm_gpio_chip *mm_gc) static void ltq_mm_save_regs(struct ltq_mm *chip)
{ {
struct ltq_mm *chip =
container_of(mm_gc, struct ltq_mm, mmchip);
/* tell the ebu controller which memory address we will be using */ /* tell the ebu controller which memory address we will be using */
ltq_ebu_w32(CPHYSADDR(chip->mmchip.regs) | 0x1, LTQ_EBU_ADDRSEL1); ltq_ebu_w32(CPHYSADDR((__force void *)chip->regs) | 0x1, LTQ_EBU_ADDRSEL1);
ltq_mm_apply(chip); ltq_mm_apply(chip);
} }
static int ltq_mm_probe(struct platform_device *pdev) static int ltq_mm_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct gpio_chip *gc;
struct ltq_mm *chip; struct ltq_mm *chip;
u32 shadow; u32 shadow;
@ -107,25 +107,29 @@ static int ltq_mm_probe(struct platform_device *pdev)
if (!chip) if (!chip)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, chip); gc = &chip->gc;
chip->mmchip.gc.ngpio = 16; gc->base = -1;
chip->mmchip.gc.direction_output = ltq_mm_dir_out; gc->ngpio = 16;
chip->mmchip.gc.set = ltq_mm_set; gc->direction_output = ltq_mm_dir_out;
chip->mmchip.save_regs = ltq_mm_save_regs; gc->set = ltq_mm_set;
gc->parent = dev;
gc->owner = THIS_MODULE;
gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np);
if (!gc->label)
return -ENOMEM;
chip->regs = devm_of_iomap(dev, np, 0, NULL);
if (IS_ERR(chip->regs))
return PTR_ERR(chip->regs);
ltq_mm_save_regs(chip);
/* store the shadow value if one was passed by the devicetree */ /* store the shadow value if one was passed by the devicetree */
if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow)) if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow))
chip->shadow = shadow; chip->shadow = shadow;
return of_mm_gpiochip_add_data(pdev->dev.of_node, &chip->mmchip, chip); return devm_gpiochip_add_data(dev, gc, chip);
}
static void ltq_mm_remove(struct platform_device *pdev)
{
struct ltq_mm *chip = platform_get_drvdata(pdev);
of_mm_gpiochip_remove(&chip->mmchip);
} }
static const struct of_device_id ltq_mm_match[] = { static const struct of_device_id ltq_mm_match[] = {
@ -136,7 +140,6 @@ MODULE_DEVICE_TABLE(of, ltq_mm_match);
static struct platform_driver ltq_mm_driver = { static struct platform_driver ltq_mm_driver = {
.probe = ltq_mm_probe, .probe = ltq_mm_probe,
.remove = ltq_mm_remove,
.driver = { .driver = {
.name = "gpio-mm-ltq", .name = "gpio-mm-ltq",
.of_match_table = ltq_mm_match, .of_match_table = ltq_mm_match,

View File

@ -41,6 +41,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
@ -61,69 +62,69 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include "gpiolib.h" #include "gpiolib.h"
static void bgpio_write8(void __iomem *reg, unsigned long data) static void gpio_mmio_write8(void __iomem *reg, unsigned long data)
{ {
writeb(data, reg); writeb(data, reg);
} }
static unsigned long bgpio_read8(void __iomem *reg) static unsigned long gpio_mmio_read8(void __iomem *reg)
{ {
return readb(reg); return readb(reg);
} }
static void bgpio_write16(void __iomem *reg, unsigned long data) static void gpio_mmio_write16(void __iomem *reg, unsigned long data)
{ {
writew(data, reg); writew(data, reg);
} }
static unsigned long bgpio_read16(void __iomem *reg) static unsigned long gpio_mmio_read16(void __iomem *reg)
{ {
return readw(reg); return readw(reg);
} }
static void bgpio_write32(void __iomem *reg, unsigned long data) static void gpio_mmio_write32(void __iomem *reg, unsigned long data)
{ {
writel(data, reg); writel(data, reg);
} }
static unsigned long bgpio_read32(void __iomem *reg) static unsigned long gpio_mmio_read32(void __iomem *reg)
{ {
return readl(reg); return readl(reg);
} }
#if BITS_PER_LONG >= 64 #if BITS_PER_LONG >= 64
static void bgpio_write64(void __iomem *reg, unsigned long data) static void gpio_mmio_write64(void __iomem *reg, unsigned long data)
{ {
writeq(data, reg); writeq(data, reg);
} }
static unsigned long bgpio_read64(void __iomem *reg) static unsigned long gpio_mmio_read64(void __iomem *reg)
{ {
return readq(reg); return readq(reg);
} }
#endif /* BITS_PER_LONG >= 64 */ #endif /* BITS_PER_LONG >= 64 */
static void bgpio_write16be(void __iomem *reg, unsigned long data) static void gpio_mmio_write16be(void __iomem *reg, unsigned long data)
{ {
iowrite16be(data, reg); iowrite16be(data, reg);
} }
static unsigned long bgpio_read16be(void __iomem *reg) static unsigned long gpio_mmio_read16be(void __iomem *reg)
{ {
return ioread16be(reg); return ioread16be(reg);
} }
static void bgpio_write32be(void __iomem *reg, unsigned long data) static void gpio_mmio_write32be(void __iomem *reg, unsigned long data)
{ {
iowrite32be(data, reg); iowrite32be(data, reg);
} }
static unsigned long bgpio_read32be(void __iomem *reg) static unsigned long gpio_mmio_read32be(void __iomem *reg)
{ {
return ioread32be(reg); return ioread32be(reg);
} }
static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int line) static unsigned long gpio_mmio_line2mask(struct gpio_chip *gc, unsigned int line)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
@ -132,10 +133,10 @@ static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int line)
return BIT(line); return BIT(line);
} }
static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio) static int gpio_mmio_get_set(struct gpio_chip *gc, unsigned int gpio)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long pinmask = bgpio_line2mask(gc, gpio); unsigned long pinmask = gpio_mmio_line2mask(gc, gpio);
bool dir = !!(chip->sdir & pinmask); bool dir = !!(chip->sdir & pinmask);
if (dir) if (dir)
@ -148,8 +149,8 @@ static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
* This assumes that the bits in the GPIO register are in native endianness. * This assumes that the bits in the GPIO register are in native endianness.
* We only assign the function pointer if we have that. * We only assign the function pointer if we have that.
*/ */
static int bgpio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask, static int gpio_mmio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits) unsigned long *bits)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long get_mask = 0, set_mask = 0; unsigned long get_mask = 0, set_mask = 0;
@ -168,18 +169,18 @@ static int bgpio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask,
return 0; return 0;
} }
static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) static int gpio_mmio_get(struct gpio_chip *gc, unsigned int gpio)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
return !!(chip->read_reg(chip->reg_dat) & bgpio_line2mask(gc, gpio)); return !!(chip->read_reg(chip->reg_dat) & gpio_mmio_line2mask(gc, gpio));
} }
/* /*
* This only works if the bits in the GPIO register are in native endianness. * This only works if the bits in the GPIO register are in native endianness.
*/ */
static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask, static int gpio_mmio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits) unsigned long *bits)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
@ -192,8 +193,8 @@ static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
/* /*
* With big endian mirrored bit order it becomes more tedious. * With big endian mirrored bit order it becomes more tedious.
*/ */
static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, static int gpio_mmio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits) unsigned long *bits)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long readmask = 0; unsigned long readmask = 0;
@ -205,7 +206,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
/* Create a mirrored mask */ /* Create a mirrored mask */
for_each_set_bit(bit, mask, gc->ngpio) for_each_set_bit(bit, mask, gc->ngpio)
readmask |= bgpio_line2mask(gc, bit); readmask |= gpio_mmio_line2mask(gc, bit);
/* Read the register */ /* Read the register */
val = chip->read_reg(chip->reg_dat) & readmask; val = chip->read_reg(chip->reg_dat) & readmask;
@ -215,23 +216,22 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
* in bit 0 ... line 31 in bit 31 for a 32bit register. * in bit 0 ... line 31 in bit 31 for a 32bit register.
*/ */
for_each_set_bit(bit, &val, gc->ngpio) for_each_set_bit(bit, &val, gc->ngpio)
*bits |= bgpio_line2mask(gc, bit); *bits |= gpio_mmio_line2mask(gc, bit);
return 0; return 0;
} }
static int bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) static int gpio_mmio_set_none(struct gpio_chip *gc, unsigned int gpio, int val)
{ {
return 0; return 0;
} }
static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) static int gpio_mmio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long mask = bgpio_line2mask(gc, gpio); unsigned long mask = gpio_mmio_line2mask(gc, gpio);
unsigned long flags;
raw_spin_lock_irqsave(&chip->lock, flags); guard(raw_spinlock)(&chip->lock);
if (val) if (val)
chip->sdata |= mask; chip->sdata |= mask;
@ -240,16 +240,14 @@ static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
chip->write_reg(chip->reg_dat, chip->sdata); chip->write_reg(chip->reg_dat, chip->sdata);
raw_spin_unlock_irqrestore(&chip->lock, flags);
return 0; return 0;
} }
static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, static int gpio_mmio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
int val) int val)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long mask = bgpio_line2mask(gc, gpio); unsigned long mask = gpio_mmio_line2mask(gc, gpio);
if (val) if (val)
chip->write_reg(chip->reg_set, mask); chip->write_reg(chip->reg_set, mask);
@ -259,12 +257,12 @@ static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
return 0; return 0;
} }
static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) static int gpio_mmio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long mask = bgpio_line2mask(gc, gpio), flags; unsigned long mask = gpio_mmio_line2mask(gc, gpio);
raw_spin_lock_irqsave(&chip->lock, flags); guard(raw_spinlock)(&chip->lock);
if (val) if (val)
chip->sdata |= mask; chip->sdata |= mask;
@ -273,15 +271,14 @@ static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
chip->write_reg(chip->reg_set, chip->sdata); chip->write_reg(chip->reg_set, chip->sdata);
raw_spin_unlock_irqrestore(&chip->lock, flags);
return 0; return 0;
} }
static void bgpio_multiple_get_masks(struct gpio_chip *gc, static void gpio_mmio_multiple_get_masks(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits, unsigned long *mask,
unsigned long *set_mask, unsigned long *bits,
unsigned long *clear_mask) unsigned long *set_mask,
unsigned long *clear_mask)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
int i; int i;
@ -291,60 +288,58 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc,
for_each_set_bit(i, mask, chip->bits) { for_each_set_bit(i, mask, chip->bits) {
if (test_bit(i, bits)) if (test_bit(i, bits))
*set_mask |= bgpio_line2mask(gc, i); *set_mask |= gpio_mmio_line2mask(gc, i);
else else
*clear_mask |= bgpio_line2mask(gc, i); *clear_mask |= gpio_mmio_line2mask(gc, i);
} }
} }
static void bgpio_set_multiple_single_reg(struct gpio_chip *gc, static void gpio_mmio_set_multiple_single_reg(struct gpio_chip *gc,
unsigned long *mask, unsigned long *mask,
unsigned long *bits, unsigned long *bits,
void __iomem *reg) void __iomem *reg)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long flags, set_mask, clear_mask; unsigned long set_mask, clear_mask;
raw_spin_lock_irqsave(&chip->lock, flags); guard(raw_spinlock)(&chip->lock);
bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
chip->sdata |= set_mask; chip->sdata |= set_mask;
chip->sdata &= ~clear_mask; chip->sdata &= ~clear_mask;
chip->write_reg(reg, chip->sdata); chip->write_reg(reg, chip->sdata);
raw_spin_unlock_irqrestore(&chip->lock, flags);
} }
static int bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, static int gpio_mmio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
bgpio_set_multiple_single_reg(gc, mask, bits, chip->reg_dat);
return 0;
}
static int bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits) unsigned long *bits)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
bgpio_set_multiple_single_reg(gc, mask, bits, chip->reg_set); gpio_mmio_set_multiple_single_reg(gc, mask, bits, chip->reg_dat);
return 0; return 0;
} }
static int bgpio_set_multiple_with_clear(struct gpio_chip *gc, static int gpio_mmio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
unsigned long *mask, unsigned long *bits)
unsigned long *bits) {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
gpio_mmio_set_multiple_single_reg(gc, mask, bits, chip->reg_set);
return 0;
}
static int gpio_mmio_set_multiple_with_clear(struct gpio_chip *gc,
unsigned long *mask,
unsigned long *bits)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long set_mask, clear_mask; unsigned long set_mask, clear_mask;
bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
if (set_mask) if (set_mask)
chip->write_reg(chip->reg_set, set_mask); chip->write_reg(chip->reg_set, set_mask);
@ -354,7 +349,8 @@ static int bgpio_set_multiple_with_clear(struct gpio_chip *gc,
return 0; return 0;
} }
static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out) static int gpio_mmio_dir_return(struct gpio_chip *gc, unsigned int gpio,
bool dir_out)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
@ -367,131 +363,125 @@ static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_ou
return pinctrl_gpio_direction_input(gc, gpio); return pinctrl_gpio_direction_input(gc, gpio);
} }
static int bgpio_dir_in_err(struct gpio_chip *gc, unsigned int gpio) static int gpio_mmio_dir_in_err(struct gpio_chip *gc, unsigned int gpio)
{ {
return -EINVAL; return -EINVAL;
} }
static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) static int gpio_mmio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
{ {
return bgpio_dir_return(gc, gpio, false); return gpio_mmio_dir_return(gc, gpio, false);
} }
static int bgpio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio,
int val) int val)
{ {
return -EINVAL; return -EINVAL;
} }
static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, static int gpio_mmio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
int val) int val)
{ {
gc->set(gc, gpio, val); gc->set(gc, gpio, val);
return bgpio_dir_return(gc, gpio, true); return gpio_mmio_dir_return(gc, gpio, true);
} }
static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) static int gpio_mmio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long flags;
raw_spin_lock_irqsave(&chip->lock, flags); scoped_guard(raw_spinlock, &chip->lock) {
chip->sdir &= ~gpio_mmio_line2mask(gc, gpio);
chip->sdir &= ~bgpio_line2mask(gc, gpio); 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);
}
if (chip->reg_dir_in) return gpio_mmio_dir_return(gc, gpio, false);
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(&chip->lock, flags);
return bgpio_dir_return(gc, gpio, false);
} }
static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) static int gpio_mmio_get_dir(struct gpio_chip *gc, unsigned int gpio)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
/* Return 0 if output, 1 if input */ /* Return 0 if output, 1 if input */
if (chip->dir_unreadable) { if (chip->dir_unreadable) {
if (chip->sdir & bgpio_line2mask(gc, gpio)) if (chip->sdir & gpio_mmio_line2mask(gc, gpio))
return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_IN;
} }
if (chip->reg_dir_out) { if (chip->reg_dir_out) {
if (chip->read_reg(chip->reg_dir_out) & bgpio_line2mask(gc, gpio)) if (chip->read_reg(chip->reg_dir_out) & gpio_mmio_line2mask(gc, gpio))
return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_IN;
} }
if (chip->reg_dir_in) if (chip->reg_dir_in)
if (!(chip->read_reg(chip->reg_dir_in) & bgpio_line2mask(gc, gpio))) if (!(chip->read_reg(chip->reg_dir_in) & gpio_mmio_line2mask(gc, gpio)))
return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_IN;
} }
static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) static void gpio_mmio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long flags;
raw_spin_lock_irqsave(&chip->lock, flags); guard(raw_spinlock)(&chip->lock);
chip->sdir |= bgpio_line2mask(gc, gpio); chip->sdir |= gpio_mmio_line2mask(gc, gpio);
if (chip->reg_dir_in) if (chip->reg_dir_in)
chip->write_reg(chip->reg_dir_in, ~chip->sdir); chip->write_reg(chip->reg_dir_in, ~chip->sdir);
if (chip->reg_dir_out) if (chip->reg_dir_out)
chip->write_reg(chip->reg_dir_out, chip->sdir); chip->write_reg(chip->reg_dir_out, chip->sdir);
raw_spin_unlock_irqrestore(&chip->lock, flags);
} }
static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, static int gpio_mmio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
int val) int val)
{ {
bgpio_dir_out(gc, gpio, val); gpio_mmio_dir_out(gc, gpio, val);
gc->set(gc, gpio, val); gc->set(gc, gpio, val);
return bgpio_dir_return(gc, gpio, true); return gpio_mmio_dir_return(gc, gpio, true);
} }
static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, static int gpio_mmio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
int val) int val)
{ {
gc->set(gc, gpio, val); gc->set(gc, gpio, val);
bgpio_dir_out(gc, gpio, val); gpio_mmio_dir_out(gc, gpio, val);
return bgpio_dir_return(gc, gpio, true); return gpio_mmio_dir_return(gc, gpio, true);
} }
static int bgpio_setup_accessors(struct device *dev, static int gpio_mmio_setup_accessors(struct device *dev,
struct gpio_generic_chip *chip, struct gpio_generic_chip *chip,
bool byte_be) bool byte_be)
{ {
switch (chip->bits) { switch (chip->bits) {
case 8: case 8:
chip->read_reg = bgpio_read8; chip->read_reg = gpio_mmio_read8;
chip->write_reg = bgpio_write8; chip->write_reg = gpio_mmio_write8;
break; break;
case 16: case 16:
if (byte_be) { if (byte_be) {
chip->read_reg = bgpio_read16be; chip->read_reg = gpio_mmio_read16be;
chip->write_reg = bgpio_write16be; chip->write_reg = gpio_mmio_write16be;
} else { } else {
chip->read_reg = bgpio_read16; chip->read_reg = gpio_mmio_read16;
chip->write_reg = bgpio_write16; chip->write_reg = gpio_mmio_write16;
} }
break; break;
case 32: case 32:
if (byte_be) { if (byte_be) {
chip->read_reg = bgpio_read32be; chip->read_reg = gpio_mmio_read32be;
chip->write_reg = bgpio_write32be; chip->write_reg = gpio_mmio_write32be;
} else { } else {
chip->read_reg = bgpio_read32; chip->read_reg = gpio_mmio_read32;
chip->write_reg = bgpio_write32; chip->write_reg = gpio_mmio_write32;
} }
break; break;
#if BITS_PER_LONG >= 64 #if BITS_PER_LONG >= 64
@ -501,8 +491,8 @@ static int bgpio_setup_accessors(struct device *dev,
"64 bit big endian byte order unsupported\n"); "64 bit big endian byte order unsupported\n");
return -EINVAL; return -EINVAL;
} else { } else {
chip->read_reg = bgpio_read64; chip->read_reg = gpio_mmio_read64;
chip->write_reg = bgpio_write64; chip->write_reg = gpio_mmio_write64;
} }
break; break;
#endif /* BITS_PER_LONG >= 64 */ #endif /* BITS_PER_LONG >= 64 */
@ -536,8 +526,8 @@ static int bgpio_setup_accessors(struct device *dev,
* - an input direction register (named "dirin") where a 1 bit indicates * - an input direction register (named "dirin") where a 1 bit indicates
* the GPIO is an input. * the GPIO is an input.
*/ */
static int bgpio_setup_io(struct gpio_generic_chip *chip, static int gpio_mmio_setup_io(struct gpio_generic_chip *chip,
const struct gpio_generic_chip_config *cfg) const struct gpio_generic_chip_config *cfg)
{ {
struct gpio_chip *gc = &chip->gc; struct gpio_chip *gc = &chip->gc;
@ -548,25 +538,25 @@ static int bgpio_setup_io(struct gpio_generic_chip *chip,
if (cfg->set && cfg->clr) { if (cfg->set && cfg->clr) {
chip->reg_set = cfg->set; chip->reg_set = cfg->set;
chip->reg_clr = cfg->clr; chip->reg_clr = cfg->clr;
gc->set = bgpio_set_with_clear; gc->set = gpio_mmio_set_with_clear;
gc->set_multiple = bgpio_set_multiple_with_clear; gc->set_multiple = gpio_mmio_set_multiple_with_clear;
} else if (cfg->set && !cfg->clr) { } else if (cfg->set && !cfg->clr) {
chip->reg_set = cfg->set; chip->reg_set = cfg->set;
gc->set = bgpio_set_set; gc->set = gpio_mmio_set_set;
gc->set_multiple = bgpio_set_multiple_set; gc->set_multiple = gpio_mmio_set_multiple_set;
} else if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) { } else if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) {
gc->set = bgpio_set_none; gc->set = gpio_mmio_set_none;
gc->set_multiple = NULL; gc->set_multiple = NULL;
} else { } else {
gc->set = bgpio_set; gc->set = gpio_mmio_set;
gc->set_multiple = bgpio_set_multiple; gc->set_multiple = gpio_mmio_set_multiple;
} }
if (!(cfg->flags & GPIO_GENERIC_UNREADABLE_REG_SET) && if (!(cfg->flags & GPIO_GENERIC_UNREADABLE_REG_SET) &&
(cfg->flags & GPIO_GENERIC_READ_OUTPUT_REG_SET)) { (cfg->flags & GPIO_GENERIC_READ_OUTPUT_REG_SET)) {
gc->get = bgpio_get_set; gc->get = gpio_mmio_get_set;
if (!chip->be_bits) if (!chip->be_bits)
gc->get_multiple = bgpio_get_set_multiple; gc->get_multiple = gpio_mmio_get_set_multiple;
/* /*
* We deliberately avoid assigning the ->get_multiple() call * We deliberately avoid assigning the ->get_multiple() call
* for big endian mirrored registers which are ALSO reflecting * for big endian mirrored registers which are ALSO reflecting
@ -575,18 +565,18 @@ static int bgpio_setup_io(struct gpio_generic_chip *chip,
* reading each line individually in that fringe case. * reading each line individually in that fringe case.
*/ */
} else { } else {
gc->get = bgpio_get; gc->get = gpio_mmio_get;
if (chip->be_bits) if (chip->be_bits)
gc->get_multiple = bgpio_get_multiple_be; gc->get_multiple = gpio_mmio_get_multiple_be;
else else
gc->get_multiple = bgpio_get_multiple; gc->get_multiple = gpio_mmio_get_multiple;
} }
return 0; return 0;
} }
static int bgpio_setup_direction(struct gpio_generic_chip *chip, static int gpio_mmio_setup_direction(struct gpio_generic_chip *chip,
const struct gpio_generic_chip_config *cfg) const struct gpio_generic_chip_config *cfg)
{ {
struct gpio_chip *gc = &chip->gc; struct gpio_chip *gc = &chip->gc;
@ -594,27 +584,27 @@ static int bgpio_setup_direction(struct gpio_generic_chip *chip,
chip->reg_dir_out = cfg->dirout; chip->reg_dir_out = cfg->dirout;
chip->reg_dir_in = cfg->dirin; chip->reg_dir_in = cfg->dirin;
if (cfg->flags & GPIO_GENERIC_NO_SET_ON_INPUT) if (cfg->flags & GPIO_GENERIC_NO_SET_ON_INPUT)
gc->direction_output = bgpio_dir_out_dir_first; gc->direction_output = gpio_mmio_dir_out_dir_first;
else else
gc->direction_output = bgpio_dir_out_val_first; gc->direction_output = gpio_mmio_dir_out_val_first;
gc->direction_input = bgpio_dir_in; gc->direction_input = gpio_mmio_dir_in;
gc->get_direction = bgpio_get_dir; gc->get_direction = gpio_mmio_get_dir;
} else { } else {
if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) if (cfg->flags & GPIO_GENERIC_NO_OUTPUT)
gc->direction_output = bgpio_dir_out_err; gc->direction_output = gpio_mmio_dir_out_err;
else else
gc->direction_output = bgpio_simple_dir_out; gc->direction_output = gpio_mmio_simple_dir_out;
if (cfg->flags & GPIO_GENERIC_NO_INPUT) if (cfg->flags & GPIO_GENERIC_NO_INPUT)
gc->direction_input = bgpio_dir_in_err; gc->direction_input = gpio_mmio_dir_in_err;
else else
gc->direction_input = bgpio_simple_dir_in; gc->direction_input = gpio_mmio_simple_dir_in;
} }
return 0; return 0;
} }
static int bgpio_request(struct gpio_chip *gc, unsigned int gpio_pin) static int gpio_mmio_request(struct gpio_chip *gc, unsigned int gpio_pin)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
@ -653,23 +643,23 @@ int gpio_generic_chip_init(struct gpio_generic_chip *chip,
gc->parent = dev; gc->parent = dev;
gc->label = dev_name(dev); gc->label = dev_name(dev);
gc->base = -1; gc->base = -1;
gc->request = bgpio_request; gc->request = gpio_mmio_request;
chip->be_bits = !!(flags & GPIO_GENERIC_BIG_ENDIAN); chip->be_bits = !!(flags & GPIO_GENERIC_BIG_ENDIAN);
ret = gpiochip_get_ngpios(gc, dev); ret = gpiochip_get_ngpios(gc, dev);
if (ret) if (ret)
gc->ngpio = chip->bits; gc->ngpio = chip->bits;
ret = bgpio_setup_io(chip, cfg); ret = gpio_mmio_setup_io(chip, cfg);
if (ret) if (ret)
return ret; return ret;
ret = bgpio_setup_accessors(dev, chip, ret = gpio_mmio_setup_accessors(dev, chip,
flags & GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER); flags & GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER);
if (ret) if (ret)
return ret; return ret;
ret = bgpio_setup_direction(chip, cfg); ret = gpio_mmio_setup_direction(chip, cfg);
if (ret) if (ret)
return ret; return ret;
@ -680,7 +670,7 @@ int gpio_generic_chip_init(struct gpio_generic_chip *chip,
} }
chip->sdata = chip->read_reg(chip->reg_dat); chip->sdata = chip->read_reg(chip->reg_dat);
if (gc->set == bgpio_set_set && if (gc->set == gpio_mmio_set_set &&
!(flags & GPIO_GENERIC_UNREADABLE_REG_SET)) !(flags & GPIO_GENERIC_UNREADABLE_REG_SET))
chip->sdata = chip->read_reg(chip->reg_set); chip->sdata = chip->read_reg(chip->reg_set);
@ -712,9 +702,8 @@ EXPORT_SYMBOL_GPL(gpio_generic_chip_init);
#if IS_ENABLED(CONFIG_GPIO_GENERIC_PLATFORM) #if IS_ENABLED(CONFIG_GPIO_GENERIC_PLATFORM)
static void __iomem *bgpio_map(struct platform_device *pdev, static void __iomem *gpio_mmio_map(struct platform_device *pdev,
const char *name, const char *name, resource_size_t sane_sz)
resource_size_t sane_sz)
{ {
struct resource *r; struct resource *r;
resource_size_t sz; resource_size_t sz;
@ -730,16 +719,16 @@ static void __iomem *bgpio_map(struct platform_device *pdev,
return devm_ioremap_resource(&pdev->dev, r); return devm_ioremap_resource(&pdev->dev, r);
} }
static const struct of_device_id bgpio_of_match[] = { static const struct of_device_id gpio_mmio_of_match[] = {
{ .compatible = "brcm,bcm6345-gpio" }, { .compatible = "brcm,bcm6345-gpio" },
{ .compatible = "wd,mbl-gpio" }, { .compatible = "wd,mbl-gpio" },
{ .compatible = "ni,169445-nand-gpio" }, { .compatible = "ni,169445-nand-gpio" },
{ .compatible = "intel,ixp4xx-expansion-bus-mmio-gpio" }, { .compatible = "intel,ixp4xx-expansion-bus-mmio-gpio" },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, bgpio_of_match); MODULE_DEVICE_TABLE(of, gpio_mmio_of_match);
static int bgpio_pdev_probe(struct platform_device *pdev) static int gpio_mmio_pdev_probe(struct platform_device *pdev)
{ {
struct gpio_generic_chip_config config; struct gpio_generic_chip_config config;
struct gpio_generic_chip *gen_gc; struct gpio_generic_chip *gen_gc;
@ -762,23 +751,23 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
sz = resource_size(r); sz = resource_size(r);
dat = bgpio_map(pdev, "dat", sz); dat = gpio_mmio_map(pdev, "dat", sz);
if (IS_ERR(dat)) if (IS_ERR(dat))
return PTR_ERR(dat); return PTR_ERR(dat);
set = bgpio_map(pdev, "set", sz); set = gpio_mmio_map(pdev, "set", sz);
if (IS_ERR(set)) if (IS_ERR(set))
return PTR_ERR(set); return PTR_ERR(set);
clr = bgpio_map(pdev, "clr", sz); clr = gpio_mmio_map(pdev, "clr", sz);
if (IS_ERR(clr)) if (IS_ERR(clr))
return PTR_ERR(clr); return PTR_ERR(clr);
dirout = bgpio_map(pdev, "dirout", sz); dirout = gpio_mmio_map(pdev, "dirout", sz);
if (IS_ERR(dirout)) if (IS_ERR(dirout))
return PTR_ERR(dirout); return PTR_ERR(dirout);
dirin = bgpio_map(pdev, "dirin", sz); dirin = gpio_mmio_map(pdev, "dirin", sz);
if (IS_ERR(dirin)) if (IS_ERR(dirin))
return PTR_ERR(dirin); return PTR_ERR(dirin);
@ -824,25 +813,25 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
return devm_gpiochip_add_data(&pdev->dev, &gen_gc->gc, NULL); return devm_gpiochip_add_data(&pdev->dev, &gen_gc->gc, NULL);
} }
static const struct platform_device_id bgpio_id_table[] = { static const struct platform_device_id gpio_mmio_id_table[] = {
{ {
.name = "basic-mmio-gpio", .name = "basic-mmio-gpio",
.driver_data = 0, .driver_data = 0,
}, },
{ } { }
}; };
MODULE_DEVICE_TABLE(platform, bgpio_id_table); MODULE_DEVICE_TABLE(platform, gpio_mmio_id_table);
static struct platform_driver bgpio_driver = { static struct platform_driver gpio_mmio_driver = {
.driver = { .driver = {
.name = "basic-mmio-gpio", .name = "basic-mmio-gpio",
.of_match_table = bgpio_of_match, .of_match_table = gpio_mmio_of_match,
}, },
.id_table = bgpio_id_table, .id_table = gpio_mmio_id_table,
.probe = bgpio_pdev_probe, .probe = gpio_mmio_pdev_probe,
}; };
module_platform_driver(bgpio_driver); module_platform_driver(gpio_mmio_driver);
#endif /* CONFIG_GPIO_GENERIC_PLATFORM */ #endif /* CONFIG_GPIO_GENERIC_PLATFORM */

View File

@ -10,6 +10,7 @@
#include <linux/cleanup.h> #include <linux/cleanup.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/usb.h> #include <linux/usb.h>
struct mpsse_priv { struct mpsse_priv {
@ -17,8 +18,10 @@ struct mpsse_priv {
struct usb_device *udev; /* USB device encompassing all MPSSEs */ struct usb_device *udev; /* USB device encompassing all MPSSEs */
struct usb_interface *intf; /* USB interface for this MPSSE */ struct usb_interface *intf; /* USB interface for this MPSSE */
u8 intf_id; /* USB interface number for this MPSSE */ u8 intf_id; /* USB interface number for this MPSSE */
struct work_struct irq_work; /* polling work thread */ struct list_head workers; /* polling work threads */
struct mutex irq_mutex; /* lock over irq_data */ struct mutex irq_mutex; /* lock over irq_data */
struct mutex irq_race; /* race for polling worker teardown */
raw_spinlock_t irq_spin; /* protects worker list */
atomic_t irq_type[16]; /* pin -> edge detection type */ atomic_t irq_type[16]; /* pin -> edge detection type */
atomic_t irq_enabled; atomic_t irq_enabled;
int id; int id;
@ -26,6 +29,9 @@ struct mpsse_priv {
u8 gpio_outputs[2]; /* Output states for GPIOs [L, H] */ u8 gpio_outputs[2]; /* Output states for GPIOs [L, H] */
u8 gpio_dir[2]; /* Directions for GPIOs [L, H] */ u8 gpio_dir[2]; /* Directions for GPIOs [L, H] */
unsigned long dir_in; /* Bitmask of valid input pins */
unsigned long dir_out; /* Bitmask of valid output pins */
u8 *bulk_in_buf; /* Extra recv buffer to grab status bytes */ u8 *bulk_in_buf; /* Extra recv buffer to grab status bytes */
struct usb_endpoint_descriptor *bulk_in; struct usb_endpoint_descriptor *bulk_in;
@ -34,6 +40,14 @@ struct mpsse_priv {
struct mutex io_mutex; /* sync I/O with disconnect */ struct mutex io_mutex; /* sync I/O with disconnect */
}; };
struct mpsse_worker {
struct mpsse_priv *priv;
struct work_struct work;
atomic_t cancelled;
struct list_head list; /* linked list */
struct list_head destroy; /* teardown linked list */
};
struct bulk_desc { struct bulk_desc {
bool tx; /* direction of bulk transfer */ bool tx; /* direction of bulk transfer */
u8 *data; /* input (tx) or output (rx) */ u8 *data; /* input (tx) or output (rx) */
@ -43,8 +57,27 @@ struct bulk_desc {
int timeout; int timeout;
}; };
#define MPSSE_NGPIO 16
struct mpsse_quirk {
const char *names[MPSSE_NGPIO]; /* Pin names, if applicable */
unsigned long dir_in; /* Bitmask of valid input pins */
unsigned long dir_out; /* Bitmask of valid output pins */
};
static struct mpsse_quirk bryx_brik_quirk = {
.names = {
[3] = "Push to Talk",
[5] = "Channel Activity",
},
.dir_out = BIT(3), /* Push to Talk */
.dir_in = BIT(5), /* Channel Activity */
};
static const struct usb_device_id gpio_mpsse_table[] = { static const struct usb_device_id gpio_mpsse_table[] = {
{ USB_DEVICE(0x0c52, 0xa064) }, /* SeaLevel Systems, Inc. */ { USB_DEVICE(0x0c52, 0xa064) }, /* SeaLevel Systems, Inc. */
{ USB_DEVICE(0x0403, 0x6988), /* FTDI, assigned to Bryx */
.driver_info = (kernel_ulong_t)&bryx_brik_quirk},
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
@ -160,6 +193,32 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank)
return buf; return buf;
} }
static int mpsse_ensure_supported(struct gpio_chip *chip,
unsigned long mask, int direction)
{
unsigned long supported, unsupported;
char *type = "input";
struct mpsse_priv *priv = gpiochip_get_data(chip);
supported = priv->dir_in;
if (direction == GPIO_LINE_DIRECTION_OUT) {
supported = priv->dir_out;
type = "output";
}
/* An invalid bit was in the provided mask */
unsupported = mask & ~supported;
if (unsupported) {
dev_err(&priv->udev->dev,
"mpsse: GPIO %lu doesn't support %s\n",
find_first_bit(&unsupported, sizeof(unsupported) * 8),
type);
return -EOPNOTSUPP;
}
return 0;
}
static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits) unsigned long *bits)
{ {
@ -167,6 +226,10 @@ static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask,
int ret; int ret;
struct mpsse_priv *priv = gpiochip_get_data(chip); struct mpsse_priv *priv = gpiochip_get_data(chip);
ret = mpsse_ensure_supported(chip, *mask, GPIO_LINE_DIRECTION_OUT);
if (ret)
return ret;
guard(mutex)(&priv->io_mutex); guard(mutex)(&priv->io_mutex);
for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { for_each_set_clump8(i, bank_mask, mask, chip->ngpio) {
bank = i / 8; bank = i / 8;
@ -194,6 +257,10 @@ static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask,
int ret; int ret;
struct mpsse_priv *priv = gpiochip_get_data(chip); struct mpsse_priv *priv = gpiochip_get_data(chip);
ret = mpsse_ensure_supported(chip, *mask, GPIO_LINE_DIRECTION_IN);
if (ret)
return ret;
guard(mutex)(&priv->io_mutex); guard(mutex)(&priv->io_mutex);
for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { for_each_set_clump8(i, bank_mask, mask, chip->ngpio) {
bank = i / 8; bank = i / 8;
@ -242,10 +309,15 @@ static int gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset,
static int gpio_mpsse_direction_output(struct gpio_chip *chip, static int gpio_mpsse_direction_output(struct gpio_chip *chip,
unsigned int offset, int value) unsigned int offset, int value)
{ {
int ret;
struct mpsse_priv *priv = gpiochip_get_data(chip); struct mpsse_priv *priv = gpiochip_get_data(chip);
int bank = (offset & 8) >> 3; int bank = (offset & 8) >> 3;
int bank_offset = offset & 7; int bank_offset = offset & 7;
ret = mpsse_ensure_supported(chip, BIT(offset), GPIO_LINE_DIRECTION_OUT);
if (ret)
return ret;
scoped_guard(mutex, &priv->io_mutex) scoped_guard(mutex, &priv->io_mutex)
priv->gpio_dir[bank] |= BIT(bank_offset); priv->gpio_dir[bank] |= BIT(bank_offset);
@ -255,15 +327,19 @@ static int gpio_mpsse_direction_output(struct gpio_chip *chip,
static int gpio_mpsse_direction_input(struct gpio_chip *chip, static int gpio_mpsse_direction_input(struct gpio_chip *chip,
unsigned int offset) unsigned int offset)
{ {
int ret;
struct mpsse_priv *priv = gpiochip_get_data(chip); struct mpsse_priv *priv = gpiochip_get_data(chip);
int bank = (offset & 8) >> 3; int bank = (offset & 8) >> 3;
int bank_offset = offset & 7; int bank_offset = offset & 7;
ret = mpsse_ensure_supported(chip, BIT(offset), GPIO_LINE_DIRECTION_IN);
if (ret)
return ret;
guard(mutex)(&priv->io_mutex); guard(mutex)(&priv->io_mutex);
priv->gpio_dir[bank] &= ~BIT(bank_offset); priv->gpio_dir[bank] &= ~BIT(bank_offset);
gpio_mpsse_set_bank(priv, bank);
return 0; return gpio_mpsse_set_bank(priv, bank);
} }
static int gpio_mpsse_get_direction(struct gpio_chip *chip, static int gpio_mpsse_get_direction(struct gpio_chip *chip,
@ -284,18 +360,62 @@ static int gpio_mpsse_get_direction(struct gpio_chip *chip,
return ret; return ret;
} }
static void gpio_mpsse_poll(struct work_struct *work) /*
* Stops all workers except `my_worker`.
* Safe to call only when `irq_race` is held.
*/
static void gpio_mpsse_stop_all_except(struct mpsse_priv *priv,
struct mpsse_worker *my_worker)
{
struct mpsse_worker *worker, *worker_tmp;
struct list_head destructors = LIST_HEAD_INIT(destructors);
scoped_guard(raw_spinlock_irqsave, &priv->irq_spin) {
list_for_each_entry_safe(worker, worker_tmp,
&priv->workers, list) {
/* Don't stop ourselves */
if (worker == my_worker)
continue;
list_del(&worker->list);
/* Give worker a chance to terminate itself */
atomic_set(&worker->cancelled, 1);
/* Keep track of stuff to cancel */
INIT_LIST_HEAD(&worker->destroy);
list_add(&worker->destroy, &destructors);
}
}
list_for_each_entry_safe(worker, worker_tmp,
&destructors, destroy) {
list_del(&worker->destroy);
cancel_work_sync(&worker->work);
kfree(worker);
}
}
static void gpio_mpsse_poll(struct work_struct *my_work)
{ {
unsigned long pin_mask, pin_states, flags; unsigned long pin_mask, pin_states, flags;
int irq_enabled, offset, err, value, fire_irq, int irq_enabled, offset, err, value, fire_irq,
irq, old_value[16], irq_type[16]; irq, old_value[16], irq_type[16];
struct mpsse_priv *priv = container_of(work, struct mpsse_priv, struct mpsse_worker *my_worker = container_of(my_work, struct mpsse_worker, work);
irq_work); struct mpsse_priv *priv = my_worker->priv;
for (offset = 0; offset < priv->gpio.ngpio; ++offset) for (offset = 0; offset < priv->gpio.ngpio; ++offset)
old_value[offset] = -1; old_value[offset] = -1;
while ((irq_enabled = atomic_read(&priv->irq_enabled))) { /*
* We only want one worker. Workers race to acquire irq_race and tear
* down all other workers. This is a cond guard so that we don't deadlock
* trying to cancel a worker.
*/
scoped_cond_guard(mutex_try, return, &priv->irq_race)
gpio_mpsse_stop_all_except(priv, my_worker);
while ((irq_enabled = atomic_read(&priv->irq_enabled)) &&
!atomic_read(&my_worker->cancelled)) {
usleep_range(MPSSE_POLL_INTERVAL, MPSSE_POLL_INTERVAL + 1000); usleep_range(MPSSE_POLL_INTERVAL, MPSSE_POLL_INTERVAL + 1000);
/* Cleanup will trigger at the end of the loop */ /* Cleanup will trigger at the end of the loop */
guard(mutex)(&priv->irq_mutex); guard(mutex)(&priv->irq_mutex);
@ -370,21 +490,45 @@ static int gpio_mpsse_set_irq_type(struct irq_data *irqd, unsigned int type)
static void gpio_mpsse_irq_disable(struct irq_data *irqd) static void gpio_mpsse_irq_disable(struct irq_data *irqd)
{ {
struct mpsse_worker *worker;
struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd);
atomic_and(~BIT(irqd->hwirq), &priv->irq_enabled); atomic_and(~BIT(irqd->hwirq), &priv->irq_enabled);
gpiochip_disable_irq(&priv->gpio, irqd->hwirq); gpiochip_disable_irq(&priv->gpio, irqd->hwirq);
/*
* Can't actually do teardown in IRQ context (it blocks).
* As a result, these workers will stick around until irq is reenabled
* or device gets disconnected
*/
scoped_guard(raw_spinlock_irqsave, &priv->irq_spin)
list_for_each_entry(worker, &priv->workers, list)
atomic_set(&worker->cancelled, 1);
} }
static void gpio_mpsse_irq_enable(struct irq_data *irqd) static void gpio_mpsse_irq_enable(struct irq_data *irqd)
{ {
struct mpsse_worker *worker;
struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd);
gpiochip_enable_irq(&priv->gpio, irqd->hwirq); gpiochip_enable_irq(&priv->gpio, irqd->hwirq);
/* If no-one else was using the IRQ, enable it */ /* If no-one else was using the IRQ, enable it */
if (!atomic_fetch_or(BIT(irqd->hwirq), &priv->irq_enabled)) { if (!atomic_fetch_or(BIT(irqd->hwirq), &priv->irq_enabled)) {
INIT_WORK(&priv->irq_work, gpio_mpsse_poll); /*
schedule_work(&priv->irq_work); * Can't be devm because it uses a non-raw spinlock (illegal in
* this context, where a raw spinlock is held by our caller)
*/
worker = kzalloc(sizeof(*worker), GFP_NOWAIT);
if (!worker)
return;
worker->priv = priv;
INIT_LIST_HEAD(&worker->list);
INIT_WORK(&worker->work, gpio_mpsse_poll);
schedule_work(&worker->work);
scoped_guard(raw_spinlock_irqsave, &priv->irq_spin)
list_add(&worker->list, &priv->workers);
} }
} }
@ -404,18 +548,49 @@ static void gpio_mpsse_ida_remove(void *data)
ida_free(&gpio_mpsse_ida, priv->id); ida_free(&gpio_mpsse_ida, priv->id);
} }
static int mpsse_init_valid_mask(struct gpio_chip *chip,
unsigned long *valid_mask,
unsigned int ngpios)
{
struct mpsse_priv *priv = gpiochip_get_data(chip);
if (WARN_ON(priv == NULL))
return -ENODEV;
*valid_mask = priv->dir_in | priv->dir_out;
return 0;
}
static void mpsse_irq_init_valid_mask(struct gpio_chip *chip,
unsigned long *valid_mask,
unsigned int ngpios)
{
struct mpsse_priv *priv = gpiochip_get_data(chip);
if (WARN_ON(priv == NULL))
return;
/* Can only use IRQ on input capable pins */
*valid_mask = priv->dir_in;
}
static int gpio_mpsse_probe(struct usb_interface *interface, static int gpio_mpsse_probe(struct usb_interface *interface,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct mpsse_priv *priv; struct mpsse_priv *priv;
struct device *dev; struct device *dev;
char *serial;
int err; int err;
struct mpsse_quirk *quirk = (void *)id->driver_info;
dev = &interface->dev; dev = &interface->dev;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&priv->workers);
priv->udev = usb_get_dev(interface_to_usbdev(interface)); priv->udev = usb_get_dev(interface_to_usbdev(interface));
priv->intf = interface; priv->intf = interface;
priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber; priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber;
@ -436,9 +611,21 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
if (err) if (err)
return err; return err;
err = devm_mutex_init(dev, &priv->irq_race);
if (err)
return err;
raw_spin_lock_init(&priv->irq_spin);
serial = priv->udev->serial;
if (!serial)
serial = "NONE";
priv->gpio.label = devm_kasprintf(dev, GFP_KERNEL, priv->gpio.label = devm_kasprintf(dev, GFP_KERNEL,
"gpio-mpsse.%d.%d", "MPSSE%04x:%04x.%d.%d.%s",
priv->id, priv->intf_id); id->idVendor, id->idProduct,
priv->intf_id, priv->id,
serial);
if (!priv->gpio.label) if (!priv->gpio.label)
return -ENOMEM; return -ENOMEM;
@ -452,10 +639,20 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
priv->gpio.get_multiple = gpio_mpsse_get_multiple; priv->gpio.get_multiple = gpio_mpsse_get_multiple;
priv->gpio.set_multiple = gpio_mpsse_set_multiple; priv->gpio.set_multiple = gpio_mpsse_set_multiple;
priv->gpio.base = -1; priv->gpio.base = -1;
priv->gpio.ngpio = 16; priv->gpio.ngpio = MPSSE_NGPIO;
priv->gpio.offset = priv->intf_id * priv->gpio.ngpio; priv->gpio.offset = priv->intf_id * priv->gpio.ngpio;
priv->gpio.can_sleep = 1; priv->gpio.can_sleep = 1;
if (quirk) {
priv->dir_out = quirk->dir_out;
priv->dir_in = quirk->dir_in;
priv->gpio.names = quirk->names;
priv->gpio.init_valid_mask = mpsse_init_valid_mask;
} else {
priv->dir_in = U16_MAX;
priv->dir_out = U16_MAX;
}
err = usb_find_common_endpoints(interface->cur_altsetting, err = usb_find_common_endpoints(interface->cur_altsetting,
&priv->bulk_in, &priv->bulk_out, &priv->bulk_in, &priv->bulk_out,
NULL, NULL); NULL, NULL);
@ -494,6 +691,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
priv->gpio.irq.parents = NULL; priv->gpio.irq.parents = NULL;
priv->gpio.irq.default_type = IRQ_TYPE_NONE; priv->gpio.irq.default_type = IRQ_TYPE_NONE;
priv->gpio.irq.handler = handle_simple_irq; priv->gpio.irq.handler = handle_simple_irq;
priv->gpio.irq.init_valid_mask = mpsse_irq_init_valid_mask;
err = devm_gpiochip_add_data(dev, &priv->gpio, priv); err = devm_gpiochip_add_data(dev, &priv->gpio, priv);
if (err) if (err)
@ -506,6 +704,13 @@ static void gpio_mpsse_disconnect(struct usb_interface *intf)
{ {
struct mpsse_priv *priv = usb_get_intfdata(intf); struct mpsse_priv *priv = usb_get_intfdata(intf);
/*
* Lock prevents double-free of worker from here and the teardown
* step at the beginning of gpio_mpsse_poll
*/
scoped_guard(mutex, &priv->irq_race)
gpio_mpsse_stop_all_except(priv, NULL);
priv->intf = NULL; priv->intf = NULL;
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
usb_put_dev(priv->udev); usb_put_dev(priv->udev);

View File

@ -694,7 +694,7 @@ static const struct of_device_id msc313_gpio_of_match[] = {
* SoC goes into suspend to memory mode so we need to save some * SoC goes into suspend to memory mode so we need to save some
* of the register bits before suspending and put it back when resuming * of the register bits before suspending and put it back when resuming
*/ */
static int __maybe_unused msc313_gpio_suspend(struct device *dev) static int msc313_gpio_suspend(struct device *dev)
{ {
struct msc313_gpio *gpio = dev_get_drvdata(dev); struct msc313_gpio *gpio = dev_get_drvdata(dev);
int i; int i;
@ -705,7 +705,7 @@ static int __maybe_unused msc313_gpio_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused msc313_gpio_resume(struct device *dev) static int msc313_gpio_resume(struct device *dev)
{ {
struct msc313_gpio *gpio = dev_get_drvdata(dev); struct msc313_gpio *gpio = dev_get_drvdata(dev);
int i; int i;
@ -716,13 +716,13 @@ static int __maybe_unused msc313_gpio_resume(struct device *dev)
return 0; return 0;
} }
static SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume); static DEFINE_SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume);
static struct platform_driver msc313_gpio_driver = { static struct platform_driver msc313_gpio_driver = {
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.of_match_table = msc313_gpio_of_match, .of_match_table = msc313_gpio_of_match,
.pm = &msc313_gpio_ops, .pm = pm_sleep_ptr(&msc313_gpio_ops),
}, },
.probe = msc313_gpio_probe, .probe = msc313_gpio_probe,
}; };

View File

@ -573,11 +573,10 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
for (i = 0; i < mvchip->chip.ngpio; i++) { for (i = 0; i < mvchip->chip.ngpio; i++) {
int irq; int irq;
irq = irq_find_mapping(mvchip->domain, i);
if (!(cause & BIT(i))) if (!(cause & BIT(i)))
continue; continue;
irq = irq_find_mapping(mvchip->domain, i);
type = irq_get_trigger_type(irq); type = irq_get_trigger_type(irq);
if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
/* Swap polarity (race with GPIO line) */ /* Swap polarity (race with GPIO line) */

View File

@ -1503,7 +1503,7 @@ static void omap_gpio_remove(struct platform_device *pdev)
clk_unprepare(bank->dbck); clk_unprepare(bank->dbck);
} }
static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev) static int omap_gpio_runtime_suspend(struct device *dev)
{ {
struct gpio_bank *bank = dev_get_drvdata(dev); struct gpio_bank *bank = dev_get_drvdata(dev);
unsigned long flags; unsigned long flags;
@ -1516,7 +1516,7 @@ static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) static int omap_gpio_runtime_resume(struct device *dev)
{ {
struct gpio_bank *bank = dev_get_drvdata(dev); struct gpio_bank *bank = dev_get_drvdata(dev);
unsigned long flags; unsigned long flags;
@ -1529,7 +1529,7 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused omap_gpio_suspend(struct device *dev) static int omap_gpio_suspend(struct device *dev)
{ {
struct gpio_bank *bank = dev_get_drvdata(dev); struct gpio_bank *bank = dev_get_drvdata(dev);
@ -1541,7 +1541,7 @@ static int __maybe_unused omap_gpio_suspend(struct device *dev)
return omap_gpio_runtime_suspend(dev); return omap_gpio_runtime_suspend(dev);
} }
static int __maybe_unused omap_gpio_resume(struct device *dev) static int omap_gpio_resume(struct device *dev)
{ {
struct gpio_bank *bank = dev_get_drvdata(dev); struct gpio_bank *bank = dev_get_drvdata(dev);
@ -1554,9 +1554,8 @@ static int __maybe_unused omap_gpio_resume(struct device *dev)
} }
static const struct dev_pm_ops gpio_pm_ops = { static const struct dev_pm_ops gpio_pm_ops = {
SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL)
NULL) LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume)
SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume)
}; };
static struct platform_driver omap_gpio_driver = { static struct platform_driver omap_gpio_driver = {
@ -1564,7 +1563,7 @@ static struct platform_driver omap_gpio_driver = {
.remove = omap_gpio_remove, .remove = omap_gpio_remove,
.driver = { .driver = {
.name = "omap_gpio", .name = "omap_gpio",
.pm = &gpio_pm_ops, .pm = pm_ptr(&gpio_pm_ops),
.of_match_table = omap_gpio_match, .of_match_table = omap_gpio_match,
}, },
}; };

View File

@ -306,7 +306,7 @@ static inline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsigned int of
* Interrupt mask register 0x40 + 5 * bank_size RW * Interrupt mask register 0x40 + 5 * bank_size RW
* Interrupt status register 0x40 + 6 * bank_size R * Interrupt status register 0x40 + 6 * bank_size R
* *
* - Registers with bit 0x80 set, the AI bit * - Registers with bit 0x80 set, the AI bit (auto increment)
* The bit is cleared and the registers fall into one of the * The bit is cleared and the registers fall into one of the
* categories above. * categories above.
*/ */
@ -854,10 +854,13 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
int level; int level;
if (chip->driver_data & PCA_PCAL) { if (chip->driver_data & PCA_PCAL) {
DECLARE_BITMAP(latched_inputs, MAX_LINE);
guard(mutex)(&chip->i2c_lock); guard(mutex)(&chip->i2c_lock);
/* Enable latch on interrupt-enabled inputs */ /* Enable latch on edge-triggered interrupt-enabled inputs */
pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask); bitmap_or(latched_inputs, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
bitmap_and(latched_inputs, latched_inputs, chip->irq_mask, gc->ngpio);
pca953x_write_regs(chip, PCAL953X_IN_LATCH, latched_inputs);
bitmap_complement(irq_mask, chip->irq_mask, gc->ngpio); bitmap_complement(irq_mask, chip->irq_mask, gc->ngpio);
@ -1203,10 +1206,10 @@ static int pca953x_probe(struct i2c_client *client)
pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK); pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
if (NBANK(chip) > 2 || PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { if (NBANK(chip) > 2 || PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
dev_info(dev, "using AI\n"); dev_info(dev, "using auto increment\n");
regmap_config = &pca953x_ai_i2c_regmap; regmap_config = &pca953x_ai_i2c_regmap;
} else { } else {
dev_info(dev, "using no AI\n"); dev_info(dev, "using no auto increment\n");
regmap_config = &pca953x_i2c_regmap; regmap_config = &pca953x_i2c_regmap;
} }

View File

@ -171,7 +171,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr)
/* /*
* Save register configuration and disable interrupts. * Save register configuration and disable interrupts.
*/ */
static void __maybe_unused pch_gpio_save_reg_conf(struct pch_gpio *chip) static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
{ {
chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien); chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien);
chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask); chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask);
@ -187,7 +187,7 @@ static void __maybe_unused pch_gpio_save_reg_conf(struct pch_gpio *chip)
/* /*
* This function restores the register configuration of the GPIO device. * This function restores the register configuration of the GPIO device.
*/ */
static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip) static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
{ {
iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien); iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien);
iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask); iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask);
@ -402,7 +402,7 @@ static int pch_gpio_probe(struct pci_dev *pdev,
return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
} }
static int __maybe_unused pch_gpio_suspend(struct device *dev) static int pch_gpio_suspend(struct device *dev)
{ {
struct pch_gpio *chip = dev_get_drvdata(dev); struct pch_gpio *chip = dev_get_drvdata(dev);
unsigned long flags; unsigned long flags;
@ -414,7 +414,7 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused pch_gpio_resume(struct device *dev) static int pch_gpio_resume(struct device *dev)
{ {
struct pch_gpio *chip = dev_get_drvdata(dev); struct pch_gpio *chip = dev_get_drvdata(dev);
unsigned long flags; unsigned long flags;
@ -428,7 +428,7 @@ static int __maybe_unused pch_gpio_resume(struct device *dev)
return 0; return 0;
} }
static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume); static DEFINE_SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
static const struct pci_device_id pch_gpio_pcidev_id[] = { static const struct pci_device_id pch_gpio_pcidev_id[] = {
{ PCI_DEVICE_DATA(INTEL, EG20T_PCH, INTEL_EG20T_PCH) }, { PCI_DEVICE_DATA(INTEL, EG20T_PCH, INTEL_EG20T_PCH) },
@ -444,7 +444,7 @@ static struct pci_driver pch_gpio_driver = {
.id_table = pch_gpio_pcidev_id, .id_table = pch_gpio_pcidev_id,
.probe = pch_gpio_probe, .probe = pch_gpio_probe,
.driver = { .driver = {
.pm = &pch_gpio_pm_ops, .pm = pm_sleep_ptr(&pch_gpio_pm_ops),
}, },
}; };

View File

@ -37,7 +37,6 @@
#define PL061_GPIO_NR 8 #define PL061_GPIO_NR 8
#ifdef CONFIG_PM
struct pl061_context_save_regs { struct pl061_context_save_regs {
u8 gpio_data; u8 gpio_data;
u8 gpio_dir; u8 gpio_dir;
@ -46,7 +45,6 @@ struct pl061_context_save_regs {
u8 gpio_iev; u8 gpio_iev;
u8 gpio_ie; u8 gpio_ie;
}; };
#endif
struct pl061 { struct pl061 {
raw_spinlock_t lock; raw_spinlock_t lock;
@ -55,9 +53,7 @@ struct pl061 {
struct gpio_chip gc; struct gpio_chip gc;
int parent_irq; int parent_irq;
#ifdef CONFIG_PM
struct pl061_context_save_regs csave_regs; struct pl061_context_save_regs csave_regs;
#endif
}; };
static int pl061_get_direction(struct gpio_chip *gc, unsigned offset) static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
@ -367,7 +363,6 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int pl061_suspend(struct device *dev) static int pl061_suspend(struct device *dev)
{ {
struct pl061 *pl061 = dev_get_drvdata(dev); struct pl061 *pl061 = dev_get_drvdata(dev);
@ -411,13 +406,7 @@ static int pl061_resume(struct device *dev)
return 0; return 0;
} }
static const struct dev_pm_ops pl061_dev_pm_ops = { static DEFINE_SIMPLE_DEV_PM_OPS(pl061_dev_pm_ops, pl061_suspend, pl061_resume);
.suspend = pl061_suspend,
.resume = pl061_resume,
.freeze = pl061_suspend,
.restore = pl061_resume,
};
#endif
static const struct amba_id pl061_ids[] = { static const struct amba_id pl061_ids[] = {
{ {
@ -431,9 +420,7 @@ MODULE_DEVICE_TABLE(amba, pl061_ids);
static struct amba_driver pl061_gpio_driver = { static struct amba_driver pl061_gpio_driver = {
.drv = { .drv = {
.name = "pl061_gpio", .name = "pl061_gpio",
#ifdef CONFIG_PM .pm = pm_sleep_ptr(&pl061_dev_pm_ops),
.pm = &pl061_dev_pm_ops,
#endif
}, },
.id_table = pl061_ids, .id_table = pl061_ids,
.probe = pl061_probe, .probe = pl061_probe,

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Layerscape GPIO QIXIS FPGA driver
*
* Copyright 2025 NXP
*/
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/regmap.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
struct qixis_cpld_gpio_config {
u64 output_lines;
};
static const struct qixis_cpld_gpio_config lx2160ardb_sfp_cfg = {
.output_lines = BIT(0),
};
static const struct qixis_cpld_gpio_config ls1046aqds_stat_pres2_cfg = {
.output_lines = 0x0,
};
static const struct regmap_config regmap_config_8r_8v = {
.reg_bits = 8,
.val_bits = 8,
};
static int qixis_cpld_gpio_probe(struct platform_device *pdev)
{
DECLARE_BITMAP(fixed_direction_output, 8);
const struct qixis_cpld_gpio_config *cfg;
struct gpio_regmap_config config = {0};
struct regmap *regmap;
void __iomem *reg;
u32 base;
int ret;
if (!pdev->dev.parent)
return -ENODEV;
cfg = device_get_match_data(&pdev->dev);
ret = device_property_read_u32(&pdev->dev, "reg", &base);
if (ret)
return ret;
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!regmap) {
/* In case there is no regmap configured by the parent device,
* create our own from the MMIO space.
*/
reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg))
return PTR_ERR(reg);
regmap = devm_regmap_init_mmio(&pdev->dev, reg, &regmap_config_8r_8v);
if (!regmap)
return -ENODEV;
/* In this case, the offset of our register is 0 inside the
* regmap area that we just created.
*/
base = 0;
}
config.reg_dat_base = GPIO_REGMAP_ADDR(base);
config.reg_set_base = GPIO_REGMAP_ADDR(base);
config.drvdata = (void *)cfg;
config.regmap = regmap;
config.parent = &pdev->dev;
config.ngpio_per_reg = 8;
config.ngpio = 8;
bitmap_from_u64(fixed_direction_output, cfg->output_lines);
config.fixed_direction_output = fixed_direction_output;
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
}
static const struct of_device_id qixis_cpld_gpio_of_match[] = {
{
.compatible = "fsl,lx2160ardb-fpga-gpio-sfp",
.data = &lx2160ardb_sfp_cfg,
},
{
.compatible = "fsl,ls1046aqds-fpga-gpio-stat-pres2",
.data = &ls1046aqds_stat_pres2_cfg,
},
{}
};
MODULE_DEVICE_TABLE(of, qixis_cpld_gpio_of_match);
static struct platform_driver qixis_cpld_gpio_driver = {
.probe = qixis_cpld_gpio_probe,
.driver = {
.name = "gpio-qixis-cpld",
.of_match_table = qixis_cpld_gpio_of_match,
},
};
module_platform_driver(qixis_cpld_gpio_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>");
MODULE_DESCRIPTION("Layerscape GPIO QIXIS FPGA driver");

View File

@ -82,7 +82,11 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
if (ret) if (ret)
return ret; return ret;
ret = regmap_read(gpio->regmap, reg, &val); /* ensure we don't spoil any register cache with pin input values */
if (gpio->reg_dat_base == gpio->reg_set_base)
ret = regmap_read_bypassed(gpio->regmap, reg, &val);
else
ret = regmap_read(gpio->regmap, reg, &val);
if (ret) if (ret)
return ret; return ret;
@ -94,7 +98,7 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
{ {
struct gpio_regmap *gpio = gpiochip_get_data(chip); struct gpio_regmap *gpio = gpiochip_get_data(chip);
unsigned int base = gpio_regmap_addr(gpio->reg_set_base); unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
unsigned int reg, mask; unsigned int reg, mask, mask_val;
int ret; int ret;
ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask); ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
@ -102,9 +106,15 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
return ret; return ret;
if (val) if (val)
ret = regmap_update_bits(gpio->regmap, reg, mask, mask); mask_val = mask;
else else
ret = regmap_update_bits(gpio->regmap, reg, mask, 0); mask_val = 0;
/* ignore input values which shadow the old output value */
if (gpio->reg_dat_base == gpio->reg_set_base)
ret = regmap_write_bits(gpio->regmap, reg, mask, mask_val);
else
ret = regmap_update_bits(gpio->regmap, reg, mask, mask_val);
return ret; return ret;
} }

View File

@ -322,6 +322,7 @@ MODULE_DEVICE_TABLE(auxiliary, gpio_shared_proxy_id_table);
static struct auxiliary_driver gpio_shared_proxy_driver = { static struct auxiliary_driver gpio_shared_proxy_driver = {
.driver = { .driver = {
.name = "gpio-shared-proxy", .name = "gpio-shared-proxy",
.suppress_bind_attrs = true,
}, },
.probe = gpio_shared_proxy_probe, .probe = gpio_shared_proxy_probe,
.id_table = gpio_shared_proxy_id_table, .id_table = gpio_shared_proxy_id_table,

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (c) 2016-2022 NVIDIA Corporation * Copyright (c) 2016-2025 NVIDIA Corporation
* *
* Author: Thierry Reding <treding@nvidia.com> * Author: Thierry Reding <treding@nvidia.com>
* Dipen Patel <dpatel@nvidia.com> * Dipen Patel <dpatel@nvidia.com>
@ -69,6 +69,30 @@
#define TEGRA186_GPIO_INTERRUPT_STATUS(x) (0x100 + (x) * 4) #define TEGRA186_GPIO_INTERRUPT_STATUS(x) (0x100 + (x) * 4)
/* Tegra410 GPIOs implemented by the COMPUTE GPIO controller */
#define TEGRA410_COMPUTE_GPIO_PORT_A 0
#define TEGRA410_COMPUTE_GPIO_PORT_B 1
#define TEGRA410_COMPUTE_GPIO_PORT_C 2
#define TEGRA410_COMPUTE_GPIO_PORT_D 3
#define TEGRA410_COMPUTE_GPIO_PORT_E 4
/* Tegra410 GPIOs implemented by the SYSTEM GPIO controller */
#define TEGRA410_SYSTEM_GPIO_PORT_A 0
#define TEGRA410_SYSTEM_GPIO_PORT_B 1
#define TEGRA410_SYSTEM_GPIO_PORT_C 2
#define TEGRA410_SYSTEM_GPIO_PORT_D 3
#define TEGRA410_SYSTEM_GPIO_PORT_E 4
#define TEGRA410_SYSTEM_GPIO_PORT_I 5
#define TEGRA410_SYSTEM_GPIO_PORT_J 6
#define TEGRA410_SYSTEM_GPIO_PORT_K 7
#define TEGRA410_SYSTEM_GPIO_PORT_L 8
#define TEGRA410_SYSTEM_GPIO_PORT_M 9
#define TEGRA410_SYSTEM_GPIO_PORT_N 10
#define TEGRA410_SYSTEM_GPIO_PORT_P 11
#define TEGRA410_SYSTEM_GPIO_PORT_Q 12
#define TEGRA410_SYSTEM_GPIO_PORT_R 13
#define TEGRA410_SYSTEM_GPIO_PORT_V 14
struct tegra_gpio_port { struct tegra_gpio_port {
const char *name; const char *name;
unsigned int bank; unsigned int bank;
@ -85,6 +109,7 @@ struct tegra_gpio_soc {
const struct tegra_gpio_port *ports; const struct tegra_gpio_port *ports;
unsigned int num_ports; unsigned int num_ports;
const char *name; const char *name;
const char *prefix;
unsigned int instance; unsigned int instance;
unsigned int num_irqs_per_bank; unsigned int num_irqs_per_bank;
@ -916,8 +941,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
char *name; char *name;
for (j = 0; j < port->pins; j++) { for (j = 0; j < port->pins; j++) {
name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, if (gpio->soc->prefix)
"P%s.%02x", port->name, j); name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "%s-P%s.%02x",
gpio->soc->prefix, port->name, j);
else
name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "P%s.%02x",
port->name, j);
if (!name) if (!name)
return -ENOMEM; return -ENOMEM;
@ -1002,14 +1031,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio);
} }
#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA_GPIO_PORT(_prefix, _name, _bank, _port, _pins) \
[TEGRA186_MAIN_GPIO_PORT_##_name] = { \ [_prefix##_GPIO_PORT_##_name] = { \
.name = #_name, \ .name = #_name, \
.bank = _bank, \ .bank = _bank, \
.port = _port, \ .port = _port, \
.pins = _pins, \ .pins = _pins, \
} }
#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA186_MAIN, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra186_main_ports[] = { static const struct tegra_gpio_port tegra186_main_ports[] = {
TEGRA186_MAIN_GPIO_PORT( A, 2, 0, 7), TEGRA186_MAIN_GPIO_PORT( A, 2, 0, 7),
TEGRA186_MAIN_GPIO_PORT( B, 3, 0, 7), TEGRA186_MAIN_GPIO_PORT( B, 3, 0, 7),
@ -1045,13 +1077,8 @@ static const struct tegra_gpio_soc tegra186_main_soc = {
.has_vm_support = false, .has_vm_support = false,
}; };
#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA186_AON_GPIO_PORT_##_name] = { \ TEGRA_GPIO_PORT(TEGRA186_AON, _name, _bank, _port, _pins)
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra186_aon_ports[] = { static const struct tegra_gpio_port tegra186_aon_ports[] = {
TEGRA186_AON_GPIO_PORT( S, 0, 1, 5), TEGRA186_AON_GPIO_PORT( S, 0, 1, 5),
@ -1073,13 +1100,8 @@ static const struct tegra_gpio_soc tegra186_aon_soc = {
.has_vm_support = false, .has_vm_support = false,
}; };
#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA194_MAIN_GPIO_PORT_##_name] = { \ TEGRA_GPIO_PORT(TEGRA194_MAIN, _name, _bank, _port, _pins)
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra194_main_ports[] = { static const struct tegra_gpio_port tegra194_main_ports[] = {
TEGRA194_MAIN_GPIO_PORT( A, 1, 2, 8), TEGRA194_MAIN_GPIO_PORT( A, 1, 2, 8),
@ -1129,13 +1151,8 @@ static const struct tegra_gpio_soc tegra194_main_soc = {
.has_vm_support = true, .has_vm_support = true,
}; };
#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA194_AON_GPIO_PORT_##_name] = { \ TEGRA_GPIO_PORT(TEGRA194_AON, _name, _bank, _port, _pins)
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra194_aon_ports[] = { static const struct tegra_gpio_port tegra194_aon_ports[] = {
TEGRA194_AON_GPIO_PORT(AA, 0, 3, 8), TEGRA194_AON_GPIO_PORT(AA, 0, 3, 8),
@ -1155,13 +1172,8 @@ static const struct tegra_gpio_soc tegra194_aon_soc = {
.has_vm_support = false, .has_vm_support = false,
}; };
#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA234_MAIN_GPIO_PORT_##_name] = { \ TEGRA_GPIO_PORT(TEGRA234_MAIN, _name, _bank, _port, _pins)
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra234_main_ports[] = { static const struct tegra_gpio_port tegra234_main_ports[] = {
TEGRA234_MAIN_GPIO_PORT( A, 0, 0, 8), TEGRA234_MAIN_GPIO_PORT( A, 0, 0, 8),
@ -1200,13 +1212,8 @@ static const struct tegra_gpio_soc tegra234_main_soc = {
.has_vm_support = true, .has_vm_support = true,
}; };
#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA234_AON_GPIO_PORT_##_name] = { \ TEGRA_GPIO_PORT(TEGRA234_AON, _name, _bank, _port, _pins)
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra234_aon_ports[] = { static const struct tegra_gpio_port tegra234_aon_ports[] = {
TEGRA234_AON_GPIO_PORT(AA, 0, 4, 8), TEGRA234_AON_GPIO_PORT(AA, 0, 4, 8),
@ -1227,13 +1234,8 @@ static const struct tegra_gpio_soc tegra234_aon_soc = {
.has_vm_support = false, .has_vm_support = false,
}; };
#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA241_MAIN_GPIO_PORT_##_name] = { \ TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins)
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra241_main_ports[] = { static const struct tegra_gpio_port tegra241_main_ports[] = {
TEGRA241_MAIN_GPIO_PORT(A, 0, 0, 8), TEGRA241_MAIN_GPIO_PORT(A, 0, 0, 8),
@ -1258,13 +1260,8 @@ static const struct tegra_gpio_soc tegra241_main_soc = {
.has_vm_support = false, .has_vm_support = false,
}; };
#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA241_AON_GPIO_PORT_##_name] = { \ TEGRA_GPIO_PORT(TEGRA241_AON, _name, _bank, _port, _pins)
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra241_aon_ports[] = { static const struct tegra_gpio_port tegra241_aon_ports[] = {
TEGRA241_AON_GPIO_PORT(AA, 0, 0, 8), TEGRA241_AON_GPIO_PORT(AA, 0, 0, 8),
@ -1280,13 +1277,8 @@ static const struct tegra_gpio_soc tegra241_aon_soc = {
.has_vm_support = false, .has_vm_support = false,
}; };
#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ #define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
[TEGRA256_MAIN_GPIO_PORT_##_name] = { \ TEGRA_GPIO_PORT(TEGRA256_MAIN, _name, _bank, _port, _pins)
.name = #_name, \
.bank = _bank, \
.port = _port, \
.pins = _pins, \
}
static const struct tegra_gpio_port tegra256_main_ports[] = { static const struct tegra_gpio_port tegra256_main_ports[] = {
TEGRA256_MAIN_GPIO_PORT(A, 0, 0, 8), TEGRA256_MAIN_GPIO_PORT(A, 0, 0, 8),
@ -1304,6 +1296,56 @@ static const struct tegra_gpio_soc tegra256_main_soc = {
.has_vm_support = true, .has_vm_support = true,
}; };
#define TEGRA410_COMPUTE_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA410_COMPUTE, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra410_compute_ports[] = {
TEGRA410_COMPUTE_GPIO_PORT(A, 0, 0, 3),
TEGRA410_COMPUTE_GPIO_PORT(B, 1, 0, 8),
TEGRA410_COMPUTE_GPIO_PORT(C, 1, 1, 3),
TEGRA410_COMPUTE_GPIO_PORT(D, 2, 0, 8),
TEGRA410_COMPUTE_GPIO_PORT(E, 2, 1, 8),
};
static const struct tegra_gpio_soc tegra410_compute_soc = {
.num_ports = ARRAY_SIZE(tegra410_compute_ports),
.ports = tegra410_compute_ports,
.name = "tegra410-gpio-compute",
.prefix = "COMPUTE",
.num_irqs_per_bank = 8,
.instance = 0,
};
#define TEGRA410_SYSTEM_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA410_SYSTEM, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra410_system_ports[] = {
TEGRA410_SYSTEM_GPIO_PORT(A, 0, 0, 7),
TEGRA410_SYSTEM_GPIO_PORT(B, 0, 1, 8),
TEGRA410_SYSTEM_GPIO_PORT(C, 0, 2, 8),
TEGRA410_SYSTEM_GPIO_PORT(D, 0, 3, 8),
TEGRA410_SYSTEM_GPIO_PORT(E, 0, 4, 6),
TEGRA410_SYSTEM_GPIO_PORT(I, 1, 0, 8),
TEGRA410_SYSTEM_GPIO_PORT(J, 1, 1, 7),
TEGRA410_SYSTEM_GPIO_PORT(K, 1, 2, 7),
TEGRA410_SYSTEM_GPIO_PORT(L, 1, 3, 7),
TEGRA410_SYSTEM_GPIO_PORT(M, 2, 0, 7),
TEGRA410_SYSTEM_GPIO_PORT(N, 2, 1, 6),
TEGRA410_SYSTEM_GPIO_PORT(P, 2, 2, 8),
TEGRA410_SYSTEM_GPIO_PORT(Q, 2, 3, 3),
TEGRA410_SYSTEM_GPIO_PORT(R, 2, 4, 2),
TEGRA410_SYSTEM_GPIO_PORT(V, 1, 4, 2),
};
static const struct tegra_gpio_soc tegra410_system_soc = {
.num_ports = ARRAY_SIZE(tegra410_system_ports),
.ports = tegra410_system_ports,
.name = "tegra410-gpio-system",
.prefix = "SYSTEM",
.num_irqs_per_bank = 8,
.instance = 0,
};
static const struct of_device_id tegra186_gpio_of_match[] = { static const struct of_device_id tegra186_gpio_of_match[] = {
{ {
.compatible = "nvidia,tegra186-gpio", .compatible = "nvidia,tegra186-gpio",
@ -1339,6 +1381,8 @@ static const struct acpi_device_id tegra186_gpio_acpi_match[] = {
{ .id = "NVDA0408", .driver_data = (kernel_ulong_t)&tegra194_aon_soc }, { .id = "NVDA0408", .driver_data = (kernel_ulong_t)&tegra194_aon_soc },
{ .id = "NVDA0508", .driver_data = (kernel_ulong_t)&tegra241_main_soc }, { .id = "NVDA0508", .driver_data = (kernel_ulong_t)&tegra241_main_soc },
{ .id = "NVDA0608", .driver_data = (kernel_ulong_t)&tegra241_aon_soc }, { .id = "NVDA0608", .driver_data = (kernel_ulong_t)&tegra241_aon_soc },
{ .id = "NVDA0708", .driver_data = (kernel_ulong_t)&tegra410_compute_soc },
{ .id = "NVDA0808", .driver_data = (kernel_ulong_t)&tegra410_system_soc },
{} {}
}; };
MODULE_DEVICE_TABLE(acpi, tegra186_gpio_acpi_match); MODULE_DEVICE_TABLE(acpi, tegra186_gpio_acpi_match);

View File

@ -279,19 +279,18 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc)
} }
/* Minimal runtime PM is needed by the IRQ subsystem */ /* Minimal runtime PM is needed by the IRQ subsystem */
static int __maybe_unused tqmx86_gpio_runtime_suspend(struct device *dev) static int tqmx86_gpio_runtime_suspend(struct device *dev)
{ {
return 0; return 0;
} }
static int __maybe_unused tqmx86_gpio_runtime_resume(struct device *dev) static int tqmx86_gpio_runtime_resume(struct device *dev)
{ {
return 0; return 0;
} }
static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = { static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = {
SET_RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, tqmx86_gpio_runtime_resume, NULL)
tqmx86_gpio_runtime_resume, NULL)
}; };
static void tqmx86_init_irq_valid_mask(struct gpio_chip *chip, static void tqmx86_init_irq_valid_mask(struct gpio_chip *chip,
@ -425,7 +424,7 @@ static int tqmx86_gpio_probe(struct platform_device *pdev)
static struct platform_driver tqmx86_gpio_driver = { static struct platform_driver tqmx86_gpio_driver = {
.driver = { .driver = {
.name = "tqmx86-gpio", .name = "tqmx86-gpio",
.pm = &tqmx86_gpio_dev_pm_ops, .pm = pm_ptr(&tqmx86_gpio_dev_pm_ops),
}, },
.probe = tqmx86_gpio_probe, .probe = tqmx86_gpio_probe,
}; };

View File

@ -426,7 +426,7 @@ static void uniphier_gpio_remove(struct platform_device *pdev)
irq_domain_remove(priv->domain); irq_domain_remove(priv->domain);
} }
static int __maybe_unused uniphier_gpio_suspend(struct device *dev) static int uniphier_gpio_suspend(struct device *dev)
{ {
struct uniphier_gpio_priv *priv = dev_get_drvdata(dev); struct uniphier_gpio_priv *priv = dev_get_drvdata(dev);
unsigned int nbanks = uniphier_gpio_get_nbanks(priv->chip.ngpio); unsigned int nbanks = uniphier_gpio_get_nbanks(priv->chip.ngpio);
@ -448,7 +448,7 @@ static int __maybe_unused uniphier_gpio_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused uniphier_gpio_resume(struct device *dev) static int uniphier_gpio_resume(struct device *dev)
{ {
struct uniphier_gpio_priv *priv = dev_get_drvdata(dev); struct uniphier_gpio_priv *priv = dev_get_drvdata(dev);
unsigned int nbanks = uniphier_gpio_get_nbanks(priv->chip.ngpio); unsigned int nbanks = uniphier_gpio_get_nbanks(priv->chip.ngpio);
@ -473,8 +473,7 @@ static int __maybe_unused uniphier_gpio_resume(struct device *dev)
} }
static const struct dev_pm_ops uniphier_gpio_pm_ops = { static const struct dev_pm_ops uniphier_gpio_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(uniphier_gpio_suspend, LATE_SYSTEM_SLEEP_PM_OPS(uniphier_gpio_suspend, uniphier_gpio_resume)
uniphier_gpio_resume)
}; };
static const struct of_device_id uniphier_gpio_match[] = { static const struct of_device_id uniphier_gpio_match[] = {
@ -489,7 +488,7 @@ static struct platform_driver uniphier_gpio_driver = {
.driver = { .driver = {
.name = "uniphier-gpio", .name = "uniphier-gpio",
.of_match_table = uniphier_gpio_match, .of_match_table = uniphier_gpio_match,
.pm = &uniphier_gpio_pm_ops, .pm = pm_sleep_ptr(&uniphier_gpio_pm_ops),
}, },
}; };
module_platform_driver(uniphier_gpio_driver); module_platform_driver(uniphier_gpio_driver);

View File

@ -500,9 +500,7 @@ static int gpio_virtuser_value_set(void *data, u64 val)
if (val > 1) if (val > 1)
return -EINVAL; return -EINVAL;
gpiod_set_value_cansleep(ld->ad.desc, (int)val); return gpiod_set_value_cansleep(ld->ad.desc, (int)val);
return 0;
} }
DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_fops, DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_fops,
@ -543,7 +541,7 @@ static void gpio_virtuser_set_value_atomic(struct irq_work *work)
struct gpio_virtuser_irq_work_context *ctx = struct gpio_virtuser_irq_work_context *ctx =
to_gpio_virtuser_irq_work_context(work); to_gpio_virtuser_irq_work_context(work);
gpiod_set_value(ctx->desc, ctx->val); ctx->ret = gpiod_set_value(ctx->desc, ctx->val);
complete(&ctx->work_completion); complete(&ctx->work_completion);
} }
@ -562,7 +560,7 @@ static int gpio_virtuser_value_atomic_set(void *data, u64 val)
gpio_virtuser_irq_work_queue_sync(&ctx); gpio_virtuser_irq_work_queue_sync(&ctx);
return 0; return ctx.ret;
} }
DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_atomic_fops, DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_atomic_fops,

View File

@ -130,7 +130,7 @@ static int xgene_gpio_dir_out(struct gpio_chip *gc,
return 0; return 0;
} }
static __maybe_unused int xgene_gpio_suspend(struct device *dev) static int xgene_gpio_suspend(struct device *dev)
{ {
struct xgene_gpio *gpio = dev_get_drvdata(dev); struct xgene_gpio *gpio = dev_get_drvdata(dev);
unsigned long bank_offset; unsigned long bank_offset;
@ -143,7 +143,7 @@ static __maybe_unused int xgene_gpio_suspend(struct device *dev)
return 0; return 0;
} }
static __maybe_unused int xgene_gpio_resume(struct device *dev) static int xgene_gpio_resume(struct device *dev)
{ {
struct xgene_gpio *gpio = dev_get_drvdata(dev); struct xgene_gpio *gpio = dev_get_drvdata(dev);
unsigned long bank_offset; unsigned long bank_offset;
@ -156,7 +156,7 @@ static __maybe_unused int xgene_gpio_resume(struct device *dev)
return 0; return 0;
} }
static SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume); static DEFINE_SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume);
static int xgene_gpio_probe(struct platform_device *pdev) static int xgene_gpio_probe(struct platform_device *pdev)
{ {
@ -204,7 +204,7 @@ static struct platform_driver xgene_gpio_driver = {
.name = "xgene-gpio", .name = "xgene-gpio",
.of_match_table = xgene_gpio_of_match, .of_match_table = xgene_gpio_of_match,
.acpi_match_table = ACPI_PTR(xgene_gpio_acpi_match), .acpi_match_table = ACPI_PTR(xgene_gpio_acpi_match),
.pm = &xgene_gpio_pm, .pm = pm_sleep_ptr(&xgene_gpio_pm),
}, },
.probe = xgene_gpio_probe, .probe = xgene_gpio_probe,
}; };

View File

@ -286,7 +286,7 @@ static void xgpio_free(struct gpio_chip *chip, unsigned int offset)
pm_runtime_put(chip->parent); pm_runtime_put(chip->parent);
} }
static int __maybe_unused xgpio_suspend(struct device *dev) static int xgpio_suspend(struct device *dev)
{ {
struct xgpio_instance *gpio = dev_get_drvdata(dev); struct xgpio_instance *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq); struct irq_data *data = irq_get_irq_data(gpio->irq);
@ -327,7 +327,7 @@ static void xgpio_irq_ack(struct irq_data *irq_data)
{ {
} }
static int __maybe_unused xgpio_resume(struct device *dev) static int xgpio_resume(struct device *dev)
{ {
struct xgpio_instance *gpio = dev_get_drvdata(dev); struct xgpio_instance *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq); struct irq_data *data = irq_get_irq_data(gpio->irq);
@ -343,7 +343,7 @@ static int __maybe_unused xgpio_resume(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused xgpio_runtime_suspend(struct device *dev) static int xgpio_runtime_suspend(struct device *dev)
{ {
struct xgpio_instance *gpio = dev_get_drvdata(dev); struct xgpio_instance *gpio = dev_get_drvdata(dev);
@ -352,7 +352,7 @@ static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused xgpio_runtime_resume(struct device *dev) static int xgpio_runtime_resume(struct device *dev)
{ {
struct xgpio_instance *gpio = dev_get_drvdata(dev); struct xgpio_instance *gpio = dev_get_drvdata(dev);
@ -360,9 +360,8 @@ static int __maybe_unused xgpio_runtime_resume(struct device *dev)
} }
static const struct dev_pm_ops xgpio_dev_pm_ops = { static const struct dev_pm_ops xgpio_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume) SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume)
SET_RUNTIME_PM_OPS(xgpio_runtime_suspend, RUNTIME_PM_OPS(xgpio_runtime_suspend, xgpio_runtime_resume, NULL)
xgpio_runtime_resume, NULL)
}; };
/** /**
@ -682,7 +681,7 @@ static struct platform_driver xgpio_plat_driver = {
.driver = { .driver = {
.name = "gpio-xilinx", .name = "gpio-xilinx",
.of_match_table = xgpio_of_match, .of_match_table = xgpio_of_match,
.pm = &xgpio_dev_pm_ops, .pm = pm_ptr(&xgpio_dev_pm_ops),
}, },
}; };

View File

@ -735,7 +735,7 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio)
} }
} }
static int __maybe_unused zynq_gpio_suspend(struct device *dev) static int zynq_gpio_suspend(struct device *dev)
{ {
struct zynq_gpio *gpio = dev_get_drvdata(dev); struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq); struct irq_data *data = irq_get_irq_data(gpio->irq);
@ -756,7 +756,7 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused zynq_gpio_resume(struct device *dev) static int zynq_gpio_resume(struct device *dev)
{ {
struct zynq_gpio *gpio = dev_get_drvdata(dev); struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq); struct irq_data *data = irq_get_irq_data(gpio->irq);
@ -779,7 +779,7 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev) static int zynq_gpio_runtime_suspend(struct device *dev)
{ {
struct zynq_gpio *gpio = dev_get_drvdata(dev); struct zynq_gpio *gpio = dev_get_drvdata(dev);
@ -788,7 +788,7 @@ static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev) static int zynq_gpio_runtime_resume(struct device *dev)
{ {
struct zynq_gpio *gpio = dev_get_drvdata(dev); struct zynq_gpio *gpio = dev_get_drvdata(dev);
@ -814,9 +814,8 @@ static void zynq_gpio_free(struct gpio_chip *chip, unsigned int offset)
} }
static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume) SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume)
SET_RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL)
zynq_gpio_runtime_resume, NULL)
}; };
static const struct zynq_platform_data versal_gpio_def = { static const struct zynq_platform_data versal_gpio_def = {
@ -1022,7 +1021,7 @@ static void zynq_gpio_remove(struct platform_device *pdev)
static struct platform_driver zynq_gpio_driver = { static struct platform_driver zynq_gpio_driver = {
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.pm = &zynq_gpio_dev_pm_ops, .pm = pm_ptr(&zynq_gpio_dev_pm_ops),
.of_match_table = zynq_gpio_of_match, .of_match_table = zynq_gpio_of_match,
}, },
.probe = zynq_gpio_probe, .probe = zynq_gpio_probe,

View File

@ -1099,7 +1099,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
} }
length = min_t(u16, agpio->pin_table_length, pin_index + bits); length = min(agpio->pin_table_length, pin_index + bits);
for (i = pin_index; i < length; ++i) { for (i = pin_index; i < length; ++i) {
unsigned int pin = agpio->pin_table[i]; unsigned int pin = agpio->pin_table[i];
struct acpi_gpio_connection *conn; struct acpi_gpio_connection *conn;

View File

@ -652,7 +652,7 @@ static enum hte_return process_hw_ts_thread(void *p)
} }
le.line_seqno = line->line_seqno; le.line_seqno = line->line_seqno;
le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
le.offset = gpio_chip_hwgpio(line->desc); le.offset = gpiod_hwgpio(line->desc);
linereq_put_event(lr, &le); linereq_put_event(lr, &le);
@ -676,7 +676,7 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
if (READ_ONCE(line->sw_debounced)) { if (READ_ONCE(line->sw_debounced)) {
line->total_discard_seq++; line->total_discard_seq++;
line->last_seqno = ts->seq; line->last_seqno = ts->seq;
mod_delayed_work(system_wq, &line->work, mod_delayed_work(system_percpu_wq, &line->work,
usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
} else { } else {
if (unlikely(ts->seq < line->line_seqno)) if (unlikely(ts->seq < line->line_seqno))
@ -769,7 +769,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
line->line_seqno++; line->line_seqno++;
le.line_seqno = line->line_seqno; le.line_seqno = line->line_seqno;
le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
le.offset = gpio_chip_hwgpio(line->desc); le.offset = gpiod_hwgpio(line->desc);
linereq_put_event(lr, &le); linereq_put_event(lr, &le);
@ -817,7 +817,7 @@ static irqreturn_t debounce_irq_handler(int irq, void *p)
{ {
struct line *line = p; struct line *line = p;
mod_delayed_work(system_wq, &line->work, mod_delayed_work(system_percpu_wq, &line->work,
usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
return IRQ_HANDLED; return IRQ_HANDLED;
@ -867,7 +867,7 @@ static void debounce_work_func(struct work_struct *work)
lr = line->req; lr = line->req;
le.timestamp_ns = line_event_timestamp(line); le.timestamp_ns = line_event_timestamp(line);
le.offset = gpio_chip_hwgpio(line->desc); le.offset = gpiod_hwgpio(line->desc);
#ifdef CONFIG_HTE #ifdef CONFIG_HTE
if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) { if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) {
/* discard events except the last one */ /* discard events except the last one */
@ -1567,7 +1567,7 @@ static void linereq_show_fdinfo(struct seq_file *out, struct file *file)
for (i = 0; i < lr->num_lines; i++) for (i = 0; i < lr->num_lines; i++)
seq_printf(out, "gpio-line:\t%d\n", seq_printf(out, "gpio-line:\t%d\n",
gpio_chip_hwgpio(lr->lines[i].desc)); gpiod_hwgpio(lr->lines[i].desc));
} }
#endif #endif
@ -2220,7 +2220,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
return; return;
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
info->offset = gpio_chip_hwgpio(desc); info->offset = gpiod_hwgpio(desc);
if (desc->name) if (desc->name)
strscpy(info->name, desc->name, sizeof(info->name)); strscpy(info->name, desc->name, sizeof(info->name));
@ -2526,7 +2526,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
struct gpio_desc *desc = data; struct gpio_desc *desc = data;
struct file *fp; struct file *fp;
if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines)) if (!test_bit(gpiod_hwgpio(desc), cdev->watched_lines))
return NOTIFY_DONE; return NOTIFY_DONE;
/* Keep the file descriptor alive for the duration of the notification. */ /* Keep the file descriptor alive for the duration of the notification. */
@ -2804,7 +2804,7 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
if (!gc) if (!gc)
return -ENODEV; return -ENODEV;
chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
return 0; return 0;
} }

View File

@ -34,30 +34,20 @@ EXPORT_SYMBOL_GPL(gpio_free);
*/ */
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
{ {
struct gpio_desc *desc;
int err; int err;
/* Compatibility: assume unavailable "valid" GPIOs will appear later */ err = gpio_request(gpio, label);
desc = gpio_to_desc(gpio);
if (!desc)
return -EPROBE_DEFER;
err = gpiod_request(desc, label);
if (err) if (err)
return err; return err;
if (flags & GPIOF_IN) if (flags & GPIOF_IN)
err = gpiod_direction_input(desc); err = gpio_direction_input(gpio);
else else
err = gpiod_direction_output_raw(desc, !!(flags & GPIOF_OUT_INIT_HIGH)); err = gpio_direction_output(gpio, !!(flags & GPIOF_OUT_INIT_HIGH));
if (err) if (err)
goto free_gpio; gpio_free(gpio);
return 0;
free_gpio:
gpiod_free(desc);
return err; return err;
} }
EXPORT_SYMBOL_GPL(gpio_request_one); EXPORT_SYMBOL_GPL(gpio_request_one);
@ -78,11 +68,9 @@ int gpio_request(unsigned gpio, const char *label)
} }
EXPORT_SYMBOL_GPL(gpio_request); EXPORT_SYMBOL_GPL(gpio_request);
static void devm_gpio_release(struct device *dev, void *res) static void devm_gpio_release(void *gpio)
{ {
unsigned *gpio = res; gpio_free((unsigned)(unsigned long)gpio);
gpio_free(*gpio);
} }
/** /**
@ -100,22 +88,22 @@ static void devm_gpio_release(struct device *dev, void *res)
int devm_gpio_request_one(struct device *dev, unsigned gpio, int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label) unsigned long flags, const char *label)
{ {
unsigned *dr;
int rc; int rc;
dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); rc = gpio_request(gpio, label);
if (!dr) if (rc)
return -ENOMEM; return rc;
if (flags & GPIOF_IN)
rc = gpio_direction_input(gpio);
else
rc = gpio_direction_output(gpio, !!(flags & GPIOF_OUT_INIT_HIGH));
rc = gpio_request_one(gpio, flags, label);
if (rc) { if (rc) {
devres_free(dr); gpio_free(gpio);
return rc; return rc;
} }
*dr = gpio; return devm_add_action_or_reset(dev, devm_gpio_release, (void *)(unsigned long)gpio);
devres_add(dev, dr);
return 0;
} }
EXPORT_SYMBOL_GPL(devm_gpio_request_one); EXPORT_SYMBOL_GPL(devm_gpio_request_one);

View File

@ -1031,85 +1031,6 @@ static int of_gpio_threecell_xlate(struct gpio_chip *gc,
return gpiospec->args[1]; return gpiospec->args[1];
} }
#if IS_ENABLED(CONFIG_OF_GPIO_MM_GPIOCHIP)
#include <linux/gpio/legacy-of-mm-gpiochip.h>
/**
* of_mm_gpiochip_add_data - Add memory mapped GPIO chip (bank)
* @np: device node of the GPIO chip
* @mm_gc: pointer to the of_mm_gpio_chip allocated structure
* @data: driver data to store in the struct gpio_chip
*
* To use this function you should allocate and fill mm_gc with:
*
* 1) In the gpio_chip structure:
* - all the callbacks
* - of_gpio_n_cells
* - of_xlate callback (optional)
*
* 3) In the of_mm_gpio_chip structure:
* - save_regs callback (optional)
*
* If succeeded, this function will map bank's memory and will
* do all necessary work for you. Then you'll able to use .regs
* to manage GPIOs from the callbacks.
*
* Returns:
* 0 on success, or negative errno on failure.
*/
int of_mm_gpiochip_add_data(struct device_node *np,
struct of_mm_gpio_chip *mm_gc,
void *data)
{
int ret = -ENOMEM;
struct gpio_chip *gc = &mm_gc->gc;
gc->label = kasprintf(GFP_KERNEL, "%pOF", np);
if (!gc->label)
goto err0;
mm_gc->regs = of_iomap(np, 0);
if (!mm_gc->regs)
goto err1;
gc->base = -1;
if (mm_gc->save_regs)
mm_gc->save_regs(mm_gc);
fwnode_handle_put(mm_gc->gc.fwnode);
mm_gc->gc.fwnode = fwnode_handle_get(of_fwnode_handle(np));
ret = gpiochip_add_data(gc, data);
if (ret)
goto err2;
return 0;
err2:
of_node_put(np);
iounmap(mm_gc->regs);
err1:
kfree(gc->label);
err0:
pr_err("%pOF: GPIO chip registration failed with status %d\n", np, ret);
return ret;
}
EXPORT_SYMBOL_GPL(of_mm_gpiochip_add_data);
/**
* of_mm_gpiochip_remove - Remove memory mapped GPIO chip (bank)
* @mm_gc: pointer to the of_mm_gpio_chip allocated structure
*/
void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
{
struct gpio_chip *gc = &mm_gc->gc;
gpiochip_remove(gc);
iounmap(mm_gc->regs);
kfree(gc->label);
}
EXPORT_SYMBOL_GPL(of_mm_gpiochip_remove);
#endif
#ifdef CONFIG_PINCTRL #ifdef CONFIG_PINCTRL
static int of_gpiochip_add_pin_range(struct gpio_chip *chip) static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
{ {

View File

@ -49,6 +49,7 @@ struct gpio_shared_entry {
unsigned int offset; unsigned int offset;
/* Index in the property value array. */ /* Index in the property value array. */
size_t index; size_t index;
struct mutex lock;
struct gpio_shared_desc *shared_desc; struct gpio_shared_desc *shared_desc;
struct kref ref; struct kref ref;
struct list_head refs; struct list_head refs;
@ -58,6 +59,7 @@ static LIST_HEAD(gpio_shared_list);
static DEFINE_MUTEX(gpio_shared_lock); static DEFINE_MUTEX(gpio_shared_lock);
static DEFINE_IDA(gpio_shared_ida); static DEFINE_IDA(gpio_shared_ida);
#if IS_ENABLED(CONFIG_OF)
static struct gpio_shared_entry * static struct gpio_shared_entry *
gpio_shared_find_entry(struct fwnode_handle *controller_node, gpio_shared_find_entry(struct fwnode_handle *controller_node,
unsigned int offset) unsigned int offset)
@ -72,7 +74,26 @@ gpio_shared_find_entry(struct fwnode_handle *controller_node,
return NULL; return NULL;
} }
#if IS_ENABLED(CONFIG_OF) /* Handle all special nodes that we should ignore. */
static bool gpio_shared_of_node_ignore(struct device_node *node)
{
/*
* __symbols__ is a special, internal node and should not be considered
* when scanning for shared GPIOs.
*/
if (of_node_name_eq(node, "__symbols__"))
return true;
/*
* GPIO hogs have a "gpios" property which is not a phandle and can't
* possibly refer to a shared GPIO.
*/
if (of_property_present(node, "gpio-hog"))
return true;
return false;
}
static int gpio_shared_of_traverse(struct device_node *curr) static int gpio_shared_of_traverse(struct device_node *curr)
{ {
struct gpio_shared_entry *entry; struct gpio_shared_entry *entry;
@ -84,6 +105,9 @@ static int gpio_shared_of_traverse(struct device_node *curr)
const char *suffix; const char *suffix;
int ret, count, i; int ret, count, i;
if (gpio_shared_of_node_ignore(curr))
return 0;
for_each_property_of_node(curr, prop) { for_each_property_of_node(curr, prop) {
/* /*
* The standard name for a GPIO property is "foo-gpios" * The standard name for a GPIO property is "foo-gpios"
@ -147,6 +171,7 @@ static int gpio_shared_of_traverse(struct device_node *curr)
entry->offset = offset; entry->offset = offset;
entry->index = count; entry->index = count;
INIT_LIST_HEAD(&entry->refs); INIT_LIST_HEAD(&entry->refs);
mutex_init(&entry->lock);
list_add_tail(&entry->list, &gpio_shared_list); list_add_tail(&entry->list, &gpio_shared_list);
} }
@ -205,7 +230,10 @@ static int gpio_shared_of_traverse(struct device_node *curr)
static int gpio_shared_of_scan(void) static int gpio_shared_of_scan(void)
{ {
return gpio_shared_of_traverse(of_root); if (of_root)
return gpio_shared_of_traverse(of_root);
return 0;
} }
#else #else
static int gpio_shared_of_scan(void) static int gpio_shared_of_scan(void)
@ -220,6 +248,7 @@ static void gpio_shared_adev_release(struct device *dev)
} }
static int gpio_shared_make_adev(struct gpio_device *gdev, static int gpio_shared_make_adev(struct gpio_device *gdev,
struct gpio_shared_entry *entry,
struct gpio_shared_ref *ref) struct gpio_shared_ref *ref)
{ {
struct auxiliary_device *adev = &ref->adev; struct auxiliary_device *adev = &ref->adev;
@ -232,6 +261,7 @@ static int gpio_shared_make_adev(struct gpio_device *gdev,
adev->id = ref->dev_id; adev->id = ref->dev_id;
adev->name = "proxy"; adev->name = "proxy";
adev->dev.parent = gdev->dev.parent; adev->dev.parent = gdev->dev.parent;
adev->dev.platform_data = entry;
adev->dev.release = gpio_shared_adev_release; adev->dev.release = gpio_shared_adev_release;
ret = auxiliary_device_init(adev); ret = auxiliary_device_init(adev);
@ -250,6 +280,84 @@ static int gpio_shared_make_adev(struct gpio_device *gdev,
return 0; return 0;
} }
#if IS_ENABLED(CONFIG_RESET_GPIO)
/*
* Special case: reset-gpio is an auxiliary device that's created dynamically
* and put in between the GPIO controller and consumers of shared GPIOs
* referred to by the "reset-gpios" property.
*
* If the supposed consumer of a shared GPIO didn't match any of the mappings
* we created when scanning the firmware nodes, it's still possible that it's
* the reset-gpio device which didn't exist at the time of the scan.
*
* This function verifies it an return true if it's the case.
*/
static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
struct gpio_shared_entry *entry,
struct gpio_shared_ref *ref)
{
struct fwnode_handle *reset_fwnode = dev_fwnode(consumer);
struct fwnode_reference_args ref_args, aux_args;
struct device *parent = consumer->parent;
bool match;
int ret;
/* The reset-gpio device must have a parent AND a firmware node. */
if (!parent || !reset_fwnode)
return false;
/*
* FIXME: use device_is_compatible() once the reset-gpio drivers gains
* a compatible string which it currently does not have.
*/
if (!strstarts(dev_name(consumer), "reset.gpio."))
return false;
/*
* Parent of the reset-gpio auxiliary device is the GPIO chip whose
* fwnode we stored in the entry structure.
*/
if (!device_match_fwnode(parent, entry->fwnode))
return false;
/*
* The device associated with the shared reference's firmware node is
* the consumer of the reset control exposed by the reset-gpio device.
* It must have a "reset-gpios" property that's referencing the entry's
* firmware node.
*
* The reference args must agree between the real consumer and the
* auxiliary reset-gpio device.
*/
ret = fwnode_property_get_reference_args(ref->fwnode, "reset-gpios",
NULL, 2, 0, &ref_args);
if (ret)
return false;
ret = fwnode_property_get_reference_args(reset_fwnode, "reset-gpios",
NULL, 2, 0, &aux_args);
if (ret) {
fwnode_handle_put(ref_args.fwnode);
return false;
}
match = ((ref_args.fwnode == entry->fwnode) &&
(aux_args.fwnode == entry->fwnode) &&
(ref_args.args[0] == aux_args.args[0]));
fwnode_handle_put(ref_args.fwnode);
fwnode_handle_put(aux_args.fwnode);
return match;
}
#else
static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
struct gpio_shared_entry *entry,
struct gpio_shared_ref *ref)
{
return false;
}
#endif /* CONFIG_RESET_GPIO */
int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags) int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
{ {
const char *dev_id = dev_name(consumer); const char *dev_id = dev_name(consumer);
@ -265,7 +373,8 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
list_for_each_entry(entry, &gpio_shared_list, list) { list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) { list_for_each_entry(ref, &entry->refs, list) {
if (!device_match_fwnode(consumer, ref->fwnode)) if (!device_match_fwnode(consumer, ref->fwnode) &&
!gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
continue; continue;
/* We've already done that on a previous request. */ /* We've already done that on a previous request. */
@ -356,7 +465,7 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
pr_debug("Setting up a shared GPIO entry for %s\n", pr_debug("Setting up a shared GPIO entry for %s\n",
fwnode_get_name(ref->fwnode)); fwnode_get_name(ref->fwnode));
ret = gpio_shared_make_adev(gdev, ref); ret = gpio_shared_make_adev(gdev, entry, ref);
if (ret) if (ret)
return ret; return ret;
} }
@ -390,10 +499,11 @@ static void gpio_shared_release(struct kref *kref)
{ {
struct gpio_shared_entry *entry = struct gpio_shared_entry *entry =
container_of(kref, struct gpio_shared_entry, ref); container_of(kref, struct gpio_shared_entry, ref);
struct gpio_shared_desc *shared_desc = entry->shared_desc; struct gpio_shared_desc *shared_desc;
guard(mutex)(&gpio_shared_lock); guard(mutex)(&entry->lock);
shared_desc = entry->shared_desc;
gpio_device_put(shared_desc->desc->gdev); gpio_device_put(shared_desc->desc->gdev);
if (shared_desc->can_sleep) if (shared_desc->can_sleep)
mutex_destroy(&shared_desc->mutex); mutex_destroy(&shared_desc->mutex);
@ -416,6 +526,8 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry)
struct gpio_shared_desc *shared_desc; struct gpio_shared_desc *shared_desc;
struct gpio_device *gdev; struct gpio_device *gdev;
lockdep_assert_held(&entry->lock);
shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL); shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL);
if (!shared_desc) if (!shared_desc)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -436,57 +548,42 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry)
return shared_desc; return shared_desc;
} }
static struct gpio_shared_entry *gpiod_shared_find(struct auxiliary_device *adev) struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
{ {
struct gpio_shared_desc *shared_desc; struct gpio_shared_desc *shared_desc;
struct gpio_shared_entry *entry; struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref; int ret;
guard(mutex)(&gpio_shared_lock); lockdep_assert_not_held(&gpio_shared_lock);
list_for_each_entry(entry, &gpio_shared_list, list) { entry = dev_get_platdata(dev);
list_for_each_entry(ref, &entry->refs, list) { if (WARN_ON(!entry))
if (adev != &ref->adev) /* Programmer bug */
continue; return ERR_PTR(-ENOENT);
if (entry->shared_desc) {
kref_get(&entry->ref);
return entry;
}
scoped_guard(mutex, &entry->lock) {
if (entry->shared_desc) {
kref_get(&entry->ref);
shared_desc = entry->shared_desc;
} else {
shared_desc = gpiod_shared_desc_create(entry); shared_desc = gpiod_shared_desc_create(entry);
if (IS_ERR(shared_desc)) if (IS_ERR(shared_desc))
return ERR_CAST(shared_desc); return ERR_CAST(shared_desc);
kref_init(&entry->ref); kref_init(&entry->ref);
entry->shared_desc = shared_desc; entry->shared_desc = shared_desc;
pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
dev_name(&adev->dev), gpio_chip_hwgpio(shared_desc->desc),
gpio_device_get_label(shared_desc->desc->gdev));
return entry;
} }
pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
dev_name(dev), gpiod_hwgpio(shared_desc->desc),
gpio_device_get_label(shared_desc->desc->gdev));
} }
return ERR_PTR(-ENOENT);
}
struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
{
struct gpio_shared_entry *entry;
int ret;
entry = gpiod_shared_find(to_auxiliary_dev(dev));
if (IS_ERR(entry))
return ERR_CAST(entry);
ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry); ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry);
if (ret) if (ret)
return ERR_PTR(ret); return ERR_PTR(ret);
return entry->shared_desc; return shared_desc;
} }
EXPORT_SYMBOL_GPL(devm_gpiod_shared_get); EXPORT_SYMBOL_GPL(devm_gpiod_shared_get);
@ -502,6 +599,7 @@ static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
static void gpio_shared_drop_entry(struct gpio_shared_entry *entry) static void gpio_shared_drop_entry(struct gpio_shared_entry *entry)
{ {
list_del(&entry->list); list_del(&entry->list);
mutex_destroy(&entry->lock);
fwnode_handle_put(entry->fwnode); fwnode_handle_put(entry->fwnode);
kfree(entry); kfree(entry);
} }

View File

@ -31,7 +31,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
gdev_node = to_software_node(fwnode); gdev_node = to_software_node(fwnode);
if (!gdev_node || !gdev_node->name) if (!gdev_node || !gdev_node->name)
return ERR_PTR(-EINVAL); goto fwnode_lookup;
/* /*
* Check for a special node that identifies undefined GPIOs, this is * Check for a special node that identifies undefined GPIOs, this is
@ -41,6 +41,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
!strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME)) !strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME))
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
fwnode_lookup:
gdev = gpio_device_find_by_fwnode(fwnode); gdev = gpio_device_find_by_fwnode(fwnode);
return gdev ?: ERR_PTR(-EPROBE_DEFER); return gdev ?: ERR_PTR(-EPROBE_DEFER);
} }

View File

@ -244,7 +244,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
* Remove this redundant call (along with the corresponding unlock) * Remove this redundant call (along with the corresponding unlock)
* when those drivers have been fixed. * when those drivers have been fixed.
*/ */
ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); ret = gpiochip_lock_as_irq(guard.gc, gpiod_hwgpio(desc));
if (ret < 0) if (ret < 0)
goto err_clr_bits; goto err_clr_bits;
@ -258,7 +258,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
return 0; return 0;
err_unlock: err_unlock:
gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc));
err_clr_bits: err_clr_bits:
clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags); clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags); clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
@ -280,7 +280,7 @@ static void gpio_sysfs_free_irq(struct gpiod_data *data)
data->irq_flags = 0; data->irq_flags = 0;
free_irq(data->irq, data); free_irq(data->irq, data);
gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc));
clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags); clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags); clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
} }
@ -478,10 +478,10 @@ static int export_gpio_desc(struct gpio_desc *desc)
if (!guard.gc) if (!guard.gc)
return -ENODEV; return -ENODEV;
offset = gpio_chip_hwgpio(desc); offset = gpiod_hwgpio(desc);
if (!gpiochip_line_is_valid(guard.gc, offset)) { if (!gpiochip_line_is_valid(guard.gc, offset)) {
pr_debug_ratelimited("%s: GPIO %d masked\n", __func__, pr_debug_ratelimited("%s: GPIO %d masked\n", __func__,
gpio_chip_hwgpio(desc)); gpiod_hwgpio(desc));
return -EINVAL; return -EINVAL;
} }
@ -823,7 +823,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
} }
desc_data->chip_attr_group.name = kasprintf(GFP_KERNEL, "gpio%u", desc_data->chip_attr_group.name = kasprintf(GFP_KERNEL, "gpio%u",
gpio_chip_hwgpio(desc)); gpiod_hwgpio(desc));
if (!desc_data->chip_attr_group.name) { if (!desc_data->chip_attr_group.name) {
status = -ENOMEM; status = -ENOMEM;
goto err_put_dirent; goto err_put_dirent;
@ -843,7 +843,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
if (status) if (status)
goto err_free_name; goto err_free_name;
path = kasprintf(GFP_KERNEL, "gpio%u/value", gpio_chip_hwgpio(desc)); path = kasprintf(GFP_KERNEL, "gpio%u/value", gpiod_hwgpio(desc));
if (!path) { if (!path) {
status = -ENOMEM; status = -ENOMEM;
goto err_remove_groups; goto err_remove_groups;
@ -1091,7 +1091,7 @@ static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data)
ret = gpiochip_sysfs_register(gdev); ret = gpiochip_sysfs_register(gdev);
if (ret) if (ret)
chip_err(gc, "failed to register the sysfs entry: %d\n", ret); gpiochip_err(gc, "failed to register the sysfs entry: %d\n", ret);
return 0; return 0;
} }

View File

@ -236,6 +236,19 @@ int desc_to_gpio(const struct gpio_desc *desc)
} }
EXPORT_SYMBOL_GPL(desc_to_gpio); EXPORT_SYMBOL_GPL(desc_to_gpio);
/**
* gpiod_hwgpio - Return the GPIO number of the passed descriptor relative to
* its chip.
* @desc: GPIO descriptor
*
* Returns:
* Hardware offset of the GPIO represented by the descriptor.
*/
int gpiod_hwgpio(const struct gpio_desc *desc)
{
return desc - &desc->gdev->descs[0];
}
EXPORT_SYMBOL_GPL(gpiod_hwgpio);
/** /**
* gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs * gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs
@ -444,7 +457,7 @@ int gpiod_get_direction(struct gpio_desc *desc)
if (!guard.gc) if (!guard.gc)
return -ENODEV; return -ENODEV;
offset = gpio_chip_hwgpio(desc); offset = gpiod_hwgpio(desc);
flags = READ_ONCE(desc->flags); flags = READ_ONCE(desc->flags);
/* /*
@ -922,8 +935,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
desc = gpiochip_get_desc(gc, hog->chip_hwnum); desc = gpiochip_get_desc(gc, hog->chip_hwnum);
if (IS_ERR(desc)) { if (IS_ERR(desc)) {
chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__, gpiochip_err(gc, "%s: unable to get GPIO desc: %ld\n",
PTR_ERR(desc)); __func__, PTR_ERR(desc));
return; return;
} }
@ -1125,7 +1138,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = gpiodev_add_to_list_unlocked(gdev); ret = gpiodev_add_to_list_unlocked(gdev);
if (ret) { if (ret) {
chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n");
goto err_free_label; goto err_free_label;
} }
} }
@ -1537,8 +1550,7 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc,
&parent_hwirq, &parent_hwirq,
&parent_type); &parent_type);
if (ret) { if (ret) {
chip_err(gc, "skip set-up on hwirq %d\n", gpiochip_err(gc, "skip set-up on hwirq %d\n", i);
i);
continue; continue;
} }
@ -1551,15 +1563,14 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc,
ret = irq_domain_alloc_irqs(gc->irq.domain, 1, ret = irq_domain_alloc_irqs(gc->irq.domain, 1,
NUMA_NO_NODE, &fwspec); NUMA_NO_NODE, &fwspec);
if (ret < 0) { if (ret < 0) {
chip_err(gc, gpiochip_err(gc,
"can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n",
i, parent_hwirq, i, parent_hwirq, ret);
ret);
} }
} }
} }
chip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__); gpiochip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__);
return; return;
} }
@ -1611,15 +1622,15 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
if (ret) if (ret)
return ret; return ret;
chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); gpiochip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq);
ret = girq->child_to_parent_hwirq(gc, hwirq, type, ret = girq->child_to_parent_hwirq(gc, hwirq, type,
&parent_hwirq, &parent_type); &parent_hwirq, &parent_type);
if (ret) { if (ret) {
chip_err(gc, "can't look up hwirq %lu\n", hwirq); gpiochip_err(gc, "can't look up hwirq %lu\n", hwirq);
return ret; return ret;
} }
chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq); gpiochip_dbg(gc, "found parent hwirq %u\n", parent_hwirq);
/* /*
* We set handle_bad_irq because the .set_type() should * We set handle_bad_irq because the .set_type() should
@ -1640,8 +1651,8 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
if (ret) if (ret)
return ret; return ret;
chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", gpiochip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
irq, parent_hwirq); irq, parent_hwirq);
irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key); irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key);
ret = irq_domain_alloc_irqs_parent(d, irq, 1, &gpio_parent_fwspec); ret = irq_domain_alloc_irqs_parent(d, irq, 1, &gpio_parent_fwspec);
/* /*
@ -1651,9 +1662,9 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
if (irq_domain_is_msi(d->parent) && (ret == -EEXIST)) if (irq_domain_is_msi(d->parent) && (ret == -EEXIST))
ret = 0; ret = 0;
if (ret) if (ret)
chip_err(gc, gpiochip_err(gc,
"failed to allocate parent hwirq %d for hwirq %lu\n", "failed to allocate parent hwirq %d for hwirq %lu\n",
parent_hwirq, hwirq); parent_hwirq, hwirq);
return ret; return ret;
} }
@ -1729,7 +1740,7 @@ static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc)
if (!gc->irq.child_to_parent_hwirq || if (!gc->irq.child_to_parent_hwirq ||
!gc->irq.fwnode) { !gc->irq.fwnode) {
chip_err(gc, "missing irqdomain vital data\n"); gpiochip_err(gc, "missing irqdomain vital data\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
@ -2002,7 +2013,7 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
if (irqchip->flags & IRQCHIP_IMMUTABLE) if (irqchip->flags & IRQCHIP_IMMUTABLE)
return; return;
chip_warn(gc, "not an immutable chip, please consider fixing it!\n"); gpiochip_warn(gc, "not an immutable chip, please consider fixing it!\n");
if (!irqchip->irq_request_resources && if (!irqchip->irq_request_resources &&
!irqchip->irq_release_resources) { !irqchip->irq_release_resources) {
@ -2018,8 +2029,8 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
* ...and if so, give a gentle warning that this is bad * ...and if so, give a gentle warning that this is bad
* practice. * practice.
*/ */
chip_info(gc, gpiochip_info(gc,
"detected irqchip that is shared with multiple gpiochips: please fix the driver.\n"); "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
return; return;
} }
@ -2048,7 +2059,8 @@ static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc,
return -EINVAL; return -EINVAL;
if (gc->to_irq) if (gc->to_irq)
chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__); gpiochip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n",
__func__);
gc->to_irq = gpiochip_to_irq; gc->to_irq = gpiochip_to_irq;
gc->irq.domain = domain; gc->irq.domain = domain;
@ -2089,7 +2101,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
return 0; return 0;
if (gc->irq.parent_handler && gc->can_sleep) { if (gc->irq.parent_handler && gc->can_sleep) {
chip_err(gc, "you cannot have chained interrupts on a chip that may sleep\n"); gpiochip_err(gc, "you cannot have chained interrupts on a chip that may sleep\n");
return -EINVAL; return -EINVAL;
} }
@ -2325,10 +2337,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc,
int ret; int ret;
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
if (!pin_range) { if (!pin_range)
chip_err(gc, "failed to allocate pin ranges\n");
return -ENOMEM; return -ENOMEM;
}
/* Use local offset as range ID */ /* Use local offset as range ID */
pin_range->range.id = gpio_offset; pin_range->range.id = gpio_offset;
@ -2347,7 +2357,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc,
pinctrl_add_gpio_range(pctldev, &pin_range->range); pinctrl_add_gpio_range(pctldev, &pin_range->range);
chip_dbg(gc, "created GPIO range %d->%d ==> %s PINGRP %s\n", gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s PINGRP %s\n",
gpio_offset, gpio_offset + pin_range->range.npins - 1, gpio_offset, gpio_offset + pin_range->range.npins - 1,
pinctrl_dev_get_devname(pctldev), pin_group); pinctrl_dev_get_devname(pctldev), pin_group);
@ -2388,10 +2398,8 @@ int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc,
int ret; int ret;
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
if (!pin_range) { if (!pin_range)
chip_err(gc, "failed to allocate pin ranges\n");
return -ENOMEM; return -ENOMEM;
}
/* Use local offset as range ID */ /* Use local offset as range ID */
pin_range->range.id = gpio_offset; pin_range->range.id = gpio_offset;
@ -2405,19 +2413,18 @@ int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc,
&pin_range->range); &pin_range->range);
if (IS_ERR(pin_range->pctldev)) { if (IS_ERR(pin_range->pctldev)) {
ret = PTR_ERR(pin_range->pctldev); ret = PTR_ERR(pin_range->pctldev);
chip_err(gc, "could not create pin range\n"); gpiochip_err(gc, "could not create pin range\n");
kfree(pin_range); kfree(pin_range);
return ret; return ret;
} }
if (pin_range->range.pins) if (pin_range->range.pins)
chip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }", gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }",
gpio_offset, gpio_offset + npins - 1, gpio_offset, gpio_offset + npins - 1,
pinctl_name, npins, pins[0]); pinctl_name, npins, pins[0]);
else else
chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n", gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
gpio_offset, gpio_offset + npins - 1, gpio_offset, gpio_offset + npins - 1, pinctl_name,
pinctl_name, pin_offset, pin_offset + npins - 1);
pin_offset, pin_offset + npins - 1);
list_add_tail(&pin_range->node, &gdev->pin_ranges); list_add_tail(&pin_range->node, &gdev->pin_ranges);
@ -2461,7 +2468,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
if (test_and_set_bit(GPIOD_FLAG_REQUESTED, &desc->flags)) if (test_and_set_bit(GPIOD_FLAG_REQUESTED, &desc->flags))
return -EBUSY; return -EBUSY;
offset = gpio_chip_hwgpio(desc); offset = gpiod_hwgpio(desc);
if (!gpiochip_line_is_valid(guard.gc, offset)) if (!gpiochip_line_is_valid(guard.gc, offset))
return -EINVAL; return -EINVAL;
@ -2523,7 +2530,7 @@ static void gpiod_free_commit(struct gpio_desc *desc)
if (guard.gc && test_bit(GPIOD_FLAG_REQUESTED, &flags)) { if (guard.gc && test_bit(GPIOD_FLAG_REQUESTED, &flags)) {
if (guard.gc->free) if (guard.gc->free)
guard.gc->free(guard.gc, gpio_chip_hwgpio(desc)); guard.gc->free(guard.gc, gpiod_hwgpio(desc));
clear_bit(GPIOD_FLAG_ACTIVE_LOW, &flags); clear_bit(GPIOD_FLAG_ACTIVE_LOW, &flags);
clear_bit(GPIOD_FLAG_REQUESTED, &flags); clear_bit(GPIOD_FLAG_REQUESTED, &flags);
@ -2627,7 +2634,7 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
int ret; int ret;
if (IS_ERR(desc)) { if (IS_ERR(desc)) {
chip_err(gc, "failed to get GPIO %s descriptor\n", name); gpiochip_err(gc, "failed to get GPIO %s descriptor\n", name);
return desc; return desc;
} }
@ -2638,7 +2645,7 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
ret = gpiod_configure_flags(desc, label, lflags, dflags); ret = gpiod_configure_flags(desc, label, lflags, dflags);
if (ret) { if (ret) {
gpiod_free_commit(desc); gpiod_free_commit(desc);
chip_err(gc, "setup of own GPIO %s failed\n", name); gpiochip_err(gc, "setup of own GPIO %s failed\n", name);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
@ -2683,7 +2690,7 @@ int gpio_do_set_config(struct gpio_desc *desc, unsigned long config)
if (!guard.gc->set_config) if (!guard.gc->set_config)
return -ENOTSUPP; return -ENOTSUPP;
ret = guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config); ret = guard.gc->set_config(guard.gc, gpiod_hwgpio(desc), config);
if (ret > 0) if (ret > 0)
ret = -EBADE; ret = -EBADE;
@ -2714,7 +2721,7 @@ static int gpio_set_config_with_argument_optional(struct gpio_desc *desc,
u32 argument) u32 argument)
{ {
struct device *dev = &desc->gdev->dev; struct device *dev = &desc->gdev->dev;
int gpio = gpio_chip_hwgpio(desc); int gpio = gpiod_hwgpio(desc);
int ret; int ret;
ret = gpio_set_config_with_argument(desc, mode, argument); ret = gpio_set_config_with_argument(desc, mode, argument);
@ -2877,9 +2884,9 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc)
*/ */
if (guard.gc->direction_input) { if (guard.gc->direction_input) {
ret = gpiochip_direction_input(guard.gc, ret = gpiochip_direction_input(guard.gc,
gpio_chip_hwgpio(desc)); gpiod_hwgpio(desc));
} else if (guard.gc->get_direction) { } else if (guard.gc->get_direction) {
dir = gpiochip_get_direction(guard.gc, gpio_chip_hwgpio(desc)); dir = gpiochip_get_direction(guard.gc, gpiod_hwgpio(desc));
if (dir < 0) if (dir < 0)
return dir; return dir;
@ -2938,12 +2945,12 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
if (guard.gc->direction_output) { if (guard.gc->direction_output) {
ret = gpiochip_direction_output(guard.gc, ret = gpiochip_direction_output(guard.gc,
gpio_chip_hwgpio(desc), val); gpiod_hwgpio(desc), val);
} else { } else {
/* Check that we are in output mode if we can */ /* Check that we are in output mode if we can */
if (guard.gc->get_direction) { if (guard.gc->get_direction) {
dir = gpiochip_get_direction(guard.gc, dir = gpiochip_get_direction(guard.gc,
gpio_chip_hwgpio(desc)); gpiod_hwgpio(desc));
if (dir < 0) if (dir < 0)
return dir; return dir;
@ -2958,7 +2965,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
* If we can't actively set the direction, we are some * If we can't actively set the direction, we are some
* output-only chip, so just drive the output as desired. * output-only chip, so just drive the output as desired.
*/ */
ret = gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), val); ret = gpiochip_set(guard.gc, gpiod_hwgpio(desc), val);
if (ret) if (ret)
return ret; return ret;
} }
@ -3109,7 +3116,7 @@ int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
} }
ret = guard.gc->en_hw_timestamp(guard.gc, ret = guard.gc->en_hw_timestamp(guard.gc,
gpio_chip_hwgpio(desc), flags); gpiod_hwgpio(desc), flags);
if (ret) if (ret)
gpiod_warn(desc, "%s: hw ts request failed\n", __func__); gpiod_warn(desc, "%s: hw ts request failed\n", __func__);
@ -3141,7 +3148,7 @@ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
return -ENOTSUPP; return -ENOTSUPP;
} }
ret = guard.gc->dis_hw_timestamp(guard.gc, gpio_chip_hwgpio(desc), ret = guard.gc->dis_hw_timestamp(guard.gc, gpiod_hwgpio(desc),
flags); flags);
if (ret) if (ret)
gpiod_warn(desc, "%s: hw ts release failed\n", __func__); gpiod_warn(desc, "%s: hw ts release failed\n", __func__);
@ -3272,7 +3279,7 @@ static int gpiochip_get(struct gpio_chip *gc, unsigned int offset)
static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc) static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc)
{ {
return gc->get ? gpiochip_get(gc, gpio_chip_hwgpio(desc)) : -EIO; return gc->get ? gpiochip_get(gc, gpiod_hwgpio(desc)) : -EIO;
} }
/* I/O calls are only valid after configuration completed; the relevant /* I/O calls are only valid after configuration completed; the relevant
@ -3432,7 +3439,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
first = i; first = i;
do { do {
const struct gpio_desc *desc = desc_array[i]; const struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc); int hwgpio = gpiod_hwgpio(desc);
__set_bit(hwgpio, mask); __set_bit(hwgpio, mask);
i++; i++;
@ -3454,7 +3461,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
for (j = first; j < i; ) { for (j = first; j < i; ) {
const struct gpio_desc *desc = desc_array[j]; const struct gpio_desc *desc = desc_array[j];
int hwgpio = gpio_chip_hwgpio(desc); int hwgpio = gpiod_hwgpio(desc);
int value = test_bit(hwgpio, bits); int value = test_bit(hwgpio, bits);
if (!raw && test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags)) if (!raw && test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags))
@ -3591,7 +3598,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value);
*/ */
static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
{ {
int ret = 0, offset = gpio_chip_hwgpio(desc); int ret = 0, offset = gpiod_hwgpio(desc);
CLASS(gpio_chip_guard, guard)(desc); CLASS(gpio_chip_guard, guard)(desc);
if (!guard.gc) if (!guard.gc)
@ -3620,7 +3627,7 @@ static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
*/ */
static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
{ {
int ret = 0, offset = gpio_chip_hwgpio(desc); int ret = 0, offset = gpiod_hwgpio(desc);
CLASS(gpio_chip_guard, guard)(desc); CLASS(gpio_chip_guard, guard)(desc);
if (!guard.gc) if (!guard.gc)
@ -3652,7 +3659,7 @@ static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
return -ENODEV; return -ENODEV;
trace_gpio_value(desc_to_gpio(desc), 0, value); trace_gpio_value(desc_to_gpio(desc), 0, value);
return gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), value); return gpiochip_set(guard.gc, gpiod_hwgpio(desc), value);
} }
/* /*
@ -3775,7 +3782,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
do { do {
struct gpio_desc *desc = desc_array[i]; struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc); int hwgpio = gpiod_hwgpio(desc);
int value = test_bit(i, value_bitmap); int value = test_bit(i, value_bitmap);
if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags))) if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags)))
@ -4035,7 +4042,7 @@ int gpiod_to_irq(const struct gpio_desc *desc)
if (!gc) if (!gc)
return -ENODEV; return -ENODEV;
offset = gpio_chip_hwgpio(desc); offset = gpiod_hwgpio(desc);
if (gc->to_irq) { if (gc->to_irq) {
ret = gc->to_irq(gc, offset); ret = gc->to_irq(gc, offset);
if (ret) if (ret)
@ -4085,8 +4092,8 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset)
int dir = gpiod_get_direction(desc); int dir = gpiod_get_direction(desc);
if (dir < 0) { if (dir < 0) {
chip_err(gc, "%s: cannot get GPIO direction\n", gpiochip_err(gc, "%s: cannot get GPIO direction\n",
__func__); __func__);
return dir; return dir;
} }
} }
@ -4094,9 +4101,9 @@ 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 */ /* To be valid for IRQ the line needs to be input or open drain */
if (test_bit(GPIOD_FLAG_IS_OUT, &desc->flags) && if (test_bit(GPIOD_FLAG_IS_OUT, &desc->flags) &&
!test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags)) { !test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags)) {
chip_err(gc, gpiochip_err(gc,
"%s: tried to flag a GPIO set as output for IRQ\n", "%s: tried to flag a GPIO set as output for IRQ\n",
__func__); __func__);
return -EIO; return -EIO;
} }
@ -4173,7 +4180,7 @@ int gpiochip_reqres_irq(struct gpio_chip *gc, unsigned int offset)
ret = gpiochip_lock_as_irq(gc, offset); ret = gpiochip_lock_as_irq(gc, offset);
if (ret) { if (ret) {
chip_err(gc, "unable to lock HW IRQ %u for IRQ\n", offset); gpiochip_err(gc, "unable to lock HW IRQ %u for IRQ\n", offset);
module_put(gc->gpiodev->owner); module_put(gc->gpiodev->owner);
return ret; return ret;
} }
@ -5015,7 +5022,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
if (test_and_set_bit(GPIOD_FLAG_IS_HOGGED, &desc->flags)) if (test_and_set_bit(GPIOD_FLAG_IS_HOGGED, &desc->flags))
return 0; return 0;
hwnum = gpio_chip_hwgpio(desc); hwnum = gpiod_hwgpio(desc);
local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name, local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name,
lflags, dflags); lflags, dflags);
@ -5096,7 +5103,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
* If pin hardware number of array member 0 is also 0, select * If pin hardware number of array member 0 is also 0, select
* its chip as a candidate for fast bitmap processing path. * its chip as a candidate for fast bitmap processing path.
*/ */
if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) { if (descs->ndescs == 0 && gpiod_hwgpio(desc) == 0) {
struct gpio_descs *array; struct gpio_descs *array;
bitmap_size = BITS_TO_LONGS(gdev->ngpio > count ? bitmap_size = BITS_TO_LONGS(gdev->ngpio > count ?
@ -5141,7 +5148,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
* Detect array members which belong to the 'fast' chip * Detect array members which belong to the 'fast' chip
* but their pins are not in hardware order. * but their pins are not in hardware order.
*/ */
else if (gpio_chip_hwgpio(desc) != descs->ndescs) { else if (gpiod_hwgpio(desc) != descs->ndescs) {
/* /*
* Don't use fast path if all array members processed so * Don't use fast path if all array members processed so
* far belong to the same chip as this one but its pin * far belong to the same chip as this one but its pin

View File

@ -275,49 +275,30 @@ int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum); struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
const char *gpiod_get_label(struct gpio_desc *desc); const char *gpiod_get_label(struct gpio_desc *desc);
/*
* Return the GPIO number of the passed descriptor relative to its chip
*/
static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
{
return desc - &desc->gdev->descs[0];
}
/* With descriptor prefix */ /* With descriptor prefix */
#define gpiod_err(desc, fmt, ...) \ #define __gpiod_pr(level, desc, fmt, ...) \
do { \ do { \
scoped_guard(srcu, &desc->gdev->desc_srcu) { \ scoped_guard(srcu, &desc->gdev->desc_srcu) { \
pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ pr_##level("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ gpiod_get_label(desc) ?: "?", ##__VA_ARGS__); \
} \ } \
} while (0) } while (0)
#define gpiod_warn(desc, fmt, ...) \ #define gpiod_err(desc, fmt, ...) __gpiod_pr(err, desc, fmt, ##__VA_ARGS__)
do { \ #define gpiod_warn(desc, fmt, ...) __gpiod_pr(warn, desc, fmt, ##__VA_ARGS__)
scoped_guard(srcu, &desc->gdev->desc_srcu) { \ #define gpiod_dbg(desc, fmt, ...) __gpiod_pr(debug, desc, fmt, ##__VA_ARGS__)
pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
} \
} while (0)
#define gpiod_dbg(desc, fmt, ...) \
do { \
scoped_guard(srcu, &desc->gdev->desc_srcu) { \
pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
} \
} while (0)
/* With chip prefix */ /* With chip prefix */
#define chip_err(gc, fmt, ...) \ #define __gpiochip_pr(level, gc, fmt, ...) \
dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) do { \
#define chip_warn(gc, fmt, ...) \ dev_##level(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__); \
dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) } while (0)
#define chip_info(gc, fmt, ...) \
dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) #define gpiochip_err(gc, fmt, ...) __gpiochip_pr(err, gc, fmt, ##__VA_ARGS__)
#define chip_dbg(gc, fmt, ...) \ #define gpiochip_warn(gc, fmt, ...) __gpiochip_pr(warn, gc, fmt, ##__VA_ARGS__)
dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) #define gpiochip_info(gc, fmt, ...) __gpiochip_pr(info, gc, fmt, ##__VA_ARGS__)
#define gpiochip_dbg(gc, fmt, ...) __gpiochip_pr(dbg, gc, fmt, ##__VA_ARGS__)
#endif /* GPIOLIB_H */ #endif /* GPIOLIB_H */

View File

@ -41,6 +41,19 @@ config INTEL_VBTN
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called intel_vbtn. be called intel_vbtn.
config INTEL_EHL_PSE_IO
tristate "Intel Elkhart Lake PSE I/O driver"
depends on PCI
select AUXILIARY_BUS
help
Select this option to enable Intel Elkhart Lake PSE GPIO and Timed
I/O support. This driver enumerates the PCI parent device and
creates auxiliary child devices for these capabilities. The actual
functionalities are provided by their respective auxiliary drivers.
To compile this driver as a module, choose M here: the module will
be called intel_ehl_pse_io.
config INTEL_INT0002_VGPIO config INTEL_INT0002_VGPIO
tristate "Intel ACPI INT0002 Virtual GPIO driver" tristate "Intel ACPI INT0002 Virtual GPIO driver"
depends on GPIOLIB && ACPI && PM_SLEEP depends on GPIOLIB && ACPI && PM_SLEEP

View File

@ -21,6 +21,7 @@ intel-target-$(CONFIG_INTEL_HID_EVENT) += hid.o
intel-target-$(CONFIG_INTEL_VBTN) += vbtn.o intel-target-$(CONFIG_INTEL_VBTN) += vbtn.o
# Intel miscellaneous drivers # Intel miscellaneous drivers
intel-target-$(CONFIG_INTEL_EHL_PSE_IO) += ehl_pse_io.o
intel-target-$(CONFIG_INTEL_INT0002_VGPIO) += int0002_vgpio.o intel-target-$(CONFIG_INTEL_INT0002_VGPIO) += int0002_vgpio.o
intel-target-$(CONFIG_INTEL_ISHTP_ECLITE) += ishtp_eclite.o intel-target-$(CONFIG_INTEL_ISHTP_ECLITE) += ishtp_eclite.o
intel-target-$(CONFIG_INTEL_OAKTRAIL) += oaktrail.o intel-target-$(CONFIG_INTEL_OAKTRAIL) += oaktrail.o

View File

@ -77,7 +77,7 @@ static const struct software_node max17047_node = {
* software node. * software node.
*/ */
static struct software_node_ref_args fusb302_mux_refs[] = { static struct software_node_ref_args fusb302_mux_refs[] = {
{ .node = NULL }, SOFTWARE_NODE_REFERENCE(NULL),
}; };
static const struct property_entry fusb302_properties[] = { static const struct property_entry fusb302_properties[] = {
@ -190,11 +190,6 @@ static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)
{ {
software_node_unregister_node_group(node_group); software_node_unregister_node_group(node_group);
if (fusb302_mux_refs[0].node) {
fwnode_handle_put(software_node_fwnode(fusb302_mux_refs[0].node));
fusb302_mux_refs[0].node = NULL;
}
if (data->dp) { if (data->dp) {
data->dp->secondary = NULL; data->dp->secondary = NULL;
fwnode_handle_put(data->dp); fwnode_handle_put(data->dp);
@ -202,7 +197,15 @@ static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)
} }
} }
static int cht_int33fe_add_nodes(struct cht_int33fe_data *data) static void cht_int33fe_put_swnode(void *data)
{
struct fwnode_handle *fwnode = data;
fwnode_handle_put(fwnode);
fusb302_mux_refs[0] = SOFTWARE_NODE_REFERENCE(NULL);
}
static int cht_int33fe_add_nodes(struct device *dev, struct cht_int33fe_data *data)
{ {
const struct software_node *mux_ref_node; const struct software_node *mux_ref_node;
int ret; int ret;
@ -212,17 +215,25 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data)
* until the mux driver has created software node for the mux device. * until the mux driver has created software node for the mux device.
* It means we depend on the mux driver. This function will return * It means we depend on the mux driver. This function will return
* -EPROBE_DEFER until the mux device is registered. * -EPROBE_DEFER until the mux device is registered.
*
* FIXME: the relevant software node exists in intel-xhci-usb-role-switch
* and - if exported - could be used to set up a static reference.
*/ */
mux_ref_node = software_node_find_by_name(NULL, "intel-xhci-usb-sw"); mux_ref_node = software_node_find_by_name(NULL, "intel-xhci-usb-sw");
if (!mux_ref_node) if (!mux_ref_node)
return -EPROBE_DEFER; return -EPROBE_DEFER;
ret = devm_add_action_or_reset(dev, cht_int33fe_put_swnode,
software_node_fwnode(mux_ref_node));
if (ret)
return ret;
/* /*
* Update node used in "usb-role-switch" property. Note that we * Update node used in "usb-role-switch" property. Note that we
* rely on software_node_register_node_group() to use the original * rely on software_node_register_node_group() to use the original
* instance of properties instead of copying them. * instance of properties instead of copying them.
*/ */
fusb302_mux_refs[0].node = mux_ref_node; fusb302_mux_refs[0] = SOFTWARE_NODE_REFERENCE(mux_ref_node);
ret = software_node_register_node_group(node_group); ret = software_node_register_node_group(node_group);
if (ret) if (ret)
@ -345,7 +356,7 @@ static int cht_int33fe_typec_probe(struct platform_device *pdev)
return fusb302_irq; return fusb302_irq;
} }
ret = cht_int33fe_add_nodes(data); ret = cht_int33fe_add_nodes(dev, data);
if (ret) if (ret)
return ret; return ret;

View File

@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Elkhart Lake Programmable Service Engine (PSE) I/O
*
* Copyright (c) 2025 Intel Corporation.
*
* Author: Raag Jadav <raag.jadav@intel.com>
*/
#include <linux/auxiliary_bus.h>
#include <linux/device/devres.h>
#include <linux/errno.h>
#include <linux/gfp_types.h>
#include <linux/ioport.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sizes.h>
#include <linux/types.h>
#include <linux/ehl_pse_io_aux.h>
#define EHL_PSE_IO_DEV_SIZE SZ_4K
static int ehl_pse_io_dev_create(struct pci_dev *pci, const char *name, int idx)
{
struct device *dev = &pci->dev;
struct auxiliary_device *adev;
struct ehl_pse_io_data *data;
resource_size_t start, offset;
u32 id;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci);
start = pci_resource_start(pci, 0);
offset = EHL_PSE_IO_DEV_SIZE * idx;
data->mem = DEFINE_RES_MEM(start + offset, EHL_PSE_IO_DEV_SIZE);
data->irq = pci_irq_vector(pci, idx);
adev = __devm_auxiliary_device_create(dev, EHL_PSE_IO_NAME, name, data, id);
return adev ? 0 : -ENODEV;
}
static int ehl_pse_io_probe(struct pci_dev *pci, const struct pci_device_id *id)
{
int ret;
ret = pcim_enable_device(pci);
if (ret)
return ret;
pci_set_master(pci);
ret = pci_alloc_irq_vectors(pci, 2, 2, PCI_IRQ_MSI);
if (ret < 0)
return ret;
ret = ehl_pse_io_dev_create(pci, EHL_PSE_GPIO_NAME, 0);
if (ret)
return ret;
return ehl_pse_io_dev_create(pci, EHL_PSE_TIO_NAME, 1);
}
static const struct pci_device_id ehl_pse_io_ids[] = {
{ PCI_VDEVICE(INTEL, 0x4b88) },
{ PCI_VDEVICE(INTEL, 0x4b89) },
{ }
};
MODULE_DEVICE_TABLE(pci, ehl_pse_io_ids);
static struct pci_driver ehl_pse_io_driver = {
.name = EHL_PSE_IO_NAME,
.id_table = ehl_pse_io_ids,
.probe = ehl_pse_io_probe,
};
module_pci_driver(ehl_pse_io_driver);
MODULE_AUTHOR("Raag Jadav <raag.jadav@intel.com>");
MODULE_DESCRIPTION("Intel Elkhart Lake PSE I/O driver");
MODULE_LICENSE("GPL");

View File

@ -89,6 +89,7 @@ config RESET_EYEQ
config RESET_GPIO config RESET_GPIO
tristate "GPIO reset controller" tristate "GPIO reset controller"
depends on GPIOLIB depends on GPIOLIB
select AUXILIARY_BUS
help help
This enables a generic reset controller for resets attached via This enables a generic reset controller for resets attached via
GPIOs. Typically for OF platforms this driver expects "reset-gpios" GPIOs. Typically for OF platforms this driver expects "reset-gpios"

View File

@ -4,20 +4,22 @@
* *
* Copyright 2013 Philipp Zabel, Pengutronix * Copyright 2013 Philipp Zabel, Pengutronix
*/ */
#include <linux/acpi.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/auxiliary_bus.h>
#include <linux/cleanup.h> #include <linux/cleanup.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/gpio/machine.h> #include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -76,10 +78,12 @@ struct reset_control_array {
/** /**
* struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices
* @of_args: phandle to the reset controller with all the args like GPIO number * @of_args: phandle to the reset controller with all the args like GPIO number
* @swnode: Software node containing the reference to the GPIO provider
* @list: list entry for the reset_gpio_lookup_list * @list: list entry for the reset_gpio_lookup_list
*/ */
struct reset_gpio_lookup { struct reset_gpio_lookup {
struct of_phandle_args of_args; struct of_phandle_args of_args;
struct fwnode_handle *swnode;
struct list_head list; struct list_head list;
}; };
@ -848,56 +852,45 @@ static void __reset_control_put_internal(struct reset_control *rstc)
kref_put(&rstc->refcnt, __reset_control_release); kref_put(&rstc->refcnt, __reset_control_release);
} }
static int __reset_add_reset_gpio_lookup(int id, struct device_node *np, static void reset_gpio_aux_device_release(struct device *dev)
unsigned int gpio,
unsigned int of_flags)
{ {
const struct fwnode_handle *fwnode = of_fwnode_handle(np); struct auxiliary_device *adev = to_auxiliary_dev(dev);
unsigned int lookup_flags;
const char *label_tmp;
/* kfree(adev);
* Later we map GPIO flags between OF and Linux, however not all }
* constants from include/dt-bindings/gpio/gpio.h and
* include/linux/gpio/machine.h match each other. static int reset_add_gpio_aux_device(struct device *parent,
*/ struct fwnode_handle *swnode,
if (of_flags > GPIO_ACTIVE_LOW) { int id, void *pdata)
pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n", {
of_flags, gpio); struct auxiliary_device *adev;
return -EINVAL; int ret;
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
if (!adev)
return -ENOMEM;
adev->id = id;
adev->name = "gpio";
adev->dev.parent = parent;
adev->dev.platform_data = pdata;
adev->dev.release = reset_gpio_aux_device_release;
device_set_node(&adev->dev, swnode);
ret = auxiliary_device_init(adev);
if (ret) {
kfree(adev);
return ret;
} }
struct gpio_device *gdev __free(gpio_device_put) = gpio_device_find_by_fwnode(fwnode); ret = __auxiliary_device_add(adev, "reset");
if (!gdev) if (ret) {
return -EPROBE_DEFER; auxiliary_device_uninit(adev);
kfree(adev);
return ret;
}
label_tmp = gpio_device_get_label(gdev); return ret;
if (!label_tmp)
return -EINVAL;
char *label __free(kfree) = kstrdup(label_tmp, GFP_KERNEL);
if (!label)
return -ENOMEM;
/* Size: one lookup entry plus sentinel */
struct gpiod_lookup_table *lookup __free(kfree) = kzalloc(struct_size(lookup, table, 2),
GFP_KERNEL);
if (!lookup)
return -ENOMEM;
lookup->dev_id = kasprintf(GFP_KERNEL, "reset-gpio.%d", id);
if (!lookup->dev_id)
return -ENOMEM;
lookup_flags = GPIO_PERSISTENT;
lookup_flags |= of_flags & GPIO_ACTIVE_LOW;
lookup->table[0] = GPIO_LOOKUP(no_free_ptr(label), gpio, "reset",
lookup_flags);
/* Not freed on success, because it is persisent subsystem data. */
gpiod_add_lookup_table(no_free_ptr(lookup));
return 0;
} }
/* /*
@ -905,8 +898,10 @@ static int __reset_add_reset_gpio_lookup(int id, struct device_node *np,
*/ */
static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
{ {
struct property_entry properties[2] = { };
unsigned int offset, of_flags, lflags;
struct reset_gpio_lookup *rgpio_dev; struct reset_gpio_lookup *rgpio_dev;
struct platform_device *pdev; struct device *parent;
int id, ret; int id, ret;
/* /*
@ -925,6 +920,28 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
*/ */
lockdep_assert_not_held(&reset_list_mutex); lockdep_assert_not_held(&reset_list_mutex);
offset = args->args[0];
of_flags = args->args[1];
/*
* Later we map GPIO flags between OF and Linux, however not all
* constants from include/dt-bindings/gpio/gpio.h and
* include/linux/gpio/machine.h match each other.
*
* FIXME: Find a better way of translating OF flags to GPIO lookup
* flags.
*/
if (of_flags > GPIO_ACTIVE_LOW) {
pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n",
of_flags, offset);
return -EINVAL;
}
struct gpio_device *gdev __free(gpio_device_put) =
gpio_device_find_by_fwnode(of_fwnode_handle(args->np));
if (!gdev)
return -EPROBE_DEFER;
guard(mutex)(&reset_gpio_lookup_mutex); guard(mutex)(&reset_gpio_lookup_mutex);
list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) { list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) {
@ -934,6 +951,10 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
} }
} }
lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW);
parent = gpio_device_to_device(gdev);
properties[0] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
id = ida_alloc(&reset_gpio_ida, GFP_KERNEL); id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
if (id < 0) if (id < 0)
return id; return id;
@ -945,11 +966,6 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
goto err_ida_free; goto err_ida_free;
} }
ret = __reset_add_reset_gpio_lookup(id, args->np, args->args[0],
args->args[1]);
if (ret < 0)
goto err_kfree;
rgpio_dev->of_args = *args; rgpio_dev->of_args = *args;
/* /*
* We keep the device_node reference, but of_args.np is put at the end * We keep the device_node reference, but of_args.np is put at the end
@ -957,20 +973,26 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
* Hold reference as long as rgpio_dev memory is valid. * Hold reference as long as rgpio_dev memory is valid.
*/ */
of_node_get(rgpio_dev->of_args.np); of_node_get(rgpio_dev->of_args.np);
pdev = platform_device_register_data(NULL, "reset-gpio", id,
&rgpio_dev->of_args, rgpio_dev->swnode = fwnode_create_software_node(properties, NULL);
sizeof(rgpio_dev->of_args)); if (IS_ERR(rgpio_dev->swnode)) {
ret = PTR_ERR_OR_ZERO(pdev); ret = PTR_ERR(rgpio_dev->swnode);
goto err_put_of_node;
}
ret = reset_add_gpio_aux_device(parent, rgpio_dev->swnode, id,
&rgpio_dev->of_args);
if (ret) if (ret)
goto err_put; goto err_del_swnode;
list_add(&rgpio_dev->list, &reset_gpio_lookup_list); list_add(&rgpio_dev->list, &reset_gpio_lookup_list);
return 0; return 0;
err_put: err_del_swnode:
fwnode_remove_software_node(rgpio_dev->swnode);
err_put_of_node:
of_node_put(rgpio_dev->of_args.np); of_node_put(rgpio_dev->of_args.np);
err_kfree:
kfree(rgpio_dev); kfree(rgpio_dev);
err_ida_free: err_ida_free:
ida_free(&reset_gpio_ida, id); ida_free(&reset_gpio_ida, id);

View File

@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/auxiliary_bus.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
struct reset_gpio_priv { struct reset_gpio_priv {
@ -61,9 +61,10 @@ static void reset_gpio_of_node_put(void *data)
of_node_put(data); of_node_put(data);
} }
static int reset_gpio_probe(struct platform_device *pdev) static int reset_gpio_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{ {
struct device *dev = &pdev->dev; struct device *dev = &adev->dev;
struct of_phandle_args *platdata = dev_get_platdata(dev); struct of_phandle_args *platdata = dev_get_platdata(dev);
struct reset_gpio_priv *priv; struct reset_gpio_priv *priv;
int ret; int ret;
@ -75,7 +76,7 @@ static int reset_gpio_probe(struct platform_device *pdev)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, &priv->rc); auxiliary_set_drvdata(adev, &priv->rc);
priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(priv->reset)) if (IS_ERR(priv->reset))
@ -99,20 +100,20 @@ static int reset_gpio_probe(struct platform_device *pdev)
return devm_reset_controller_register(dev, &priv->rc); return devm_reset_controller_register(dev, &priv->rc);
} }
static const struct platform_device_id reset_gpio_ids[] = { static const struct auxiliary_device_id reset_gpio_ids[] = {
{ .name = "reset-gpio", }, { .name = "reset.gpio" },
{} {}
}; };
MODULE_DEVICE_TABLE(platform, reset_gpio_ids); MODULE_DEVICE_TABLE(auxiliary, reset_gpio_ids);
static struct platform_driver reset_gpio_driver = { static struct auxiliary_driver reset_gpio_driver = {
.probe = reset_gpio_probe, .probe = reset_gpio_probe,
.id_table = reset_gpio_ids, .id_table = reset_gpio_ids,
.driver = { .driver = {
.name = "reset-gpio", .name = "reset-gpio",
}, },
}; };
module_platform_driver(reset_gpio_driver); module_auxiliary_driver(reset_gpio_driver);
MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>"); MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
MODULE_DESCRIPTION("Generic GPIO reset driver"); MODULE_DESCRIPTION("Generic GPIO reset driver");

View File

@ -52,20 +52,6 @@ static struct spi_board_info amp_info_template = {
.mode = SPI_MODE_0, .mode = SPI_MODE_0,
}; };
static const struct software_node cs42l43_gpiochip_swnode = {
.name = "cs42l43-pinctrl",
};
static const struct software_node_ref_args cs42l43_cs_refs[] = {
SOFTWARE_NODE_REFERENCE(&cs42l43_gpiochip_swnode, 0, GPIO_ACTIVE_LOW),
SOFTWARE_NODE_REFERENCE(&swnode_gpio_undefined),
};
static const struct property_entry cs42l43_cs_props[] = {
PROPERTY_ENTRY_REF_ARRAY("cs-gpios", cs42l43_cs_refs),
{}
};
static int cs42l43_spi_tx(struct regmap *regmap, const u8 *buf, unsigned int len) static int cs42l43_spi_tx(struct regmap *regmap, const u8 *buf, unsigned int len)
{ {
const u8 *end = buf + len; const u8 *end = buf + len;
@ -324,11 +310,6 @@ static void cs42l43_release_of_node(void *data)
fwnode_handle_put(data); fwnode_handle_put(data);
} }
static void cs42l43_release_sw_node(void *data)
{
software_node_unregister(&cs42l43_gpiochip_swnode);
}
static int cs42l43_spi_probe(struct platform_device *pdev) static int cs42l43_spi_probe(struct platform_device *pdev)
{ {
struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent); struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
@ -391,6 +372,15 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
fwnode_property_read_u32(xu_fwnode, "01fa-sidecar-instances", &nsidecars); fwnode_property_read_u32(xu_fwnode, "01fa-sidecar-instances", &nsidecars);
if (nsidecars) { if (nsidecars) {
struct software_node_ref_args args[] = {
SOFTWARE_NODE_REFERENCE(fwnode, 0, GPIO_ACTIVE_LOW),
SOFTWARE_NODE_REFERENCE(&swnode_gpio_undefined),
};
struct property_entry props[] = {
PROPERTY_ENTRY_REF_ARRAY("cs-gpios", args),
{ }
};
ret = fwnode_property_read_u32(xu_fwnode, "01fa-spk-id-val", &spkid); ret = fwnode_property_read_u32(xu_fwnode, "01fa-spk-id-val", &spkid);
if (!ret) { if (!ret) {
dev_dbg(priv->dev, "01fa-spk-id-val = %d\n", spkid); dev_dbg(priv->dev, "01fa-spk-id-val = %d\n", spkid);
@ -403,17 +393,7 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
"Failed to get spk-id-gpios\n"); "Failed to get spk-id-gpios\n");
} }
ret = software_node_register(&cs42l43_gpiochip_swnode); ret = device_create_managed_software_node(&priv->ctlr->dev, props, NULL);
if (ret)
return dev_err_probe(priv->dev, ret,
"Failed to register gpio swnode\n");
ret = devm_add_action_or_reset(priv->dev, cs42l43_release_sw_node, NULL);
if (ret)
return ret;
ret = device_create_managed_software_node(&priv->ctlr->dev,
cs42l43_cs_props, NULL);
if (ret) if (ret)
return dev_err_probe(priv->dev, ret, "Failed to add swnode\n"); return dev_err_probe(priv->dev, ret, "Failed to add swnode\n");
} else { } else {

View File

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Intel Elkhart Lake PSE I/O Auxiliary Device
*
* Copyright (c) 2025 Intel Corporation.
*
* Author: Raag Jadav <raag.jadav@intel.com>
*/
#ifndef _EHL_PSE_IO_AUX_H_
#define _EHL_PSE_IO_AUX_H_
#include <linux/ioport.h>
#define EHL_PSE_IO_NAME "ehl_pse_io"
#define EHL_PSE_GPIO_NAME "gpio"
#define EHL_PSE_TIO_NAME "pps_tio"
struct ehl_pse_io_data {
struct resource mem;
int irq;
};
#endif /* _EHL_PSE_IO_AUX_H_ */

View File

@ -173,6 +173,8 @@ bool gpiod_is_shared(const struct gpio_desc *desc);
struct gpio_desc *gpio_to_desc(unsigned gpio); struct gpio_desc *gpio_to_desc(unsigned gpio);
int desc_to_gpio(const struct gpio_desc *desc); int desc_to_gpio(const struct gpio_desc *desc);
int gpiod_hwgpio(const struct gpio_desc *desc);
struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode,
const char *con_id, int index, const char *con_id, int index,
enum gpiod_flags flags, enum gpiod_flags flags,

View File

@ -1,36 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* OF helpers for the old of_mm_gpio_chip, used on ppc32 and nios2,
* do not use in new code.
*
* Copyright (c) 2007-2008 MontaVista Software, Inc.
*
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
*/
#ifndef __LINUX_GPIO_LEGACY_OF_MM_GPIO_CHIP_H
#define __LINUX_GPIO_LEGACY_OF_MM_GPIO_CHIP_H
#include <linux/gpio/driver.h>
#include <linux/of.h>
/*
* OF GPIO chip for memory mapped banks
*/
struct of_mm_gpio_chip {
struct gpio_chip gc;
void (*save_regs)(struct of_mm_gpio_chip *mm_gc);
void __iomem *regs;
};
static inline struct of_mm_gpio_chip *to_of_mm_gpio_chip(struct gpio_chip *gc)
{
return container_of(gc, struct of_mm_gpio_chip, gc);
}
extern int of_mm_gpiochip_add_data(struct device_node *np,
struct of_mm_gpio_chip *mm_gc,
void *data);
extern void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc);
#endif /* __LINUX_GPIO_LEGACY_OF_MM_GPIO_CHIP_H */

View File

@ -50,8 +50,8 @@ struct regmap;
* @regmap_irq_chip: (Optional) Pointer on an regmap_irq_chip structure. If * @regmap_irq_chip: (Optional) Pointer on an regmap_irq_chip structure. If
* set, a regmap-irq device will be created and the IRQ * set, a regmap-irq device will be created and the IRQ
* domain will be set accordingly. * domain will be set accordingly.
* @regmap_irq_line (Optional) The IRQ the device uses to signal interrupts. * @regmap_irq_line: (Optional) The IRQ the device uses to signal interrupts.
* @regmap_irq_flags (Optional) The IRQF_ flags to use for the interrupt. * @regmap_irq_flags: (Optional) The IRQF_ flags to use for the interrupt.
* *
* The ->reg_mask_xlate translates a given base address and GPIO offset to * The ->reg_mask_xlate translates a given base address and GPIO offset to
* register and mask pair. The base address is one of the given register * register and mask pair. The base address is one of the given register

View File

@ -355,19 +355,26 @@ struct software_node;
/** /**
* struct software_node_ref_args - Reference property with additional arguments * struct software_node_ref_args - Reference property with additional arguments
* @node: Reference to a software node * @swnode: Reference to a software node
* @fwnode: Alternative reference to a firmware node handle
* @nargs: Number of elements in @args array * @nargs: Number of elements in @args array
* @args: Integer arguments * @args: Integer arguments
*/ */
struct software_node_ref_args { struct software_node_ref_args {
const struct software_node *node; const struct software_node *swnode;
struct fwnode_handle *fwnode;
unsigned int nargs; unsigned int nargs;
u64 args[NR_FWNODE_REFERENCE_ARGS]; u64 args[NR_FWNODE_REFERENCE_ARGS];
}; };
#define SOFTWARE_NODE_REFERENCE(_ref_, ...) \ #define SOFTWARE_NODE_REFERENCE(_ref_, ...) \
(const struct software_node_ref_args) { \ (const struct software_node_ref_args) { \
.node = _ref_, \ .swnode = _Generic(_ref_, \
const struct software_node *: _ref_, \
default: NULL), \
.fwnode = _Generic(_ref_, \
struct fwnode_handle *: _ref_, \
default: NULL), \
.nargs = COUNT_ARGS(__VA_ARGS__), \ .nargs = COUNT_ARGS(__VA_ARGS__), \
.args = { __VA_ARGS__ }, \ .args = { __VA_ARGS__ }, \
} }

View File

@ -564,13 +564,14 @@ static inline bool strstarts(const char *str, const char *prefix)
/** /**
* strends - Check if a string ends with another string. * strends - Check if a string ends with another string.
* @str - NULL-terminated string to check against @suffix * @str: NULL-terminated string to check against @suffix
* @suffix - NULL-terminated string defining the suffix to look for in @str * @suffix: NULL-terminated string defining the suffix to look for in @str
* *
* Returns: * Returns:
* True if @str ends with @suffix. False in all other cases. * True if @str ends with @suffix. False in all other cases.
*/ */
static inline bool strends(const char *str, const char *suffix) static inline bool __attribute__((nonnull(1, 2)))
strends(const char *str, const char *suffix)
{ {
unsigned int str_len = strlen(str), suffix_len = strlen(suffix); unsigned int str_len = strlen(str), suffix_len = strlen(suffix);