diff options
65 files changed, 1928 insertions, 910 deletions
diff --git a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml index d78da7dd2a56..184432d24ea1 100644 --- a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml @@ -11,7 +11,10 @@ maintainers: properties: compatible: - items: + oneOf: + - items: + - const: microchip,pic64gx-gpio + - const: microchip,mpfs-gpio - enum: - microchip,mpfs-gpio - microchip,coregpio-rtl-v3 diff --git a/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml index c994177de940..3f4bbd57fc52 100644 --- a/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml @@ -22,6 +22,8 @@ properties: - cznic,moxtet-gpio - dlg,slg7xl45106 - fcs,fxl6408 + - fsl,ls1046aqds-fpga-gpio-stat-pres2 + - fsl,lx2160ardb-fpga-gpio-sfp - gateworks,pld-gpio - ibm,ppc4xx-gpio - loongson,ls1x-gpio diff --git a/Documentation/driver-api/gpio/index.rst b/Documentation/driver-api/gpio/index.rst index 87929840e85a..bee58f709b9a 100644 --- a/Documentation/driver-api/gpio/index.rst +++ b/Documentation/driver-api/gpio/index.rst @@ -15,6 +15,7 @@ Contents: legacy-boards drivers-on-gpio bt8xxgpio + pca953x Core ==== diff --git a/Documentation/driver-api/gpio/pca953x.rst b/Documentation/driver-api/gpio/pca953x.rst new file mode 100644 index 000000000000..4bd7cf1120cb --- /dev/null +++ b/Documentation/driver-api/gpio/pca953x.rst @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS index aad523a1efac..a83e86930c4c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12548,6 +12548,13 @@ F: drivers/gpu/drm/xe/ F: include/drm/intel/ 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 M: Tony Nguyen <anthony.l.nguyen@intel.com> M: Przemek Kitszel <przemyslaw.kitszel@intel.com> diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 13173795c43d..3dbff0261f0a 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -316,6 +316,7 @@ config ARCH_QCOM select GPIOLIB select PINCTRL select HAVE_PWRCTRL if PCI + select HAVE_SHARED_GPIOS help This enables support for the ARMv8 based Qualcomm chipsets. diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index be1e9e61a7bf..16a8301c25d6 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -535,14 +535,29 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, ref_array = prop->pointer; 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) return -ENOENT; if (nargs_prop) { - error = property_entry_read_int_array(ref->node->properties, - nargs_prop, sizeof(u32), - &nargs_prop_val, 1); + error = fwnode_property_read_u32(refnode, nargs_prop, &nargs_prop_val); if (error) return error; @@ -555,7 +570,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, if (!args) return 0; - args->fwnode = software_node_get(refnode); + args->fwnode = fwnode_handle_get(refnode); args->nargs = nargs; for (i = 0; i < nargs; i++) @@ -635,7 +650,10 @@ software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode) 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 * diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b8b6537290b0..c74da29253e8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -45,14 +45,6 @@ config GPIOLIB_IRQCHIP select IRQ_DOMAIN 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 def_bool y depends on HAVE_SHARED_GPIOS || COMPILE_TEST @@ -484,7 +476,6 @@ config GPIO_MENZ127 config GPIO_MM_LANTIQ bool "Lantiq Memory mapped GPIOs" depends on LANTIQ && SOC_XWAY - select OF_GPIO_MM_GPIOCHIP help 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 @@ -1421,7 +1412,7 @@ config HTC_EGPIO config GPIO_ELKHARTLAKE tristate "Intel Elkhart Lake PSE GPIO support" - depends on X86 || COMPILE_TEST + depends on INTEL_EHL_PSE_IO select GPIO_TANGIER help Select this option to enable GPIO support for Intel Elkhart Lake @@ -1573,6 +1564,15 @@ config GPIO_PMIC_EIC_SPRD help 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 bool "RICOH RC5T583 GPIO" depends on MFD_RC5T583 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d0020bc70b84..2421a8fd3733 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -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_POLARFIRE_SOC) += gpio-mpfs.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_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 8ed74e05903a..5acaeab029ec 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -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 GPIO drivers from arch/* and other places that should be placed diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 7953a9c4e36d..2e0ae953dd99 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -24,12 +24,11 @@ /* * 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 * access to some GPIOs and to arbitrate between coprocessor and ARM. */ #include <linux/gpio/consumer.h> -#include "gpiolib.h" /* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ #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 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); 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 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)) return -EOPNOTSUPP; diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index f40c9472588b..af9287ff5dc4 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -533,7 +533,6 @@ static void brcmstb_gpio_shutdown(struct platform_device *pdev) brcmstb_gpio_quiesce(&pdev->dev, false); } -#ifdef CONFIG_PM_SLEEP static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv, struct brcmstb_gpio_bank *bank) { @@ -572,14 +571,9 @@ static int brcmstb_gpio_resume(struct device *dev) 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 = { - .suspend_noirq = brcmstb_gpio_suspend, - .resume_noirq = brcmstb_gpio_resume, + .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend), + .resume_noirq = pm_sleep_ptr(brcmstb_gpio_resume), }; static int brcmstb_gpio_probe(struct platform_device *pdev) @@ -755,7 +749,7 @@ static struct platform_driver brcmstb_gpio_driver = { .driver = { .name = "brcmstb-gpio", .of_match_table = brcmstb_gpio_of_match, - .pm = &brcmstb_gpio_pm_ops, + .pm = pm_sleep_ptr(&brcmstb_gpio_pm_ops), }, .probe = brcmstb_gpio_probe, .remove = brcmstb_gpio_remove, diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 05401da03ca3..324eeb77dbd5 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -52,10 +52,8 @@ struct bt8xxgpio { struct pci_dev *pdev; struct gpio_chip gpio; -#ifdef CONFIG_PM u32 saved_outen; u32 saved_data; -#endif }; #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); } -#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); 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); } - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - 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); - 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); @@ -267,10 +256,8 @@ static int bt8xxgpio_resume(struct pci_dev *pdev) return 0; } -#else -#define bt8xxgpio_suspend NULL -#define bt8xxgpio_resume NULL -#endif /* CONFIG_PM */ + +static DEFINE_SIMPLE_DEV_PM_OPS(bt8xxgpio_pm_ops, bt8xxgpio_suspend, bt8xxgpio_resume); static const struct pci_device_id bt8xxgpio_pci_tbl[] = { { 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, .probe = bt8xxgpio_probe, .remove = bt8xxgpio_remove, - .suspend = bt8xxgpio_suspend, - .resume = bt8xxgpio_resume, + .driver.pm = &bt8xxgpio_pm_ops, }; module_pci_driver(bt8xxgpio_pci_driver); diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index b42ff46d292b..4986c465c9a8 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -79,7 +79,6 @@ struct dwapb_platform_data { unsigned int nports; }; -#ifdef CONFIG_PM_SLEEP /* Store GPIO context across system-wide suspend/resume transitions */ struct dwapb_context { u32 data; @@ -92,7 +91,6 @@ struct dwapb_context { u32 int_deb; u32 wake_en; }; -#endif struct dwapb_gpio_port_irqchip { unsigned int nr_irqs; @@ -103,9 +101,7 @@ struct dwapb_gpio_port { struct gpio_generic_chip chip; struct dwapb_gpio_port_irqchip *pirq; struct dwapb_gpio *gpio; -#ifdef CONFIG_PM_SLEEP struct dwapb_context *ctx; -#endif unsigned int idx; }; @@ -363,7 +359,6 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) return 0; } -#ifdef CONFIG_PM_SLEEP static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) { 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; } -#else -#define dwapb_irq_set_wake NULL -#endif static const struct irq_chip dwapb_irq_chip = { .name = DWAPB_DRIVER_NAME, @@ -390,7 +382,7 @@ static const struct irq_chip dwapb_irq_chip = { .irq_set_type = dwapb_irq_set_type, .irq_enable = dwapb_irq_enable, .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, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; @@ -759,7 +751,6 @@ static int dwapb_gpio_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int dwapb_gpio_suspend(struct device *dev) { struct dwapb_gpio *gpio = dev_get_drvdata(dev); @@ -844,15 +835,14 @@ static int dwapb_gpio_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend, - dwapb_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, + dwapb_gpio_suspend, dwapb_gpio_resume); static struct platform_driver dwapb_gpio_driver = { .driver = { .name = DWAPB_DRIVER_NAME, - .pm = &dwapb_gpio_pm_ops, + .pm = pm_sleep_ptr(&dwapb_gpio_pm_ops), .of_match_table = dwapb_of_match, .acpi_match_table = dwapb_acpi_match, }, diff --git a/drivers/gpio/gpio-elkhartlake.c b/drivers/gpio/gpio-elkhartlake.c index 95de52d2cc63..b96e7928b6e5 100644 --- a/drivers/gpio/gpio-elkhartlake.c +++ b/drivers/gpio/gpio-elkhartlake.c @@ -2,43 +2,46 @@ /* * Intel Elkhart Lake PSE GPIO driver * - * Copyright (c) 2023 Intel Corporation. + * Copyright (c) 2023, 2025 Intel Corporation. * * Authors: Pandith N <pandith.n@intel.com> * Raag Jadav <raag.jadav@intel.com> */ +#include <linux/auxiliary_bus.h> #include <linux/device.h> #include <linux/err.h> #include <linux/module.h> -#include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/ehl_pse_io_aux.h> + #include "gpio-tangier.h" /* Each Intel EHL PSE GPIO Controller has 30 GPIO pins */ #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; - int irq, ret; + int ret; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + data = dev_get_platdata(dev); + if (!data) + return -ENODATA; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) 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)) return PTR_ERR(priv->reg_base); priv->dev = dev; - priv->irq = irq; + priv->irq = data->irq; priv->info.base = -1; priv->info.ngpio = EHL_PSE_NGPIO; @@ -51,25 +54,24 @@ static int ehl_gpio_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "tng_gpio_probe error\n"); - platform_set_drvdata(pdev, priv); + auxiliary_set_drvdata(adev, priv); return 0; } -static const struct platform_device_id ehl_gpio_ids[] = { - { "gpio-elkhartlake" }, +static const struct auxiliary_device_id ehl_gpio_ids[] = { + { 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 = { - .name = "gpio-elkhartlake", .pm = pm_sleep_ptr(&tng_gpio_pm_ops), }, .probe = ehl_gpio_probe, .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("Raag Jadav <raag.jadav@intel.com>"); diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c index 86ebc66b1104..afc1b8461dab 100644 --- a/drivers/gpio/gpio-fxl6408.c +++ b/drivers/gpio/gpio-fxl6408.c @@ -123,6 +123,8 @@ static int fxl6408_probe(struct i2c_client *client) if (ret) return ret; + i2c_set_clientdata(client, gpio_config.regmap); + /* 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); 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)); } +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[] = { { .compatible = "fcs,fxl6408" }, { } @@ -146,6 +158,7 @@ MODULE_DEVICE_TABLE(i2c, fxl6408_id); static struct i2c_driver fxl6408_driver = { .driver = { .name = "fxl6408", + .pm = pm_sleep_ptr(&fxl6408_pm_ops), .of_match_table = fxl6408_dt_ids, }, .probe = fxl6408_probe, diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 0c0f97fa14fc..e4fa84e22726 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -46,7 +46,7 @@ /* Structure for an irq of the core - called an underlying irq */ 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 */ }; @@ -242,30 +242,22 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, irq, offset); gpio_generic_chip_lock_irqsave(&priv->chip, flags); - - /* Request underlying irq if not already requested */ lirq->irq = irq; uirq = &priv->uirqs[lirq->index]; - if (uirq->refcnt == 0) { - /* - * FIXME: This is not how locking works at all, you can't just - * release the lock for a moment to do something that can't - * sleep... - */ - gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); + gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); + + /* Request underlying irq if not already requested */ + if (atomic_fetch_add(1, &uirq->refcnt) == 0) { ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, dev_name(priv->dev), priv); if (ret) { dev_err(priv->dev, "Could not request underlying irq %d\n", uirq->uirq); + atomic_dec(&uirq->refcnt); /* rollback */ return ret; } - gpio_generic_chip_lock_irqsave(&priv->chip, flags); } - uirq->refcnt++; - - gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); /* Setup irq */ 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) { uirq = &priv->uirqs[lirq->index]; - uirq->refcnt--; - if (uirq->refcnt == 0) { + if (atomic_dec_and_test(&uirq->refcnt)) { gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); free_irq(uirq->uirq, priv); return; @@ -434,6 +425,7 @@ static int grgpio_probe(struct platform_device *ofdev) continue; } priv->uirqs[lirq->index].uirq = ret; + atomic_set(&priv->uirqs[lirq->index].refcnt, 0); } } diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index 2eaed83214d8..72935d6dbebf 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -364,21 +364,20 @@ static int __init egpio_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int egpio_suspend(struct platform_device *pdev, pm_message_t state) +static int egpio_suspend(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)) enable_irq_wake(ei->chained_irq); 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); /* Update registers from the cache, in case @@ -386,19 +385,15 @@ static int egpio_resume(struct platform_device *pdev) egpio_write_cache(ei); 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 = { .driver = { .name = "htc-egpio", .suppress_bind_attrs = true, + .pm = pm_sleep_ptr(&egpio_pm_ops), }, - .suspend = egpio_suspend, - .resume = egpio_resume, }; static int __init egpio_init(void) diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index c64aaa896766..452a9ce61488 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -48,8 +48,6 @@ #include <linux/property.h> #include <linux/delay.h> -#include "gpiolib.h" - struct gpio_latch_priv { struct gpio_chip gc; struct gpio_descs *clk_gpios; diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index 02f181cb219e..77d07e31366f 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -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.set = loongson_gpio_set; lgpio->chip.gc.parent = &pdev->dev; + lgpio->chip.gc.base = -1; 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 = { .label = "ls2k2000_gpio", - .mode = BIT_CTRL_MODE, - .conf_offset = 0x0, - .in_offset = 0x20, - .out_offset = 0x10, - .inten_offset = 0x30, + .mode = BYTE_CTRL_MODE, + .conf_offset = 0x800, + .in_offset = 0xa00, + .out_offset = 0x900, + .inten_offset = 0xb00, }; static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = { diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c index da2bf9381cc4..52b13c6ae496 100644 --- a/drivers/gpio/gpio-menz127.c +++ b/drivers/gpio/gpio-menz127.c @@ -24,6 +24,11 @@ #define MEN_Z127_ODER 0x1C #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 /* 16 bit compare register. Each bit represents 50us */ #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 device *dev = &mdev->dev; int ret; + unsigned long sz; men_z127_gpio = devm_kzalloc(dev, sizeof(struct men_z127_gpio), GFP_KERNEL); @@ -163,9 +169,21 @@ static int men_z127_probe(struct mcb_device *mdev, 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) { .dev = &mdev->dev, - .sz = 4, + .sz = sz, .dat = men_z127_gpio->reg_base + MEN_Z127_PSR, .set = men_z127_gpio->reg_base + MEN_Z127_CTRL, .dirout = men_z127_gpio->reg_base + MEN_Z127_GPIODR, @@ -186,7 +204,9 @@ static int men_z127_probe(struct mcb_device *mdev, } 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); @@ -201,7 +221,7 @@ static struct mcb_driver men_z127_driver = { module_mcb_driver(men_z127_driver); MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); -MODULE_DESCRIPTION("MEN 16z127 GPIO Controller"); +MODULE_DESCRIPTION("MEN GPIO Controller"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("mcb:16z127"); MODULE_IMPORT_NS("MCB"); diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index f6af81bf2b13..6576e5dcb0ee 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -160,7 +160,7 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) /* * 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; @@ -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. */ -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; @@ -479,7 +479,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, 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); unsigned long flags; @@ -491,7 +491,7 @@ static int __maybe_unused ioh_gpio_suspend(struct device *dev) 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); unsigned long flags; @@ -505,7 +505,7 @@ static int __maybe_unused ioh_gpio_resume(struct device *dev) 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[] = { { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) }, @@ -518,7 +518,7 @@ static struct pci_driver ioh_gpio_driver = { .id_table = ioh_gpio_pcidev_id, .probe = ioh_gpio_probe, .driver = { - .pm = &ioh_gpio_pm_ops, + .pm = pm_sleep_ptr(&ioh_gpio_pm_ops), }, }; diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index abffce3894fc..6668686a28ff 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -424,7 +424,7 @@ mlxbf2_gpio_probe(struct platform_device *pdev) 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); @@ -436,7 +436,7 @@ static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev) 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); @@ -447,7 +447,7 @@ static int __maybe_unused mlxbf2_gpio_resume(struct device *dev) 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[] = { { "MLNXBF22", 0 }, @@ -459,7 +459,7 @@ static struct platform_driver mlxbf2_gpio_driver = { .driver = { .name = "mlxbf2_gpio", .acpi_match_table = mlxbf2_gpio_acpi_match, - .pm = &mlxbf2_pm_ops, + .pm = pm_sleep_ptr(&mlxbf2_pm_ops), }, .probe = mlxbf2_gpio_probe, }; diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c index 8f1405733d98..1bd98c50a459 100644 --- a/drivers/gpio/gpio-mm-lantiq.c +++ b/drivers/gpio/gpio-mm-lantiq.c @@ -10,7 +10,6 @@ #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/gpio/driver.h> -#include <linux/gpio/legacy-of-mm-gpiochip.h> #include <linux/of.h> #include <linux/io.h> #include <linux/slab.h> @@ -27,7 +26,8 @@ #define LTQ_EBU_WP 0x80000000 /* write protect bit */ struct ltq_mm { - struct of_mm_gpio_chip mmchip; + struct gpio_chip gc; + void __iomem *regs; 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); 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); 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. * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * @val: Value to be written to specified signal. + * @offset: GPIO signal number. + * @value: Value to be written to specified signal. * * 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. * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * @val: Value to be written to specified signal. + * @offset: GPIO signal number. + * @value: Value to be written to specified signal. * * 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 - * @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 */ - 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); } 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; u32 shadow; @@ -107,25 +107,29 @@ static int ltq_mm_probe(struct platform_device *pdev) if (!chip) return -ENOMEM; - platform_set_drvdata(pdev, chip); + gc = &chip->gc; + + gc->base = -1; + gc->ngpio = 16; + gc->direction_output = ltq_mm_dir_out; + 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); - chip->mmchip.gc.ngpio = 16; - chip->mmchip.gc.direction_output = ltq_mm_dir_out; - chip->mmchip.gc.set = ltq_mm_set; - chip->mmchip.save_regs = ltq_mm_save_regs; + ltq_mm_save_regs(chip); /* store the shadow value if one was passed by the devicetree */ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow)) chip->shadow = shadow; - return of_mm_gpiochip_add_data(pdev->dev.of_node, &chip->mmchip, chip); -} - -static void ltq_mm_remove(struct platform_device *pdev) -{ - struct ltq_mm *chip = platform_get_drvdata(pdev); - - of_mm_gpiochip_remove(&chip->mmchip); + return devm_gpiochip_add_data(dev, gc, chip); } 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 = { .probe = ltq_mm_probe, - .remove = ltq_mm_remove, .driver = { .name = "gpio-mm-ltq", .of_match_table = ltq_mm_match, diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 7d6dd36cf1ae..b3a26a06260b 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -41,6 +41,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/compiler.h> #include <linux/err.h> #include <linux/init.h> @@ -61,69 +62,69 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` #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); } -static unsigned long bgpio_read8(void __iomem *reg) +static unsigned long gpio_mmio_read8(void __iomem *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); } -static unsigned long bgpio_read16(void __iomem *reg) +static unsigned long gpio_mmio_read16(void __iomem *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); } -static unsigned long bgpio_read32(void __iomem *reg) +static unsigned long gpio_mmio_read32(void __iomem *reg) { return readl(reg); } #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); } -static unsigned long bgpio_read64(void __iomem *reg) +static unsigned long gpio_mmio_read64(void __iomem *reg) { return readq(reg); } #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); } -static unsigned long bgpio_read16be(void __iomem *reg) +static unsigned long gpio_mmio_read16be(void __iomem *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); } -static unsigned long bgpio_read32be(void __iomem *reg) +static unsigned long gpio_mmio_read32be(void __iomem *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); @@ -132,10 +133,10 @@ static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int 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); - unsigned long pinmask = bgpio_line2mask(gc, gpio); + unsigned long pinmask = gpio_mmio_line2mask(gc, gpio); bool dir = !!(chip->sdir & pinmask); 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. * We only assign the function pointer if we have that. */ -static int bgpio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); 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; } -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); - 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. */ -static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_get_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { 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. */ -static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); unsigned long readmask = 0; @@ -205,7 +206,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, /* Create a mirrored mask */ for_each_set_bit(bit, mask, gc->ngpio) - readmask |= bgpio_line2mask(gc, bit); + readmask |= gpio_mmio_line2mask(gc, bit); /* Read the register */ 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. */ for_each_set_bit(bit, &val, gc->ngpio) - *bits |= bgpio_line2mask(gc, bit); + *bits |= gpio_mmio_line2mask(gc, bit); 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; } -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); - unsigned long mask = bgpio_line2mask(gc, gpio); - unsigned long flags; + unsigned long mask = gpio_mmio_line2mask(gc, gpio); - raw_spin_lock_irqsave(&chip->lock, flags); + guard(raw_spinlock)(&chip->lock); if (val) 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); - raw_spin_unlock_irqrestore(&chip->lock, flags); - return 0; } -static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, - int val) +static int gpio_mmio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, + int val) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - unsigned long mask = bgpio_line2mask(gc, gpio); + unsigned long mask = gpio_mmio_line2mask(gc, gpio); if (val) 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; } -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); - 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) 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); - raw_spin_unlock_irqrestore(&chip->lock, flags); - return 0; } -static void bgpio_multiple_get_masks(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits, - unsigned long *set_mask, - unsigned long *clear_mask) +static void gpio_mmio_multiple_get_masks(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits, + unsigned long *set_mask, + unsigned long *clear_mask) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); int i; @@ -291,60 +288,58 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc, for_each_set_bit(i, mask, chip->bits) { if (test_bit(i, bits)) - *set_mask |= bgpio_line2mask(gc, i); + *set_mask |= gpio_mmio_line2mask(gc, i); 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, - unsigned long *mask, - unsigned long *bits, - void __iomem *reg) +static void gpio_mmio_set_multiple_single_reg(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits, + void __iomem *reg) { 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 &= ~clear_mask; 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, - unsigned long *bits) +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); + gpio_mmio_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) +static int gpio_mmio_set_multiple_set(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_set); + gpio_mmio_set_multiple_single_reg(gc, mask, bits, chip->reg_set); return 0; } -static int bgpio_set_multiple_with_clear(struct gpio_chip *gc, - unsigned long *mask, - unsigned long *bits) +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); 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) 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; } -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); @@ -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); } -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; } -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, - int val) +static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, + int val) { return -EINVAL; } -static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, - int val) +static int gpio_mmio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, + int 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); - 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); - - raw_spin_unlock_irqrestore(&chip->lock, flags); + 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); + } - return bgpio_dir_return(gc, gpio, false); + return gpio_mmio_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); /* Return 0 if output, 1 if input */ 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_IN; } 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_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_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); - 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) 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); } -static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, - int val) +static int gpio_mmio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, + int val) { - bgpio_dir_out(gc, gpio, val); + gpio_mmio_dir_out(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, - int val) +static int gpio_mmio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, + int val) { gc->set(gc, gpio, val); - bgpio_dir_out(gc, gpio, val); - return bgpio_dir_return(gc, gpio, true); + gpio_mmio_dir_out(gc, gpio, val); + return gpio_mmio_dir_return(gc, gpio, true); } -static int bgpio_setup_accessors(struct device *dev, - struct gpio_generic_chip *chip, - bool byte_be) +static int gpio_mmio_setup_accessors(struct device *dev, + struct gpio_generic_chip *chip, + bool byte_be) { switch (chip->bits) { case 8: - chip->read_reg = bgpio_read8; - chip->write_reg = bgpio_write8; + chip->read_reg = gpio_mmio_read8; + chip->write_reg = gpio_mmio_write8; break; case 16: if (byte_be) { - chip->read_reg = bgpio_read16be; - chip->write_reg = bgpio_write16be; + chip->read_reg = gpio_mmio_read16be; + chip->write_reg = gpio_mmio_write16be; } else { - chip->read_reg = bgpio_read16; - chip->write_reg = bgpio_write16; + chip->read_reg = gpio_mmio_read16; + chip->write_reg = gpio_mmio_write16; } break; case 32: if (byte_be) { - chip->read_reg = bgpio_read32be; - chip->write_reg = bgpio_write32be; + chip->read_reg = gpio_mmio_read32be; + chip->write_reg = gpio_mmio_write32be; } else { - chip->read_reg = bgpio_read32; - chip->write_reg = bgpio_write32; + chip->read_reg = gpio_mmio_read32; + chip->write_reg = gpio_mmio_write32; } break; #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"); return -EINVAL; } else { - chip->read_reg = bgpio_read64; - chip->write_reg = bgpio_write64; + chip->read_reg = gpio_mmio_read64; + chip->write_reg = gpio_mmio_write64; } break; #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 * the GPIO is an input. */ -static int bgpio_setup_io(struct gpio_generic_chip *chip, - const struct gpio_generic_chip_config *cfg) +static int gpio_mmio_setup_io(struct gpio_generic_chip *chip, + const struct gpio_generic_chip_config *cfg) { 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) { chip->reg_set = cfg->set; chip->reg_clr = cfg->clr; - gc->set = bgpio_set_with_clear; - gc->set_multiple = bgpio_set_multiple_with_clear; + gc->set = gpio_mmio_set_with_clear; + gc->set_multiple = gpio_mmio_set_multiple_with_clear; } else if (cfg->set && !cfg->clr) { chip->reg_set = cfg->set; - gc->set = bgpio_set_set; - gc->set_multiple = bgpio_set_multiple_set; + gc->set = gpio_mmio_set_set; + gc->set_multiple = gpio_mmio_set_multiple_set; } else if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) { - gc->set = bgpio_set_none; + gc->set = gpio_mmio_set_none; gc->set_multiple = NULL; } else { - gc->set = bgpio_set; - gc->set_multiple = bgpio_set_multiple; + gc->set = gpio_mmio_set; + gc->set_multiple = gpio_mmio_set_multiple; } if (!(cfg->flags & GPIO_GENERIC_UNREADABLE_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) - gc->get_multiple = bgpio_get_set_multiple; + gc->get_multiple = gpio_mmio_get_set_multiple; /* * We deliberately avoid assigning the ->get_multiple() call * 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. */ } else { - gc->get = bgpio_get; + gc->get = gpio_mmio_get; if (chip->be_bits) - gc->get_multiple = bgpio_get_multiple_be; + gc->get_multiple = gpio_mmio_get_multiple_be; else - gc->get_multiple = bgpio_get_multiple; + gc->get_multiple = gpio_mmio_get_multiple; } return 0; } -static int bgpio_setup_direction(struct gpio_generic_chip *chip, - const struct gpio_generic_chip_config *cfg) +static int gpio_mmio_setup_direction(struct gpio_generic_chip *chip, + const struct gpio_generic_chip_config *cfg) { 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_in = cfg->dirin; 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 - gc->direction_output = bgpio_dir_out_val_first; - gc->direction_input = bgpio_dir_in; - gc->get_direction = bgpio_get_dir; + gc->direction_output = gpio_mmio_dir_out_val_first; + gc->direction_input = gpio_mmio_dir_in; + gc->get_direction = gpio_mmio_get_dir; } else { if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) - gc->direction_output = bgpio_dir_out_err; + gc->direction_output = gpio_mmio_dir_out_err; else - gc->direction_output = bgpio_simple_dir_out; + gc->direction_output = gpio_mmio_simple_dir_out; if (cfg->flags & GPIO_GENERIC_NO_INPUT) - gc->direction_input = bgpio_dir_in_err; + gc->direction_input = gpio_mmio_dir_in_err; else - gc->direction_input = bgpio_simple_dir_in; + gc->direction_input = gpio_mmio_simple_dir_in; } 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); @@ -653,23 +643,23 @@ int gpio_generic_chip_init(struct gpio_generic_chip *chip, gc->parent = dev; gc->label = dev_name(dev); gc->base = -1; - gc->request = bgpio_request; + gc->request = gpio_mmio_request; chip->be_bits = !!(flags & GPIO_GENERIC_BIG_ENDIAN); ret = gpiochip_get_ngpios(gc, dev); if (ret) gc->ngpio = chip->bits; - ret = bgpio_setup_io(chip, cfg); + ret = gpio_mmio_setup_io(chip, cfg); if (ret) return ret; - ret = bgpio_setup_accessors(dev, chip, + ret = gpio_mmio_setup_accessors(dev, chip, flags & GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER); if (ret) return ret; - ret = bgpio_setup_direction(chip, cfg); + ret = gpio_mmio_setup_direction(chip, cfg); if (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); - if (gc->set == bgpio_set_set && + if (gc->set == gpio_mmio_set_set && !(flags & GPIO_GENERIC_UNREADABLE_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) -static void __iomem *bgpio_map(struct platform_device *pdev, - const char *name, - resource_size_t sane_sz) +static void __iomem *gpio_mmio_map(struct platform_device *pdev, + const char *name, resource_size_t sane_sz) { struct resource *r; resource_size_t sz; @@ -730,16 +719,16 @@ static void __iomem *bgpio_map(struct platform_device *pdev, 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 = "wd,mbl-gpio" }, { .compatible = "ni,169445-nand-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 *gen_gc; @@ -762,23 +751,23 @@ static int bgpio_pdev_probe(struct platform_device *pdev) sz = resource_size(r); - dat = bgpio_map(pdev, "dat", sz); + dat = gpio_mmio_map(pdev, "dat", sz); if (IS_ERR(dat)) return PTR_ERR(dat); - set = bgpio_map(pdev, "set", sz); + set = gpio_mmio_map(pdev, "set", sz); if (IS_ERR(set)) return PTR_ERR(set); - clr = bgpio_map(pdev, "clr", sz); + clr = gpio_mmio_map(pdev, "clr", sz); if (IS_ERR(clr)) return PTR_ERR(clr); - dirout = bgpio_map(pdev, "dirout", sz); + dirout = gpio_mmio_map(pdev, "dirout", sz); if (IS_ERR(dirout)) return PTR_ERR(dirout); - dirin = bgpio_map(pdev, "dirin", sz); + dirin = gpio_mmio_map(pdev, "dirin", sz); if (IS_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); } -static const struct platform_device_id bgpio_id_table[] = { +static const struct platform_device_id gpio_mmio_id_table[] = { { .name = "basic-mmio-gpio", .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 = { .name = "basic-mmio-gpio", - .of_match_table = bgpio_of_match, + .of_match_table = gpio_mmio_of_match, }, - .id_table = bgpio_id_table, - .probe = bgpio_pdev_probe, + .id_table = gpio_mmio_id_table, + .probe = gpio_mmio_pdev_probe, }; -module_platform_driver(bgpio_driver); +module_platform_driver(gpio_mmio_driver); #endif /* CONFIG_GPIO_GENERIC_PLATFORM */ diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 9f42bb30b4ec..ace652ba4df1 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -10,6 +10,7 @@ #include <linux/cleanup.h> #include <linux/gpio/driver.h> #include <linux/mutex.h> +#include <linux/spinlock.h> #include <linux/usb.h> struct mpsse_priv { @@ -17,8 +18,10 @@ struct mpsse_priv { struct usb_device *udev; /* USB device encompassing all MPSSEs */ struct usb_interface *intf; /* USB interface 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_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_enabled; int id; @@ -26,6 +29,9 @@ struct mpsse_priv { u8 gpio_outputs[2]; /* Output states 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 */ struct usb_endpoint_descriptor *bulk_in; @@ -34,6 +40,14 @@ struct mpsse_priv { 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 { bool tx; /* direction of bulk transfer */ u8 *data; /* input (tx) or output (rx) */ @@ -43,8 +57,27 @@ struct bulk_desc { 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[] = { { 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 */ }; @@ -160,6 +193,32 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank) 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, unsigned long *bits) { @@ -167,6 +226,10 @@ static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, int ret; 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); for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { bank = i / 8; @@ -194,6 +257,10 @@ static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask, int ret; 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); for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { 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, unsigned int offset, int value) { + int ret; struct mpsse_priv *priv = gpiochip_get_data(chip); int bank = (offset & 8) >> 3; 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) 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, unsigned int offset) { + int ret; struct mpsse_priv *priv = gpiochip_get_data(chip); int bank = (offset & 8) >> 3; 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); 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, @@ -284,18 +360,62 @@ static int gpio_mpsse_get_direction(struct gpio_chip *chip, 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; int irq_enabled, offset, err, value, fire_irq, irq, old_value[16], irq_type[16]; - struct mpsse_priv *priv = container_of(work, struct mpsse_priv, - irq_work); + struct mpsse_worker *my_worker = container_of(my_work, struct mpsse_worker, work); + struct mpsse_priv *priv = my_worker->priv; for (offset = 0; offset < priv->gpio.ngpio; ++offset) 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); /* Cleanup will trigger at the end of the loop */ 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) { + struct mpsse_worker *worker; struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); atomic_and(~BIT(irqd->hwirq), &priv->irq_enabled); 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) { + struct mpsse_worker *worker; struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); gpiochip_enable_irq(&priv->gpio, irqd->hwirq); /* If no-one else was using the IRQ, enable it */ 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); } +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, const struct usb_device_id *id) { struct mpsse_priv *priv; struct device *dev; + char *serial; int err; + struct mpsse_quirk *quirk = (void *)id->driver_info; dev = &interface->dev; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + INIT_LIST_HEAD(&priv->workers); + priv->udev = usb_get_dev(interface_to_usbdev(interface)); priv->intf = interface; priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber; @@ -436,9 +611,21 @@ static int gpio_mpsse_probe(struct usb_interface *interface, if (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, - "gpio-mpsse.%d.%d", - priv->id, priv->intf_id); + "MPSSE%04x:%04x.%d.%d.%s", + id->idVendor, id->idProduct, + priv->intf_id, priv->id, + serial); if (!priv->gpio.label) 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.set_multiple = gpio_mpsse_set_multiple; priv->gpio.base = -1; - priv->gpio.ngpio = 16; + priv->gpio.ngpio = MPSSE_NGPIO; priv->gpio.offset = priv->intf_id * priv->gpio.ngpio; 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, &priv->bulk_in, &priv->bulk_out, NULL, NULL); @@ -494,6 +691,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface, priv->gpio.irq.parents = NULL; priv->gpio.irq.default_type = IRQ_TYPE_NONE; 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); if (err) @@ -506,6 +704,13 @@ static void gpio_mpsse_disconnect(struct usb_interface *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; usb_set_intfdata(intf, NULL); usb_put_dev(priv->udev); diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c index b0cccd856840..7345afdc78de 100644 --- a/drivers/gpio/gpio-msc313.c +++ b/drivers/gpio/gpio-msc313.c @@ -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 * 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); int i; @@ -705,7 +705,7 @@ static int __maybe_unused msc313_gpio_suspend(struct device *dev) 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); int i; @@ -716,13 +716,13 @@ static int __maybe_unused msc313_gpio_resume(struct device *dev) 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 = { .driver = { .name = DRIVER_NAME, .of_match_table = msc313_gpio_of_match, - .pm = &msc313_gpio_ops, + .pm = pm_sleep_ptr(&msc313_gpio_ops), }, .probe = msc313_gpio_probe, }; diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index ac799fced950..22c36b79e249 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -573,11 +573,10 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) for (i = 0; i < mvchip->chip.ngpio; i++) { int irq; - irq = irq_find_mapping(mvchip->domain, i); - if (!(cause & BIT(i))) continue; + irq = irq_find_mapping(mvchip->domain, i); type = irq_get_trigger_type(irq); if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { /* Swap polarity (race with GPIO line) */ diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index a268c76bdca6..e136e81794df 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1503,7 +1503,7 @@ static void omap_gpio_remove(struct platform_device *pdev) 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); unsigned long flags; @@ -1516,7 +1516,7 @@ static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev) 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); unsigned long flags; @@ -1529,7 +1529,7 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) 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); @@ -1541,7 +1541,7 @@ static int __maybe_unused omap_gpio_suspend(struct device *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); @@ -1554,9 +1554,8 @@ static int __maybe_unused omap_gpio_resume(struct device *dev) } static const struct dev_pm_ops gpio_pm_ops = { - SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, - NULL) - SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) + RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL) + LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) }; static struct platform_driver omap_gpio_driver = { @@ -1564,7 +1563,7 @@ static struct platform_driver omap_gpio_driver = { .remove = omap_gpio_remove, .driver = { .name = "omap_gpio", - .pm = &gpio_pm_ops, + .pm = pm_ptr(&gpio_pm_ops), .of_match_table = omap_gpio_match, }, }; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b46927f55038..0a3916cc2772 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -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 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 * categories above. */ @@ -854,10 +854,13 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) int level; if (chip->driver_data & PCA_PCAL) { + DECLARE_BITMAP(latched_inputs, MAX_LINE); guard(mutex)(&chip->i2c_lock); - /* Enable latch on interrupt-enabled inputs */ - pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask); + /* Enable latch on edge-triggered interrupt-enabled inputs */ + 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); @@ -1203,10 +1206,10 @@ static int pca953x_probe(struct i2c_client *client) pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK); 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; } else { - dev_info(dev, "using no AI\n"); + dev_info(dev, "using no auto increment\n"); regmap_config = &pca953x_i2c_regmap; } diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 9925687e05fb..4ffa0955a9e3 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -171,7 +171,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) /* * 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.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. */ -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.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]); } -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); unsigned long flags; @@ -414,7 +414,7 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) 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); unsigned long flags; @@ -428,7 +428,7 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) 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[] = { { 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, .probe = pch_gpio_probe, .driver = { - .pm = &pch_gpio_pm_ops, + .pm = pm_sleep_ptr(&pch_gpio_pm_ops), }, }; diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 02e4ffcf5a6f..919cf86fd590 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -37,7 +37,6 @@ #define PL061_GPIO_NR 8 -#ifdef CONFIG_PM struct pl061_context_save_regs { u8 gpio_data; u8 gpio_dir; @@ -46,7 +45,6 @@ struct pl061_context_save_regs { u8 gpio_iev; u8 gpio_ie; }; -#endif struct pl061 { raw_spinlock_t lock; @@ -55,9 +53,7 @@ struct pl061 { struct gpio_chip gc; int parent_irq; -#ifdef CONFIG_PM struct pl061_context_save_regs csave_regs; -#endif }; 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; } -#ifdef CONFIG_PM static int pl061_suspend(struct device *dev) { struct pl061 *pl061 = dev_get_drvdata(dev); @@ -411,13 +406,7 @@ static int pl061_resume(struct device *dev) return 0; } -static const struct dev_pm_ops pl061_dev_pm_ops = { - .suspend = pl061_suspend, - .resume = pl061_resume, - .freeze = pl061_suspend, - .restore = pl061_resume, -}; -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(pl061_dev_pm_ops, pl061_suspend, pl061_resume); static const struct amba_id pl061_ids[] = { { @@ -431,9 +420,7 @@ MODULE_DEVICE_TABLE(amba, pl061_ids); static struct amba_driver pl061_gpio_driver = { .drv = { .name = "pl061_gpio", -#ifdef CONFIG_PM - .pm = &pl061_dev_pm_ops, -#endif + .pm = pm_sleep_ptr(&pl061_dev_pm_ops), }, .id_table = pl061_ids, .probe = pl061_probe, diff --git a/drivers/gpio/gpio-qixis-fpga.c b/drivers/gpio/gpio-qixis-fpga.c new file mode 100644 index 000000000000..6e67f43ac0bd --- /dev/null +++ b/drivers/gpio/gpio-qixis-fpga.c @@ -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, ®map_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"); diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index f4267af00027..e5ba38e65c10 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -82,7 +82,11 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset) if (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) 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); unsigned int base = gpio_regmap_addr(gpio->reg_set_base); - unsigned int reg, mask; + unsigned int reg, mask, mask_val; int ret; ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); @@ -102,9 +106,15 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset, return ret; if (val) - ret = regmap_update_bits(gpio->regmap, reg, mask, mask); + mask_val = mask; + else + 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, 0); + ret = regmap_update_bits(gpio->regmap, reg, mask, mask_val); return ret; } diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c index 3ef2c40ed152..29d7d2e4dfc0 100644 --- a/drivers/gpio/gpio-shared-proxy.c +++ b/drivers/gpio/gpio-shared-proxy.c @@ -322,6 +322,7 @@ MODULE_DEVICE_TABLE(auxiliary, gpio_shared_proxy_id_table); static struct auxiliary_driver gpio_shared_proxy_driver = { .driver = { .name = "gpio-shared-proxy", + .suppress_bind_attrs = true, }, .probe = gpio_shared_proxy_probe, .id_table = gpio_shared_proxy_id_table, diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 4d3db6e06eeb..b1498b59a921 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -1,6 +1,6 @@ // 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> * Dipen Patel <dpatel@nvidia.com> @@ -69,6 +69,30 @@ #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 { const char *name; unsigned int bank; @@ -85,6 +109,7 @@ struct tegra_gpio_soc { const struct tegra_gpio_port *ports; unsigned int num_ports; const char *name; + const char *prefix; unsigned int instance; unsigned int num_irqs_per_bank; @@ -916,8 +941,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) char *name; for (j = 0; j < port->pins; j++) { - name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, - "P%s.%02x", port->name, j); + if (gpio->soc->prefix) + 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) 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); } -#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA186_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ +#define TEGRA_GPIO_PORT(_prefix, _name, _bank, _port, _pins) \ + [_prefix##_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .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[] = { TEGRA186_MAIN_GPIO_PORT( A, 2, 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, }; -#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA186_AON_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA186_AON, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra186_aon_ports[] = { 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, }; -#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA194_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA194_MAIN, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra194_main_ports[] = { 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, }; -#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA194_AON_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA194_AON, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra194_aon_ports[] = { 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, }; -#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA234_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA234_MAIN, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra234_main_ports[] = { 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, }; -#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA234_AON_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA234_AON, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra234_aon_ports[] = { 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, }; -#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA241_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra241_main_ports[] = { 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, }; -#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA241_AON_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA241_AON, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra241_aon_ports[] = { 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, }; -#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA256_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA256_MAIN, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra256_main_ports[] = { 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, }; +#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[] = { { .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 = "NVDA0508", .driver_data = (kernel_ulong_t)&tegra241_main_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); diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 27dd09273292..eedfc0e371e3 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -279,19 +279,18 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) } /* 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; } -static int __maybe_unused tqmx86_gpio_runtime_resume(struct device *dev) +static int tqmx86_gpio_runtime_resume(struct device *dev) { return 0; } static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = { - SET_RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, - tqmx86_gpio_runtime_resume, NULL) + RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, tqmx86_gpio_runtime_resume, NULL) }; static void tqmx86_init_irq_valid_mask(struct gpio_chip *chip, @@ -425,7 +424,7 @@ out_pm_dis: static struct platform_driver tqmx86_gpio_driver = { .driver = { .name = "tqmx86-gpio", - .pm = &tqmx86_gpio_dev_pm_ops, + .pm = pm_ptr(&tqmx86_gpio_dev_pm_ops), }, .probe = tqmx86_gpio_probe, }; diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 197bb1d22b3c..0574dde5b5bb 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -426,7 +426,7 @@ static void uniphier_gpio_remove(struct platform_device *pdev) 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); 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; } -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); 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 = { - SET_LATE_SYSTEM_SLEEP_PM_OPS(uniphier_gpio_suspend, - uniphier_gpio_resume) + LATE_SYSTEM_SLEEP_PM_OPS(uniphier_gpio_suspend, uniphier_gpio_resume) }; static const struct of_device_id uniphier_gpio_match[] = { @@ -489,7 +488,7 @@ static struct platform_driver uniphier_gpio_driver = { .driver = { .name = "uniphier-gpio", .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); diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index a10eab7d2617..37f2ce20f1ae 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -500,9 +500,7 @@ static int gpio_virtuser_value_set(void *data, u64 val) if (val > 1) return -EINVAL; - gpiod_set_value_cansleep(ld->ad.desc, (int)val); - - return 0; + return gpiod_set_value_cansleep(ld->ad.desc, (int)val); } 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 = 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); } @@ -562,7 +560,7 @@ static int gpio_virtuser_value_atomic_set(void *data, u64 val) gpio_virtuser_irq_work_queue_sync(&ctx); - return 0; + return ctx.ret; } DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_atomic_fops, diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c index 4f627de3f56c..809668449dbe 100644 --- a/drivers/gpio/gpio-xgene.c +++ b/drivers/gpio/gpio-xgene.c @@ -130,7 +130,7 @@ static int xgene_gpio_dir_out(struct gpio_chip *gc, 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); unsigned long bank_offset; @@ -143,7 +143,7 @@ static __maybe_unused int xgene_gpio_suspend(struct device *dev) 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); unsigned long bank_offset; @@ -156,7 +156,7 @@ static __maybe_unused int xgene_gpio_resume(struct device *dev) 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) { @@ -204,7 +204,7 @@ static struct platform_driver xgene_gpio_driver = { .name = "xgene-gpio", .of_match_table = xgene_gpio_of_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, }; diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 83675ac81077..be4b4d730547 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -286,7 +286,7 @@ static void xgpio_free(struct gpio_chip *chip, unsigned int offset) 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 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 irq_data *data = irq_get_irq_data(gpio->irq); @@ -343,7 +343,7 @@ static int __maybe_unused xgpio_resume(struct device *dev) 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); @@ -352,7 +352,7 @@ static int __maybe_unused xgpio_runtime_suspend(struct device *dev) 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); @@ -360,9 +360,8 @@ static int __maybe_unused xgpio_runtime_resume(struct device *dev) } static const struct dev_pm_ops xgpio_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume) - SET_RUNTIME_PM_OPS(xgpio_runtime_suspend, - xgpio_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume) + RUNTIME_PM_OPS(xgpio_runtime_suspend, xgpio_runtime_resume, NULL) }; /** @@ -682,7 +681,7 @@ static struct platform_driver xgpio_plat_driver = { .driver = { .name = "gpio-xilinx", .of_match_table = xgpio_of_match, - .pm = &xgpio_dev_pm_ops, + .pm = pm_ptr(&xgpio_dev_pm_ops), }, }; diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 0ffd76e8951f..97780c57ab56 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -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 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; } -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 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; } -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); @@ -788,7 +788,7 @@ static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev) 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); @@ -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 = { - SET_SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume) - SET_RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, - zynq_gpio_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume) + RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL) }; 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 = { .driver = { .name = DRIVER_NAME, - .pm = &zynq_gpio_dev_pm_ops, + .pm = pm_ptr(&zynq_gpio_dev_pm_ops), .of_match_table = zynq_gpio_of_match, }, .probe = zynq_gpio_probe, diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index d441c1236d8c..83dd227dbbec 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -1099,7 +1099,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, 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) { unsigned int pin = agpio->pin_table[i]; struct acpi_gpio_connection *conn; diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 084656564176..3735c9fe1502 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -652,7 +652,7 @@ static enum hte_return process_hw_ts_thread(void *p) } le.line_seqno = line->line_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); @@ -676,7 +676,7 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) if (READ_ONCE(line->sw_debounced)) { line->total_discard_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))); } else { if (unlikely(ts->seq < line->line_seqno)) @@ -769,7 +769,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p) line->line_seqno++; le.line_seqno = line->line_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); @@ -817,7 +817,7 @@ static irqreturn_t debounce_irq_handler(int irq, void *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))); return IRQ_HANDLED; @@ -867,7 +867,7 @@ static void debounce_work_func(struct work_struct *work) lr = line->req; le.timestamp_ns = line_event_timestamp(line); - le.offset = gpio_chip_hwgpio(line->desc); + le.offset = gpiod_hwgpio(line->desc); #ifdef CONFIG_HTE if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) { /* 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++) seq_printf(out, "gpio-line:\t%d\n", - gpio_chip_hwgpio(lr->lines[i].desc)); + gpiod_hwgpio(lr->lines[i].desc)); } #endif @@ -2220,7 +2220,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, return; memset(info, 0, sizeof(*info)); - info->offset = gpio_chip_hwgpio(desc); + info->offset = gpiod_hwgpio(desc); if (desc->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 file *fp; - if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines)) + if (!test_bit(gpiod_hwgpio(desc), cdev->watched_lines)) return NOTIFY_DONE; /* 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) 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; } diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index 3bc93ccadb5b..ef3f2ef30cf2 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -34,30 +34,20 @@ EXPORT_SYMBOL_GPL(gpio_free); */ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { - struct gpio_desc *desc; int err; - /* Compatibility: assume unavailable "valid" GPIOs will appear later */ - desc = gpio_to_desc(gpio); - if (!desc) - return -EPROBE_DEFER; - - err = gpiod_request(desc, label); + err = gpio_request(gpio, label); if (err) return err; if (flags & GPIOF_IN) - err = gpiod_direction_input(desc); + err = gpio_direction_input(gpio); else - err = gpiod_direction_output_raw(desc, !!(flags & GPIOF_OUT_INIT_HIGH)); + err = gpio_direction_output(gpio, !!(flags & GPIOF_OUT_INIT_HIGH)); if (err) - goto free_gpio; - - return 0; + gpio_free(gpio); - free_gpio: - gpiod_free(desc); return err; } EXPORT_SYMBOL_GPL(gpio_request_one); @@ -78,11 +68,9 @@ int gpio_request(unsigned gpio, const char *label) } 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(*gpio); + gpio_free((unsigned)(unsigned long)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, unsigned long flags, const char *label) { - unsigned *dr; int rc; - dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); - if (!dr) - return -ENOMEM; + rc = gpio_request(gpio, label); + if (rc) + 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) { - devres_free(dr); + gpio_free(gpio); return rc; } - *dr = gpio; - devres_add(dev, dr); - - return 0; + return devm_add_action_or_reset(dev, devm_gpio_release, (void *)(unsigned long)gpio); } EXPORT_SYMBOL_GPL(devm_gpio_request_one); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index fad4edf9cc5c..8657379e9165 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1031,85 +1031,6 @@ static int of_gpio_threecell_xlate(struct gpio_chip *gc, 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 static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index fa1d16635ea7..8bdd107b1ad1 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -49,6 +49,7 @@ struct gpio_shared_entry { unsigned int offset; /* Index in the property value array. */ size_t index; + struct mutex lock; struct gpio_shared_desc *shared_desc; struct kref ref; struct list_head refs; @@ -58,6 +59,7 @@ static LIST_HEAD(gpio_shared_list); static DEFINE_MUTEX(gpio_shared_lock); static DEFINE_IDA(gpio_shared_ida); +#if IS_ENABLED(CONFIG_OF) static struct gpio_shared_entry * gpio_shared_find_entry(struct fwnode_handle *controller_node, unsigned int offset) @@ -72,7 +74,26 @@ gpio_shared_find_entry(struct fwnode_handle *controller_node, 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) { struct gpio_shared_entry *entry; @@ -84,6 +105,9 @@ static int gpio_shared_of_traverse(struct device_node *curr) const char *suffix; int ret, count, i; + if (gpio_shared_of_node_ignore(curr)) + return 0; + for_each_property_of_node(curr, prop) { /* * 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->index = count; INIT_LIST_HEAD(&entry->refs); + mutex_init(&entry->lock); 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) { - return gpio_shared_of_traverse(of_root); + if (of_root) + return gpio_shared_of_traverse(of_root); + + return 0; } #else 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, + struct gpio_shared_entry *entry, struct gpio_shared_ref *ref) { 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->name = "proxy"; adev->dev.parent = gdev->dev.parent; + adev->dev.platform_data = entry; adev->dev.release = gpio_shared_adev_release; ret = auxiliary_device_init(adev); @@ -250,6 +280,84 @@ static int gpio_shared_make_adev(struct gpio_device *gdev, 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) { 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(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; /* 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", fwnode_get_name(ref->fwnode)); - ret = gpio_shared_make_adev(gdev, ref); + ret = gpio_shared_make_adev(gdev, entry, ref); if (ret) return ret; } @@ -390,10 +499,11 @@ static void gpio_shared_release(struct kref *kref) { struct gpio_shared_entry *entry = 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); if (shared_desc->can_sleep) 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_device *gdev; + lockdep_assert_held(&entry->lock); + shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL); if (!shared_desc) return ERR_PTR(-ENOMEM); @@ -436,57 +548,42 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry) 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_entry *entry; - struct gpio_shared_ref *ref; - - guard(mutex)(&gpio_shared_lock); + int ret; - list_for_each_entry(entry, &gpio_shared_list, list) { - list_for_each_entry(ref, &entry->refs, list) { - if (adev != &ref->adev) - continue; + lockdep_assert_not_held(&gpio_shared_lock); - if (entry->shared_desc) { - kref_get(&entry->ref); - return entry; - } + entry = dev_get_platdata(dev); + if (WARN_ON(!entry)) + /* Programmer bug */ + return ERR_PTR(-ENOENT); + 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); if (IS_ERR(shared_desc)) return ERR_CAST(shared_desc); kref_init(&entry->ref); 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; } - } - - 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); + 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)); + } ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry); if (ret) return ERR_PTR(ret); - return entry->shared_desc; + return shared_desc; } 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) { list_del(&entry->list); + mutex_destroy(&entry->lock); fwnode_handle_put(entry->fwnode); kfree(entry); } diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index e3806db1c0e0..b44f35d68459 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -31,7 +31,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) gdev_node = to_software_node(fwnode); if (!gdev_node || !gdev_node->name) - return ERR_PTR(-EINVAL); + goto fwnode_lookup; /* * 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)) return ERR_PTR(-ENOENT); +fwnode_lookup: gdev = gpio_device_find_by_fwnode(fwnode); return gdev ?: ERR_PTR(-EPROBE_DEFER); } diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 9a849245b358..cd553acf3055 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -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) * 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) goto err_clr_bits; @@ -258,7 +258,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags) return 0; err_unlock: - gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc)); err_clr_bits: clear_bit(GPIOD_FLAG_EDGE_RISING, &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; 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_FALLING, &desc->flags); } @@ -478,10 +478,10 @@ static int export_gpio_desc(struct gpio_desc *desc) if (!guard.gc) return -ENODEV; - offset = gpio_chip_hwgpio(desc); + offset = gpiod_hwgpio(desc); if (!gpiochip_line_is_valid(guard.gc, offset)) { pr_debug_ratelimited("%s: GPIO %d masked\n", __func__, - gpio_chip_hwgpio(desc)); + gpiod_hwgpio(desc)); 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", - gpio_chip_hwgpio(desc)); + gpiod_hwgpio(desc)); if (!desc_data->chip_attr_group.name) { status = -ENOMEM; goto err_put_dirent; @@ -843,7 +843,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) if (status) 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) { status = -ENOMEM; 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); 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; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 476fcc897d90..91e0c384f34a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -236,6 +236,19 @@ int desc_to_gpio(const struct gpio_desc *desc) } 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 @@ -444,7 +457,7 @@ int gpiod_get_direction(struct gpio_desc *desc) if (!guard.gc) return -ENODEV; - offset = gpio_chip_hwgpio(desc); + offset = gpiod_hwgpio(desc); 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); if (IS_ERR(desc)) { - chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__, - PTR_ERR(desc)); + gpiochip_err(gc, "%s: unable to get GPIO desc: %ld\n", + __func__, PTR_ERR(desc)); return; } @@ -1125,7 +1138,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiodev_add_to_list_unlocked(gdev); 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; } } @@ -1537,8 +1550,7 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc, &parent_hwirq, &parent_type); if (ret) { - chip_err(gc, "skip set-up on hwirq %d\n", - i); + gpiochip_err(gc, "skip set-up on hwirq %d\n", i); continue; } @@ -1551,15 +1563,14 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc, ret = irq_domain_alloc_irqs(gc->irq.domain, 1, NUMA_NO_NODE, &fwspec); if (ret < 0) { - chip_err(gc, - "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", - i, parent_hwirq, - ret); + gpiochip_err(gc, + "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", + i, parent_hwirq, ret); } } } - chip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__); + gpiochip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__); return; } @@ -1611,15 +1622,15 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, if (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, &parent_hwirq, &parent_type); 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; } - 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 @@ -1640,8 +1651,8 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, if (ret) return ret; - chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", - irq, parent_hwirq); + gpiochip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", + irq, parent_hwirq); 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); /* @@ -1651,9 +1662,9 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, if (irq_domain_is_msi(d->parent) && (ret == -EEXIST)) ret = 0; if (ret) - chip_err(gc, - "failed to allocate parent hwirq %d for hwirq %lu\n", - parent_hwirq, hwirq); + gpiochip_err(gc, + "failed to allocate parent hwirq %d for hwirq %lu\n", + parent_hwirq, hwirq); 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 || !gc->irq.fwnode) { - chip_err(gc, "missing irqdomain vital data\n"); + gpiochip_err(gc, "missing irqdomain vital data\n"); return ERR_PTR(-EINVAL); } @@ -2002,7 +2013,7 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc) if (irqchip->flags & IRQCHIP_IMMUTABLE) 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 && !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 * practice. */ - chip_info(gc, - "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n"); + gpiochip_info(gc, + "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n"); return; } @@ -2048,7 +2059,8 @@ static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc, return -EINVAL; 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->irq.domain = domain; @@ -2089,7 +2101,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, return 0; 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; } @@ -2325,10 +2337,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, int ret; pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); - if (!pin_range) { - chip_err(gc, "failed to allocate pin ranges\n"); + if (!pin_range) return -ENOMEM; - } /* Use local offset as range ID */ 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); - 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, pinctrl_dev_get_devname(pctldev), pin_group); @@ -2388,10 +2398,8 @@ int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc, int ret; pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); - if (!pin_range) { - chip_err(gc, "failed to allocate pin ranges\n"); + if (!pin_range) return -ENOMEM; - } /* Use local offset as range ID */ pin_range->range.id = gpio_offset; @@ -2405,19 +2413,18 @@ int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc, &pin_range->range); if (IS_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); return ret; } if (pin_range->range.pins) - chip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }", - gpio_offset, gpio_offset + npins - 1, - pinctl_name, npins, pins[0]); + gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }", + gpio_offset, gpio_offset + npins - 1, + pinctl_name, npins, pins[0]); else - chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n", - gpio_offset, gpio_offset + npins - 1, - pinctl_name, - pin_offset, pin_offset + npins - 1); + gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n", + gpio_offset, gpio_offset + npins - 1, pinctl_name, + pin_offset, pin_offset + npins - 1); list_add_tail(&pin_range->node, &gdev->pin_ranges); @@ -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)) return -EBUSY; - offset = gpio_chip_hwgpio(desc); + offset = gpiod_hwgpio(desc); if (!gpiochip_line_is_valid(guard.gc, offset)) 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->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_REQUESTED, &flags); @@ -2627,7 +2634,7 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, int ret; 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; } @@ -2638,7 +2645,7 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, ret = gpiod_configure_flags(desc, label, lflags, dflags); if (ret) { 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); } @@ -2683,7 +2690,7 @@ int gpio_do_set_config(struct gpio_desc *desc, unsigned long config) if (!guard.gc->set_config) 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) ret = -EBADE; @@ -2714,7 +2721,7 @@ static int gpio_set_config_with_argument_optional(struct gpio_desc *desc, u32 argument) { struct device *dev = &desc->gdev->dev; - int gpio = gpio_chip_hwgpio(desc); + int gpio = gpiod_hwgpio(desc); int ret; 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) { ret = gpiochip_direction_input(guard.gc, - gpio_chip_hwgpio(desc)); + gpiod_hwgpio(desc)); } 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) return dir; @@ -2938,12 +2945,12 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) if (guard.gc->direction_output) { ret = gpiochip_direction_output(guard.gc, - gpio_chip_hwgpio(desc), val); + gpiod_hwgpio(desc), val); } else { /* Check that we are in output mode if we can */ if (guard.gc->get_direction) { dir = gpiochip_get_direction(guard.gc, - gpio_chip_hwgpio(desc)); + gpiod_hwgpio(desc)); if (dir < 0) 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 * 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) 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, - gpio_chip_hwgpio(desc), flags); + gpiod_hwgpio(desc), flags); if (ret) 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; } - ret = guard.gc->dis_hw_timestamp(guard.gc, gpio_chip_hwgpio(desc), + ret = guard.gc->dis_hw_timestamp(guard.gc, gpiod_hwgpio(desc), flags); if (ret) 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) { - 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 @@ -3432,7 +3439,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, first = i; do { const struct gpio_desc *desc = desc_array[i]; - int hwgpio = gpio_chip_hwgpio(desc); + int hwgpio = gpiod_hwgpio(desc); __set_bit(hwgpio, mask); i++; @@ -3454,7 +3461,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, for (j = first; j < i; ) { 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); 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) { - int ret = 0, offset = gpio_chip_hwgpio(desc); + int ret = 0, offset = gpiod_hwgpio(desc); CLASS(gpio_chip_guard, guard)(desc); 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) { - int ret = 0, offset = gpio_chip_hwgpio(desc); + int ret = 0, offset = gpiod_hwgpio(desc); CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) @@ -3652,7 +3659,7 @@ static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) return -ENODEV; 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 { 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); 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) return -ENODEV; - offset = gpio_chip_hwgpio(desc); + offset = gpiod_hwgpio(desc); if (gc->to_irq) { ret = gc->to_irq(gc, offset); if (ret) @@ -4085,8 +4092,8 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) int dir = gpiod_get_direction(desc); if (dir < 0) { - chip_err(gc, "%s: cannot get GPIO direction\n", - __func__); + gpiochip_err(gc, "%s: cannot get GPIO direction\n", + __func__); 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 */ if (test_bit(GPIOD_FLAG_IS_OUT, &desc->flags) && !test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags)) { - chip_err(gc, - "%s: tried to flag a GPIO set as output for IRQ\n", - __func__); + gpiochip_err(gc, + "%s: tried to flag a GPIO set as output for IRQ\n", + __func__); return -EIO; } @@ -4173,7 +4180,7 @@ int gpiochip_reqres_irq(struct gpio_chip *gc, unsigned int offset) ret = gpiochip_lock_as_irq(gc, offset); 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); 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)) return 0; - hwnum = gpio_chip_hwgpio(desc); + hwnum = gpiod_hwgpio(desc); local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name, 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 * 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; 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 * 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 * far belong to the same chip as this one but its pin diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index abd870fb4a3b..77f6f2936dc2 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -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); 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 */ -#define gpiod_err(desc, fmt, ...) \ +#define __gpiod_pr(level, desc, fmt, ...) \ do { \ scoped_guard(srcu, &desc->gdev->desc_srcu) { \ - pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ - gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ + pr_##level("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ?: "?", ##__VA_ARGS__); \ } \ } while (0) -#define gpiod_warn(desc, fmt, ...) \ -do { \ - scoped_guard(srcu, &desc->gdev->desc_srcu) { \ - pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ - gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ - } \ -} while (0) +#define gpiod_err(desc, fmt, ...) __gpiod_pr(err, desc, fmt, ##__VA_ARGS__) +#define gpiod_warn(desc, fmt, ...) __gpiod_pr(warn, desc, fmt, ##__VA_ARGS__) +#define gpiod_dbg(desc, fmt, ...) __gpiod_pr(debug, desc, fmt, ##__VA_ARGS__) + +/* With chip prefix */ -#define gpiod_dbg(desc, fmt, ...) \ +#define __gpiochip_pr(level, gc, 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__); \ - } \ + dev_##level(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__); \ } while (0) -/* With chip prefix */ - -#define chip_err(gc, fmt, ...) \ - dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_warn(gc, fmt, ...) \ - dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_info(gc, fmt, ...) \ - dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_dbg(gc, fmt, ...) \ - dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) +#define gpiochip_err(gc, fmt, ...) __gpiochip_pr(err, gc, fmt, ##__VA_ARGS__) +#define gpiochip_warn(gc, fmt, ...) __gpiochip_pr(warn, gc, fmt, ##__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 */ diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index 19a2246f2770..2900407d6095 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -41,6 +41,19 @@ config INTEL_VBTN To compile this driver as a module, choose M here: the module will 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 tristate "Intel ACPI INT0002 Virtual GPIO driver" depends on GPIOLIB && ACPI && PM_SLEEP diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 78acb414e154..138b13756158 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -21,6 +21,7 @@ intel-target-$(CONFIG_INTEL_HID_EVENT) += hid.o intel-target-$(CONFIG_INTEL_VBTN) += vbtn.o # 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_ISHTP_ECLITE) += ishtp_eclite.o intel-target-$(CONFIG_INTEL_OAKTRAIL) += oaktrail.o diff --git a/drivers/platform/x86/intel/chtwc_int33fe.c b/drivers/platform/x86/intel/chtwc_int33fe.c index 29e8b5432f4c..d183aa53c318 100644 --- a/drivers/platform/x86/intel/chtwc_int33fe.c +++ b/drivers/platform/x86/intel/chtwc_int33fe.c @@ -77,7 +77,7 @@ static const struct software_node max17047_node = { * software node. */ static struct software_node_ref_args fusb302_mux_refs[] = { - { .node = NULL }, + SOFTWARE_NODE_REFERENCE(NULL), }; 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); - 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) { data->dp->secondary = NULL; 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; 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. * It means we depend on the mux driver. This function will return * -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"); if (!mux_ref_node) 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 * rely on software_node_register_node_group() to use the original * 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); if (ret) @@ -345,7 +356,7 @@ static int cht_int33fe_typec_probe(struct platform_device *pdev) return fusb302_irq; } - ret = cht_int33fe_add_nodes(data); + ret = cht_int33fe_add_nodes(dev, data); if (ret) return ret; diff --git a/drivers/platform/x86/intel/ehl_pse_io.c b/drivers/platform/x86/intel/ehl_pse_io.c new file mode 100644 index 000000000000..861e14808b35 --- /dev/null +++ b/drivers/platform/x86/intel/ehl_pse_io.c @@ -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"); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 78b7078478d4..30659115de55 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -89,6 +89,7 @@ config RESET_EYEQ config RESET_GPIO tristate "GPIO reset controller" depends on GPIOLIB + select AUXILIARY_BUS help This enables a generic reset controller for resets attached via GPIOs. Typically for OF platforms this driver expects "reset-gpios" diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 22f67fc77ae5..843cffc93909 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -4,20 +4,22 @@ * * Copyright 2013 Philipp Zabel, Pengutronix */ + +#include <linux/acpi.h> #include <linux/atomic.h> +#include <linux/auxiliary_bus.h> #include <linux/cleanup.h> #include <linux/device.h> #include <linux/err.h> #include <linux/export.h> -#include <linux/kernel.h> -#include <linux/kref.h> #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> +#include <linux/gpio/property.h> #include <linux/idr.h> +#include <linux/kernel.h> +#include <linux/kref.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/acpi.h> -#include <linux/platform_device.h> #include <linux/reset.h> #include <linux/reset-controller.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 * @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 */ struct reset_gpio_lookup { struct of_phandle_args of_args; + struct fwnode_handle *swnode; 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); } -static int __reset_add_reset_gpio_lookup(int id, struct device_node *np, - unsigned int gpio, - unsigned int of_flags) +static void reset_gpio_aux_device_release(struct device *dev) { - const struct fwnode_handle *fwnode = of_fwnode_handle(np); - unsigned int lookup_flags; - const char *label_tmp; + struct auxiliary_device *adev = to_auxiliary_dev(dev); - /* - * 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. - */ - if (of_flags > GPIO_ACTIVE_LOW) { - pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n", - of_flags, gpio); - return -EINVAL; - } - - struct gpio_device *gdev __free(gpio_device_put) = gpio_device_find_by_fwnode(fwnode); - if (!gdev) - return -EPROBE_DEFER; - - label_tmp = gpio_device_get_label(gdev); - if (!label_tmp) - return -EINVAL; + kfree(adev); +} - char *label __free(kfree) = kstrdup(label_tmp, GFP_KERNEL); - if (!label) - return -ENOMEM; +static int reset_add_gpio_aux_device(struct device *parent, + struct fwnode_handle *swnode, + int id, void *pdata) +{ + struct auxiliary_device *adev; + int ret; - /* Size: one lookup entry plus sentinel */ - struct gpiod_lookup_table *lookup __free(kfree) = kzalloc(struct_size(lookup, table, 2), - GFP_KERNEL); - if (!lookup) + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) return -ENOMEM; - lookup->dev_id = kasprintf(GFP_KERNEL, "reset-gpio.%d", id); - if (!lookup->dev_id) - 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); - lookup_flags = GPIO_PERSISTENT; - lookup_flags |= of_flags & GPIO_ACTIVE_LOW; - lookup->table[0] = GPIO_LOOKUP(no_free_ptr(label), gpio, "reset", - lookup_flags); + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ret; + } - /* Not freed on success, because it is persisent subsystem data. */ - gpiod_add_lookup_table(no_free_ptr(lookup)); + ret = __auxiliary_device_add(adev, "reset"); + if (ret) { + auxiliary_device_uninit(adev); + kfree(adev); + return ret; + } - return 0; + return ret; } /* @@ -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) { + struct property_entry properties[2] = { }; + unsigned int offset, of_flags, lflags; struct reset_gpio_lookup *rgpio_dev; - struct platform_device *pdev; + struct device *parent; 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); + 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); 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); if (id < 0) return id; @@ -945,11 +966,6 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) 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; /* * 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. */ of_node_get(rgpio_dev->of_args.np); - pdev = platform_device_register_data(NULL, "reset-gpio", id, - &rgpio_dev->of_args, - sizeof(rgpio_dev->of_args)); - ret = PTR_ERR_OR_ZERO(pdev); + + rgpio_dev->swnode = fwnode_create_software_node(properties, NULL); + if (IS_ERR(rgpio_dev->swnode)) { + 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) - goto err_put; + goto err_del_swnode; list_add(&rgpio_dev->list, &reset_gpio_lookup_list); 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); -err_kfree: kfree(rgpio_dev); err_ida_free: ida_free(&reset_gpio_ida, id); diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c index 2290b25b6703..e5512b3b596b 100644 --- a/drivers/reset/reset-gpio.c +++ b/drivers/reset/reset-gpio.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/auxiliary_bus.h> #include <linux/gpio/consumer.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/platform_device.h> #include <linux/reset-controller.h> struct reset_gpio_priv { @@ -61,9 +61,10 @@ static void reset_gpio_of_node_put(void *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 reset_gpio_priv *priv; int ret; @@ -75,7 +76,7 @@ static int reset_gpio_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, &priv->rc); + auxiliary_set_drvdata(adev, &priv->rc); priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 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); } -static const struct platform_device_id reset_gpio_ids[] = { - { .name = "reset-gpio", }, +static const struct auxiliary_device_id reset_gpio_ids[] = { + { .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, .id_table = reset_gpio_ids, .driver = { .name = "reset-gpio", }, }; -module_platform_driver(reset_gpio_driver); +module_auxiliary_driver(reset_gpio_driver); MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>"); MODULE_DESCRIPTION("Generic GPIO reset driver"); diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index 14307dd800b7..4b6b65f450a8 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -52,20 +52,6 @@ static struct spi_board_info amp_info_template = { .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) { const u8 *end = buf + len; @@ -324,11 +310,6 @@ static void cs42l43_release_of_node(void *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) { 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); 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); if (!ret) { 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"); } - ret = software_node_register(&cs42l43_gpiochip_swnode); - 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); + ret = device_create_managed_software_node(&priv->ctlr->dev, props, NULL); if (ret) return dev_err_probe(priv->dev, ret, "Failed to add swnode\n"); } else { diff --git a/include/linux/ehl_pse_io_aux.h b/include/linux/ehl_pse_io_aux.h new file mode 100644 index 000000000000..afb8587ee5fb --- /dev/null +++ b/include/linux/ehl_pse_io_aux.h @@ -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_ */ diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index a8acb7c0b5af..cafeb7a40ad1 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -173,6 +173,8 @@ bool gpiod_is_shared(const struct gpio_desc *desc); struct gpio_desc *gpio_to_desc(unsigned gpio); 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, const char *con_id, int index, enum gpiod_flags flags, diff --git a/include/linux/gpio/legacy-of-mm-gpiochip.h b/include/linux/gpio/legacy-of-mm-gpiochip.h deleted file mode 100644 index 2e2bd3b19cc3..000000000000 --- a/include/linux/gpio/legacy-of-mm-gpiochip.h +++ /dev/null @@ -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 */ diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h index 87983a5f3681..12d154732ca9 100644 --- a/include/linux/gpio/regmap.h +++ b/include/linux/gpio/regmap.h @@ -50,8 +50,8 @@ struct regmap; * @regmap_irq_chip: (Optional) Pointer on an regmap_irq_chip structure. If * set, a regmap-irq device will be created and the IRQ * domain will be set accordingly. - * @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_line: (Optional) The IRQ the device uses to signal interrupts. + * @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 * register and mask pair. The base address is one of the given register diff --git a/include/linux/property.h b/include/linux/property.h index 50b26589dd70..272bfbdea7bf 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -355,19 +355,26 @@ struct software_node; /** * 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 * @args: Integer arguments */ struct software_node_ref_args { - const struct software_node *node; + const struct software_node *swnode; + struct fwnode_handle *fwnode; unsigned int nargs; u64 args[NR_FWNODE_REFERENCE_ARGS]; }; #define SOFTWARE_NODE_REFERENCE(_ref_, ...) \ (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__), \ .args = { __VA_ARGS__ }, \ } diff --git a/include/linux/string.h b/include/linux/string.h index 929d05d1247c..0266dbdaa4cd 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -564,13 +564,14 @@ static inline bool strstarts(const char *str, const char *prefix) /** * strends - Check if a string ends with another string. - * @str - NULL-terminated string to check against @suffix - * @suffix - NULL-terminated string defining the suffix to look for in @str + * @str: NULL-terminated string to check against @suffix + * @suffix: NULL-terminated string defining the suffix to look for in @str * * Returns: * 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); |
