linux-gpio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/5] Add mfd, pinctrl and pwm support to EN7581 SoC
@ 2024-08-31 14:27 Lorenzo Bianconi
  2024-08-31 14:27 ` [PATCH v3 1/5] dt-bindings: arm: airoha: Add the chip-scu node for " Lorenzo Bianconi
                   ` (4 more replies)
  0 siblings, 5 replies; 24+ messages in thread
From: Lorenzo Bianconi @ 2024-08-31 14:27 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: linux-mediatek, linux-gpio, devicetree, linux-arm-kernel,
	upstream, benjamin.larsson, ansuelsmth, linux-pwm

Introduce airoha-mfd driver in order to load pinctrl and pwm drivers for
EN7581 SoC. airoha-mfd is needed since both pinctrl and pwm drivers
needs to access the same memory block (gpio memory region) to configure
{gio,irq}_chip and pwm functionalities respectively, so model them as
childs of a parent mfd driver.
Current EN7581 pinctrl driver supports the following functionalities:
- pin multiplexing via chip_scu syscon
- pin pull-up, pull-down, open-drain, current strength,
  {input,output}_enable, output_{low,high} via chip_scu syscon
- gpio controller
- irq controller

---
Changes in v3:
- introduce airoha-mfd driver
- add pwm driver to the same series
- model pinctrl and pwm drivers as childs of a parent mfd driver.
- access chip-scu memory region in pinctrl driver via syscon
- introduce a single airoha,en7581-gpio-sysctl.yaml binding and get rid
  of dedicated bindings for pinctrl and pwm
- add airoha,en7581-chip-scu.yaml binding do the series
- Link to v2: https://lore.kernel.org/r/20240822-en7581-pinctrl-v2-0-ba1559173a7f@kernel.org

Changes in v2:
- Fix compilation errors
- Collapse some register mappings for gpio and irq controllers
- update dt-bindings according to new register mapping
- fix some dt-bindings errors
- Link to v1: https://lore.kernel.org/all/cover.1723392444.git.lorenzo@kernel.org/

---
Benjamin Larsson (1):
      pwm: airoha: Add support for EN7581 SoC

Christian Marangi (2):
      dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller
      mfd: airoha: Add support for Airoha EN7581 MFD

Lorenzo Bianconi (2):
      dt-bindings: arm: airoha: Add the chip-scu node for EN7581 SoC
      pinctrl: airoha: Add support for EN7581 SoC

 .../bindings/arm/airoha,en7581-chip-scu.yaml       |   42 +
 .../bindings/mfd/airoha,en7581-gpio-sysctl.yaml    |  433 +++
 MAINTAINERS                                        |    7 +
 drivers/mfd/Kconfig                                |    8 +
 drivers/mfd/Makefile                               |    2 +
 drivers/mfd/airoha-en7581-gpio-mfd.c               |   72 +
 drivers/pinctrl/mediatek/Kconfig                   |   16 +-
 drivers/pinctrl/mediatek/Makefile                  |    1 +
 drivers/pinctrl/mediatek/pinctrl-airoha.c          | 2964 ++++++++++++++++++++
 drivers/pwm/Kconfig                                |   10 +
 drivers/pwm/Makefile                               |    1 +
 drivers/pwm/pwm-airoha.c                           |  435 +++
 include/linux/mfd/airoha-en7581-mfd.h              |    9 +
 13 files changed, 3999 insertions(+), 1 deletion(-)
---
base-commit: defaf1a2113a22b00dfa1abc0fd2014820eaf065
change-id: 20240818-en7581-pinctrl-1bf120154be0
prerequisite-change-id: 20240705-for-6-11-bpf-a349efc08df8:v2

Best regards,
-- 
Lorenzo Bianconi <lorenzo@kernel.org>


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 1/5] dt-bindings: arm: airoha: Add the chip-scu node for EN7581 SoC
  2024-08-31 14:27 [PATCH v3 0/5] Add mfd, pinctrl and pwm support to EN7581 SoC Lorenzo Bianconi
@ 2024-08-31 14:27 ` Lorenzo Bianconi
  2024-09-03 15:05   ` Rob Herring (Arm)
  2024-08-31 14:27 ` [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller Lorenzo Bianconi
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 24+ messages in thread
From: Lorenzo Bianconi @ 2024-08-31 14:27 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: linux-mediatek, linux-gpio, devicetree, linux-arm-kernel,
	upstream, benjamin.larsson, ansuelsmth, linux-pwm

This patch adds the chip-scu document bindings for EN7581 SoC.
The airoha chip-scu block provides a configuration interface for clock,
io-muxing and other functionalities used by multiple controllers (e.g.
clock, pinctrl, ecc.) on EN7581 SoC.

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 .../bindings/arm/airoha,en7581-chip-scu.yaml       | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml b/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml
new file mode 100644
index 000000000000..67c449d804c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/airoha,en7581-chip-scu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Airoha Chip SCU Controller for EN7581 SoC
+
+maintainers:
+  - Lorenzo Bianconi <lorenzo@kernel.org>
+
+description:
+  The airoha chip-scu block provides a configuration interface for clock,
+  io-muxing and other functionalities used by multiple controllers (e.g. clock,
+  pinctrl, ecc) on EN7581 SoC.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - airoha,en7581-chip-scu
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    soc {
+      #address-cells = <2>;
+      #size-cells = <2>;
+      syscon@1fa20000 {
+        compatible = "airoha,en7581-chip-scu", "syscon";
+        reg = <0x0 0x1fa20000 0x0 0x388>;
+      };
+    };

-- 
2.46.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller
  2024-08-31 14:27 [PATCH v3 0/5] Add mfd, pinctrl and pwm support to EN7581 SoC Lorenzo Bianconi
  2024-08-31 14:27 ` [PATCH v3 1/5] dt-bindings: arm: airoha: Add the chip-scu node for " Lorenzo Bianconi
@ 2024-08-31 14:27 ` Lorenzo Bianconi
  2024-09-03 15:09   ` Rob Herring
  2024-09-03 15:33   ` Lee Jones
  2024-08-31 14:27 ` [PATCH v3 3/5] mfd: airoha: Add support for Airoha EN7581 MFD Lorenzo Bianconi
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 24+ messages in thread
From: Lorenzo Bianconi @ 2024-08-31 14:27 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: linux-mediatek, linux-gpio, devicetree, linux-arm-kernel,
	upstream, benjamin.larsson, ansuelsmth, linux-pwm

From: Christian Marangi <ansuelsmth@gmail.com>

Add support for Airoha EN7581 GPIO System Controller which provide a
register map for controlling the GPIO, pinctrl and PWM of the SoC.

Schema define cells for both gpio/interrupt controller and PWM.
Moreover it provides a dedicated pinctrl node for pins and config
definitions.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 .../bindings/mfd/airoha,en7581-gpio-sysctl.yaml    | 433 +++++++++++++++++++++
 1 file changed, 433 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
new file mode 100644
index 000000000000..a9080c7f50f9
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
@@ -0,0 +1,433 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/airoha,en7581-gpio-sysctl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Airoha EN7581 GPIO System Controller
+
+maintainers:
+  - Christian Marangi <ansuelsmth@gmail.com>
+  - Lorenzo Bianconi <lorenzo@kernel.org>
+
+description:
+  Airoha EN7581 SoC GPIO system controller which provided a register map
+  for controlling the GPIO, pins and PWM of the SoC.
+
+properties:
+  compatible:
+    const: airoha,en7581-gpio-sysctl
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  gpio-controller: true
+
+  '#gpio-cells':
+    const: 2
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 2
+
+  "#pwm-cells":
+    const: 3
+
+  pinctrl:
+    type: object
+
+    $ref: /schemas/pinctrl/pinctrl.yaml
+
+    patternProperties:
+      '-pins$':
+        type: object
+
+        patternProperties:
+          '^.*mux.*$':
+            type: object
+
+            description:
+              pinmux configuration nodes.
+
+            $ref: /schemas/pinctrl/pinmux-node.yaml
+
+            properties:
+              function:
+                description:
+                  A string containing the name of the function to mux to the group.
+                enum: [pon, tod_1pps, sipo, mdio, uart, i2c, jtag, pcm, spi,
+                       pcm_spi, i2s, emmc, pnand, pcie_reset, pwm, phy1_led0,
+                       phy2_led0, phy3_led0, phy4_led0, phy1_led1, phy2_led1,
+                       phy3_led1, phy4_led1]
+
+              groups:
+                description:
+                  An array of strings. Each string contains the name of a group.
+
+            required:
+              - function
+              - groups
+
+            allOf:
+              - if:
+                  properties:
+                    function:
+                      const: pon
+                then:
+                  properties:
+                    groups:
+                      enum: [pon]
+              - if:
+                  properties:
+                    function:
+                      const: tod_1pps
+                then:
+                  properties:
+                    groups:
+                      enum: [pon_tod_1pps, gsw_tod_1pps]
+              - if:
+                  properties:
+                    function:
+                      const: sipo
+                then:
+                  properties:
+                    groups:
+                      enum: [sipo, sipo_rclk]
+              - if:
+                  properties:
+                    function:
+                      const: mdio
+                then:
+                  properties:
+                    groups:
+                      enum: [mdio]
+              - if:
+                  properties:
+                    function:
+                      const: uart
+                then:
+                  properties:
+                    groups:
+                      items:
+                        enum: [uart2, uart2_cts_rts, hsuart, hsuart_cts_rts, uart4,
+                               uart5]
+                      maxItems: 2
+              - if:
+                  properties:
+                    function:
+                      const: i2c
+                then:
+                  properties:
+                    groups:
+                      enum: [i2c1]
+              - if:
+                  properties:
+                    function:
+                      const: jtag
+                then:
+                  properties:
+                    groups:
+                      enum: [jtag_udi, jtag_dfd]
+              - if:
+                  properties:
+                    function:
+                      const: pcm
+                then:
+                  properties:
+                    groups:
+                      enum: [pcm1, pcm2]
+              - if:
+                  properties:
+                    function:
+                      const: spi
+                then:
+                  properties:
+                    groups:
+                      items:
+                        enum: [spi_quad, spi_cs1]
+                      maxItems: 2
+              - if:
+                  properties:
+                    function:
+                      const: pcm_spi
+                then:
+                  properties:
+                    groups:
+                      items:
+                        enum: [pcm_spi, pcm_spi_int, pcm_spi_rst, pcm_spi_cs1,
+                               pcm_spi_cs2_p156, pcm_spi_cs2_p128, pcm_spi_cs3,
+                               pcm_spi_cs4]
+                      maxItems: 7
+              - if:
+                  properties:
+                    function:
+                      const: i2c
+                then:
+                  properties:
+                    groups:
+                      enum: [i2s]
+              - if:
+                  properties:
+                    function:
+                      const: emmc
+                then:
+                  properties:
+                    groups:
+                      enum: [emmc]
+              - if:
+                  properties:
+                    function:
+                      const: pnand
+                then:
+                  properties:
+                    groups:
+                      enum: [pnand]
+              - if:
+                  properties:
+                    function:
+                      const: pcie_reset
+                then:
+                  properties:
+                    groups:
+                      enum: [pcie_reset0, pcie_reset1, pcie_reset2]
+              - if:
+                  properties:
+                    function:
+                      const: pwm
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio0, gpio1, gpio2, gpio3, gpio4, gpio5, gpio6,
+                             gpio7, gpio8, gpio9, gpio10, gpio11, gpio12, gpio13,
+                             gpio14, gpio15, gpio16, gpio17, gpio18, gpio19,
+                             gpio20, gpio21, gpio22, gpio23, gpio24, gpio25,
+                             gpio26, gpio27, gpio28, gpio29, gpio30, gpio31,
+                             gpio36, gpio37, gpio38, gpio39, gpio40, gpio41,
+                             gpio42, gpio43, gpio44, gpio45, gpio46, gpio47]
+              - if:
+                  properties:
+                    function:
+                      const: phy1_led0
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio33, gpio34, gpio35, gpio42]
+              - if:
+                  properties:
+                    function:
+                      const: phy2_led0
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio33, gpio34, gpio35, gpio42]
+              - if:
+                  properties:
+                    function:
+                      const: phy3_led0
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio33, gpio34, gpio35, gpio42]
+              - if:
+                  properties:
+                    function:
+                      const: phy4_led0
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio33, gpio34, gpio35, gpio42]
+              - if:
+                  properties:
+                    function:
+                      const: phy1_led1
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio43, gpio44, gpio45, gpio46]
+              - if:
+                  properties:
+                    function:
+                      const: phy2_led1
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio43, gpio44, gpio45, gpio46]
+              - if:
+                  properties:
+                    function:
+                      const: phy3_led1
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio43, gpio44, gpio45, gpio46]
+              - if:
+                  properties:
+                    function:
+                      const: phy4_led1
+                then:
+                  properties:
+                    groups:
+                      enum: [gpio43, gpio44, gpio45, gpio46]
+
+            additionalProperties: false
+
+          '^.*conf.*$':
+            type: object
+
+            description:
+              pinconf configuration nodes.
+
+            $ref: /schemas/pinctrl/pincfg-node.yaml
+
+            properties:
+              pins:
+                description:
+                  An array of strings. Each string contains the name of a pin.
+                items:
+                  enum: [uart1_txd, uart1_rxd, i2c_scl, i2c_sda, spi_cs0, spi_clk,
+                         spi_mosi, spi_miso, gpio0, gpio1, gpio2, gpio3, gpio4,
+                         gpio5, gpio6, gpio7, gpio8, gpio9, gpio10, gpio11, gpio12,
+                         gpio13, gpio14, gpio15, gpio16, gpio17, gpio18, gpio19,
+                         gpio20, gpio21, gpio22, gpio23, gpio24, gpio25, gpio26,
+                         gpio27, gpio28, gpio29, gpio30, gpio31, gpio32, gpio33,
+                         gpio34, gpio35, gpio36, gpio37, gpio38, gpio39, gpio40,
+                         gpio41, gpio42, gpio43, gpio44, gpio45, gpio46,
+                         pcie_reset0, pcie_reset1, pcie_reset2]
+                minItems: 1
+                maxItems: 58
+
+              bias-disable: true
+
+              bias-pull-up: true
+
+              bias-pull-down: true
+
+              input-enable: true
+
+              output-enable: true
+
+              output-low: true
+
+              output-high: true
+
+              drive-open-drain: true
+
+              drive-strength:
+                description:
+                  Selects the drive strength for MIO pins, in mA.
+                enum: [2, 4, 6, 8]
+
+            required:
+              - pins
+
+            additionalProperties: false
+
+        additionalProperties: false
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - gpio-controller
+  - "#gpio-cells"
+  - "#pwm-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    mfd@1fbf0200 {
+        compatible = "airoha,en7581-gpio-sysctl";
+        reg = <0x1fbf0200 0xc0>;
+
+        interrupt-parent = <&gic>;
+        interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+
+        gpio-controller;
+        #gpio-cells = <2>;
+
+        interrupt-controller;
+        #interrupt-cells = <2>;
+
+        #pwm-cells = <3>;
+
+        pinctrl {
+            pcie1-rst-pins {
+                conf {
+                    pins = "pcie_reset1";
+                    drive-open-drain = <1>;
+                };
+            };
+
+            pwm-pins {
+                mux {
+                    function = "pwm";
+                    groups = "gpio18";
+                };
+            };
+
+            spi-pins {
+                mux {
+                    function = "spi";
+                    groups = "spi_quad", "spi_cs1";
+                };
+            };
+
+            uart2-pins {
+                mux {
+                    function = "uart";
+                    groups = "uart2", "uart2_cts_rts";
+                };
+            };
+
+            uar5-pins {
+                mux {
+                    function = "uart";
+                    groups = "uart5";
+                };
+            };
+
+            mmc-pins {
+                mux {
+                    function = "emmc";
+                    groups = "emmc";
+                };
+            };
+
+            mdio-pins {
+                mux {
+                    function = "mdio";
+                    groups = "mdio";
+                };
+
+                conf {
+                    pins = "gpio2";
+                    output-enable;
+                };
+            };
+
+            gswp1-led0-pins {
+                mux {
+                    function = "phy1_led0";
+                    groups = "gpio33";
+                };
+            };
+
+            gswp2-led1-pins {
+                mux {
+                    function = "phy2_led1";
+                    groups = "gpio44";
+                };
+            };
+        };
+    };
+
+...

-- 
2.46.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 3/5] mfd: airoha: Add support for Airoha EN7581 MFD
  2024-08-31 14:27 [PATCH v3 0/5] Add mfd, pinctrl and pwm support to EN7581 SoC Lorenzo Bianconi
  2024-08-31 14:27 ` [PATCH v3 1/5] dt-bindings: arm: airoha: Add the chip-scu node for " Lorenzo Bianconi
  2024-08-31 14:27 ` [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller Lorenzo Bianconi
@ 2024-08-31 14:27 ` Lorenzo Bianconi
  2024-08-31 14:27 ` [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC Lorenzo Bianconi
  2024-08-31 14:27 ` [PATCH v3 5/5] pwm: " Lorenzo Bianconi
  4 siblings, 0 replies; 24+ messages in thread
From: Lorenzo Bianconi @ 2024-08-31 14:27 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: linux-mediatek, linux-gpio, devicetree, linux-arm-kernel,
	upstream, benjamin.larsson, ansuelsmth, linux-pwm

From: Christian Marangi <ansuelsmth@gmail.com>

Support for Airoha EN7581 Multi Function Device that
expose PINCTRL functionality and PWM functionality.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
 drivers/mfd/Kconfig                   |  8 ++++
 drivers/mfd/Makefile                  |  2 +
 drivers/mfd/airoha-en7581-gpio-mfd.c  | 72 +++++++++++++++++++++++++++++++++++
 include/linux/mfd/airoha-en7581-mfd.h |  9 +++++
 4 files changed, 91 insertions(+)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index bc8be2e593b6..c690378066ac 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -20,6 +20,14 @@ config MFD_CS5535
 	  This is the core driver for CS5535/CS5536 MFD functions.  This is
 	  necessary for using the board's GPIO and MFGPT functionality.
 
+config MFD_AIROHA_EN7581
+	bool "Airoha EN7581 Multi Function Device"
+	depends on (ARCH_AIROHA || COMPILE_TEST) && OF
+	select MFD_CORE
+	help
+	  Support for Airoha EN7581 Multi Function Device that
+	  expose PINCTRL functionality and PWM functionality.
+
 config MFD_ALTERA_A10SR
 	bool "Altera Arria10 DevKit System Resource chip"
 	depends on ARCH_INTEL_SOCFPGA && SPI_MASTER=y && OF
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 02b651cd7535..075dbff618e0 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -256,6 +256,8 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTWC)	+= intel_soc_pmic_chtwc.o
 obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI)	+= intel_soc_pmic_chtdc_ti.o
 obj-$(CONFIG_INTEL_SOC_PMIC_MRFLD)	+= intel_soc_pmic_mrfld.o
 
+obj-$(CONFIG_MFD_AIROHA_EN7581)	+= airoha-en7581-gpio-mfd.o
+
 obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
 obj-$(CONFIG_MFD_ALTERA_SYSMGR) += altera-sysmgr.o
 obj-$(CONFIG_MFD_STPMIC1)	+= stpmic1.o
diff --git a/drivers/mfd/airoha-en7581-gpio-mfd.c b/drivers/mfd/airoha-en7581-gpio-mfd.c
new file mode 100644
index 000000000000..7888c175f6c5
--- /dev/null
+++ b/drivers/mfd/airoha-en7581-gpio-mfd.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MFD driver for Airoha EN7581
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/mfd/airoha-en7581-mfd.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+
+static struct resource airoha_mfd_pinctrl_intr[] = {
+	{
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static const struct mfd_cell airoha_mfd_devs[] = {
+	{
+		.name = "pinctrl-airoha",
+		.resources = airoha_mfd_pinctrl_intr,
+		.num_resources = ARRAY_SIZE(airoha_mfd_pinctrl_intr),
+	}, {
+		.name = "airoha-pwm",
+	},
+};
+
+static int airoha_mfd_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct airoha_mfd *mfd;
+	int irq;
+
+	mfd = devm_kzalloc(dev, sizeof(*mfd), GFP_KERNEL);
+	if (!mfd)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, mfd);
+
+	mfd->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(mfd->base))
+		return PTR_ERR(mfd->base);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	airoha_mfd_pinctrl_intr[0].start = irq;
+	airoha_mfd_pinctrl_intr[0].end = irq;
+
+	return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, airoha_mfd_devs,
+				    ARRAY_SIZE(airoha_mfd_devs), NULL, 0,
+				    NULL);
+}
+
+static const struct of_device_id airoha_mfd_of_match[] = {
+	{ .compatible = "airoha,en7581-gpio-sysctl" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, airoha_mfd_of_match);
+
+static struct platform_driver airoha_mfd_driver = {
+	.probe = airoha_mfd_probe,
+	.driver = {
+		.name = KBUILD_MODNAME,
+		.of_match_table = airoha_mfd_of_match,
+	},
+};
+module_platform_driver(airoha_mfd_driver);
+
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("Driver for Airoha EN7581 MFD");
diff --git a/include/linux/mfd/airoha-en7581-mfd.h b/include/linux/mfd/airoha-en7581-mfd.h
new file mode 100644
index 000000000000..25e73952a777
--- /dev/null
+++ b/include/linux/mfd/airoha-en7581-mfd.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_INCLUDE_MFD_AIROHA_EN7581_MFD_H_
+#define _LINUX_INCLUDE_MFD_AIROHA_EN7581_MFD_H_
+
+struct airoha_mfd {
+	void __iomem *base;
+};
+
+#endif  /* _LINUX_INCLUDE_MFD_AIROHA_EN7581_MFD_H_ */

-- 
2.46.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC
  2024-08-31 14:27 [PATCH v3 0/5] Add mfd, pinctrl and pwm support to EN7581 SoC Lorenzo Bianconi
                   ` (2 preceding siblings ...)
  2024-08-31 14:27 ` [PATCH v3 3/5] mfd: airoha: Add support for Airoha EN7581 MFD Lorenzo Bianconi
@ 2024-08-31 14:27 ` Lorenzo Bianconi
  2024-08-31 17:35   ` kernel test robot
                     ` (2 more replies)
  2024-08-31 14:27 ` [PATCH v3 5/5] pwm: " Lorenzo Bianconi
  4 siblings, 3 replies; 24+ messages in thread
From: Lorenzo Bianconi @ 2024-08-31 14:27 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: linux-mediatek, linux-gpio, devicetree, linux-arm-kernel,
	upstream, benjamin.larsson, ansuelsmth, linux-pwm

Introduce pinctrl driver for EN7581 SoC. Current EN7581 pinctrl driver
supports the following functionalities:
- pin multiplexing
- pin pull-up, pull-down, open-drain, current strength,
  {input,output}_enable, output_{low,high}
- gpio controller
- irq controller

Tested-by: Benjamin Larsson <benjamin.larsson@genexis.eu>
Co-developed-by: Benjamin Larsson <benjamin.larsson@genexis.eu>
Signed-off-by: Benjamin Larsson <benjamin.larsson@genexis.eu>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 MAINTAINERS                               |    7 +
 drivers/pinctrl/mediatek/Kconfig          |   16 +-
 drivers/pinctrl/mediatek/Makefile         |    1 +
 drivers/pinctrl/mediatek/pinctrl-airoha.c | 2964 +++++++++++++++++++++++++++++
 4 files changed, 2987 insertions(+), 1 deletion(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8766f3e5e87e..75c0ae2224e3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17967,6 +17967,13 @@ F:	drivers/pinctrl/
 F:	include/dt-bindings/pinctrl/
 F:	include/linux/pinctrl/
 
+PIN CONTROLLER - AIROHA
+M:	Lorenzo Bianconi <lorenzo@kernel.org>
+L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml
+F:	drivers/pinctrl/mediatek/pinctrl-airoha.c
+
 PIN CONTROLLER - AMD
 M:	Basavaraj Natikar <Basavaraj.Natikar@amd.com>
 M:	Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig
index 7af287252834..43bbf606ee7d 100644
--- a/drivers/pinctrl/mediatek/Kconfig
+++ b/drivers/pinctrl/mediatek/Kconfig
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "MediaTek pinctrl drivers"
-	depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST
+	depends on ARCH_MEDIATEK || ARCH_AIROHA || RALINK || COMPILE_TEST
 
 config EINT_MTK
 	tristate "MediaTek External Interrupt Support"
@@ -126,6 +126,20 @@ config PINCTRL_MT8127
 	select PINCTRL_MTK
 
 # For ARMv8 SoCs
+config PINCTRL_AIROHA
+	tristate "Airoha pin control"
+	depends on OF
+	depends on ARM64 || COMPILE_TEST
+	select PINMUX
+	select GENERIC_PINCONF
+	select GENERIC_PINCTRL_GROUPS
+	select GENERIC_PINMUX_FUNCTIONS
+	select GPIOLIB
+	select GPIOLIB_IRQCHIP
+	help
+	  Say yes here to support pin controller and gpio driver
+	  on Airoha EN7581 SoC.
+
 config PINCTRL_MT2712
 	bool "MediaTek MT2712 pin control"
 	depends on OF
diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile
index 680f7e8526e0..1405d434218e 100644
--- a/drivers/pinctrl/mediatek/Makefile
+++ b/drivers/pinctrl/mediatek/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_PINCTRL_MTK_MOORE)		+= pinctrl-moore.o
 obj-$(CONFIG_PINCTRL_MTK_PARIS)		+= pinctrl-paris.o
 
 # SoC Drivers
+obj-$(CONFIG_PINCTRL_AIROHA)		+= pinctrl-airoha.o
 obj-$(CONFIG_PINCTRL_MT7620)		+= pinctrl-mt7620.o
 obj-$(CONFIG_PINCTRL_MT7621)		+= pinctrl-mt7621.o
 obj-$(CONFIG_PINCTRL_MT76X8)		+= pinctrl-mt76x8.o
diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c
new file mode 100644
index 000000000000..f942c2b75100
--- /dev/null
+++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c
@@ -0,0 +1,2964 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
+ * Author: Benjamin Larsson <benjamin.larsson@genexis.eu>
+ * Author: Markus Gothe <markus.gothe@genexis.eu>
+ */
+
+#include <dt-bindings/pinctrl/mt65xx.h>
+#include <linux/bitfield.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/airoha-en7581-mfd.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "../core.h"
+#include "../pinconf.h"
+#include "../pinmux.h"
+
+#define PINCTRL_PIN_GROUP(id)						\
+	PINCTRL_PINGROUP(#id, id##_pins, ARRAY_SIZE(id##_pins))
+
+#define PINCTRL_FUNC_DESC(id)						\
+	{								\
+		.desc = {						\
+			.func = {					\
+				.name = #id,				\
+				.groups = id##_groups,			\
+				.ngroups = ARRAY_SIZE(id##_groups),	\
+			}						\
+		},							\
+		.groups = id##_func_group,				\
+		.group_size = ARRAY_SIZE(id##_func_group),		\
+	}
+
+#define PINCTRL_CONF_DESC(p, offset, mask)				\
+	{								\
+		.pin = p,						\
+		.reg = { offset, mask },				\
+	}
+
+/* MUX */
+#define REG_GPIO_2ND_I2C_MODE			0x0214
+#define GPIO_MDC_IO_MASTER_MODE_MODE		BIT(14)
+#define GPIO_I2C_MASTER_MODE_MODE		BIT(13)
+#define GPIO_I2S_MODE_MASK			BIT(12)
+#define GPIO_I2C_SLAVE_MODE_MODE		BIT(11)
+#define GPIO_LAN3_LED1_MODE_MASK		BIT(10)
+#define GPIO_LAN3_LED0_MODE_MASK		BIT(9)
+#define GPIO_LAN2_LED1_MODE_MASK		BIT(8)
+#define GPIO_LAN2_LED0_MODE_MASK		BIT(7)
+#define GPIO_LAN1_LED1_MODE_MASK		BIT(6)
+#define GPIO_LAN1_LED0_MODE_MASK		BIT(5)
+#define GPIO_LAN0_LED1_MODE_MASK		BIT(4)
+#define GPIO_LAN0_LED0_MODE_MASK		BIT(3)
+#define PON_TOD_1PPS_MODE_MASK			BIT(2)
+#define GSW_TOD_1PPS_MODE_MASK			BIT(1)
+#define GPIO_2ND_I2C_MODE_MASK			BIT(0)
+
+#define REG_GPIO_SPI_CS1_MODE			0x0218
+#define GPIO_PCM_SPI_CS4_MODE_MASK		BIT(21)
+#define GPIO_PCM_SPI_CS3_MODE_MASK		BIT(20)
+#define GPIO_PCM_SPI_CS2_MODE_P156_MASK		BIT(19)
+#define GPIO_PCM_SPI_CS2_MODE_P128_MASK		BIT(18)
+#define GPIO_PCM_SPI_CS1_MODE_MASK		BIT(17)
+#define GPIO_PCM_SPI_MODE_MASK			BIT(16)
+#define GPIO_PCM2_MODE_MASK			BIT(13)
+#define GPIO_PCM1_MODE_MASK			BIT(12)
+#define GPIO_PCM_INT_MODE_MASK			BIT(9)
+#define GPIO_PCM_RESET_MODE_MASK		BIT(8)
+#define GPIO_SPI_QUAD_MODE_MASK			BIT(4)
+#define GPIO_SPI_CS4_MODE_MASK			BIT(3)
+#define GPIO_SPI_CS3_MODE_MASK			BIT(2)
+#define GPIO_SPI_CS2_MODE_MASK			BIT(1)
+#define GPIO_SPI_CS1_MODE_MASK			BIT(0)
+
+#define REG_GPIO_PON_MODE			0x021c
+#define GPIO_PARALLEL_NAND_MODE_MASK		BIT(14)
+#define GPIO_SGMII_MDIO_MODE_MASK		BIT(13)
+#define GPIO_PCIE_RESET2_MASK			BIT(12)
+#define SIPO_RCLK_MODE_MASK			BIT(11)
+#define GPIO_PCIE_RESET1_MASK			BIT(10)
+#define GPIO_PCIE_RESET0_MASK			BIT(9)
+#define GPIO_UART5_MODE_MASK			BIT(8)
+#define GPIO_UART4_MODE_MASK			BIT(7)
+#define GPIO_HSUART_CTS_RTS_MODE_MASK		BIT(6)
+#define GPIO_HSUART_MODE_MASK			BIT(5)
+#define GPIO_UART2_CTS_RTS_MODE_MASK		BIT(4)
+#define GPIO_UART2_MODE_MASK			BIT(3)
+#define GPIO_SIPO_MODE_MASK			BIT(2)
+#define GPIO_EMMC_MODE_MASK			BIT(1)
+#define GPIO_PON_MODE_MASK			BIT(0)
+
+#define REG_NPU_UART_EN				0x0224
+#define JTAG_UDI_EN_MASK			BIT(4)
+#define JTAG_DFD_EN_MASK			BIT(3)
+
+/* LED MAP */
+#define REG_LAN_LED0_MAPPING			0x027c
+#define REG_LAN_LED1_MAPPING			0x0280
+
+#define LAN4_LED_MAPPING_MASK			GENMASK(18, 16)
+#define LAN4_PHY4_LED_MAP			BIT(18)
+#define LAN4_PHY2_LED_MAP			BIT(17)
+#define LAN4_PHY1_LED_MAP			BIT(16)
+#define LAN4_PHY0_LED_MAP			0
+#define LAN4_PHY3_LED_MAP			GENMASK(17, 16)
+
+#define LAN3_LED_MAPPING_MASK			GENMASK(14, 12)
+#define LAN3_PHY4_LED_MAP			BIT(14)
+#define LAN3_PHY2_LED_MAP			BIT(13)
+#define LAN3_PHY1_LED_MAP			BIT(12)
+#define LAN3_PHY0_LED_MAP			0
+#define LAN3_PHY3_LED_MAP			GENMASK(13, 12)
+
+#define LAN2_LED_MAPPING_MASK			GENMASK(10, 8)
+#define LAN2_PHY4_LED_MAP			BIT(12)
+#define LAN2_PHY2_LED_MAP			BIT(11)
+#define LAN2_PHY1_LED_MAP			BIT(10)
+#define LAN2_PHY0_LED_MAP			0
+#define LAN2_PHY3_LED_MAP			GENMASK(11, 10)
+
+#define LAN1_LED_MAPPING_MASK			GENMASK(6, 4)
+#define LAN1_PHY4_LED_MAP			BIT(6)
+#define LAN1_PHY2_LED_MAP			BIT(5)
+#define LAN1_PHY1_LED_MAP			BIT(4)
+#define LAN1_PHY0_LED_MAP			0
+#define LAN1_PHY3_LED_MAP			GENMASK(5, 4)
+
+#define LAN0_LED_MAPPING_MASK			GENMASK(2, 0)
+#define LAN0_PHY4_LED_MAP			BIT(3)
+#define LAN0_PHY2_LED_MAP			BIT(2)
+#define LAN0_PHY1_LED_MAP			BIT(1)
+#define LAN0_PHY0_LED_MAP			0
+#define LAN0_PHY3_LED_MAP			GENMASK(2, 1)
+
+/* CONF */
+#define REG_I2C_SDA_E2				0x001c
+#define SPI_MISO_E2_MASK			BIT(14)
+#define SPI_MOSI_E2_MASK			BIT(13)
+#define SPI_CLK_E2_MASK				BIT(12)
+#define SPI_CS0_E2_MASK				BIT(11)
+#define PCIE2_RESET_E2_MASK			BIT(10)
+#define PCIE1_RESET_E2_MASK			BIT(9)
+#define PCIE0_RESET_E2_MASK			BIT(8)
+#define UART1_RXD_E2_MASK			BIT(3)
+#define UART1_TXD_E2_MASK			BIT(2)
+#define I2C_SCL_E2_MASK				BIT(1)
+#define I2C_SDA_E2_MASK				BIT(0)
+
+#define REG_I2C_SDA_E4				0x0020
+#define SPI_MISO_E4_MASK			BIT(14)
+#define SPI_MOSI_E4_MASK			BIT(13)
+#define SPI_CLK_E4_MASK				BIT(12)
+#define SPI_CS0_E4_MASK				BIT(11)
+#define PCIE2_RESET_E4_MASK			BIT(10)
+#define PCIE1_RESET_E4_MASK			BIT(9)
+#define PCIE0_RESET_E4_MASK			BIT(8)
+#define UART1_RXD_E4_MASK			BIT(3)
+#define UART1_TXD_E4_MASK			BIT(2)
+#define I2C_SCL_E4_MASK				BIT(1)
+#define I2C_SDA_E4_MASK				BIT(0)
+
+#define REG_GPIO_L_E2				0x0024
+#define REG_GPIO_L_E4				0x0028
+#define REG_GPIO_H_E2				0x002c
+#define REG_GPIO_H_E4				0x0030
+
+#define REG_I2C_SDA_PU				0x0044
+#define SPI_MISO_PU_MASK			BIT(14)
+#define SPI_MOSI_PU_MASK			BIT(13)
+#define SPI_CLK_PU_MASK				BIT(12)
+#define SPI_CS0_PU_MASK				BIT(11)
+#define PCIE2_RESET_PU_MASK			BIT(10)
+#define PCIE1_RESET_PU_MASK			BIT(9)
+#define PCIE0_RESET_PU_MASK			BIT(8)
+#define UART1_RXD_PU_MASK			BIT(3)
+#define UART1_TXD_PU_MASK			BIT(2)
+#define I2C_SCL_PU_MASK				BIT(1)
+#define I2C_SDA_PU_MASK				BIT(0)
+
+#define REG_I2C_SDA_PD				0x0048
+#define SPI_MISO_PD_MASK			BIT(14)
+#define SPI_MOSI_PD_MASK			BIT(13)
+#define SPI_CLK_PD_MASK				BIT(12)
+#define SPI_CS0_PD_MASK				BIT(11)
+#define PCIE2_RESET_PD_MASK			BIT(10)
+#define PCIE1_RESET_PD_MASK			BIT(9)
+#define PCIE0_RESET_PD_MASK			BIT(8)
+#define UART1_RXD_PD_MASK			BIT(3)
+#define UART1_TXD_PD_MASK			BIT(2)
+#define I2C_SCL_PD_MASK				BIT(1)
+#define I2C_SDA_PD_MASK				BIT(0)
+
+#define REG_GPIO_L_PU				0x004c
+#define REG_GPIO_L_PD				0x0050
+#define REG_GPIO_H_PU				0x0054
+#define REG_GPIO_H_PD				0x0058
+
+#define REG_PCIE_RESET_OD			0x018c
+#define PCIE2_RESET_OD_MASK			BIT(2)
+#define PCIE1_RESET_OD_MASK			BIT(1)
+#define PCIE0_RESET_OD_MASK			BIT(0)
+
+/* GPIOs */
+#define REG_GPIO_CTRL				0x0000
+#define REG_GPIO_DATA				0x0004
+#define REG_GPIO_INT				0x0008
+#define REG_GPIO_INT_EDGE			0x000c
+#define REG_GPIO_INT_LEVEL			0x0010
+#define REG_GPIO_OE				0x0014
+#define REG_GPIO_CTRL1				0x0020
+
+/* PWM MODE CONF */
+#define REG_GPIO_FLASH_MODE_CFG			0x0034
+#define GPIO15_FLASH_MODE_CFG			BIT(15)
+#define GPIO14_FLASH_MODE_CFG			BIT(14)
+#define GPIO13_FLASH_MODE_CFG			BIT(13)
+#define GPIO12_FLASH_MODE_CFG			BIT(12)
+#define GPIO11_FLASH_MODE_CFG			BIT(11)
+#define GPIO10_FLASH_MODE_CFG			BIT(10)
+#define GPIO9_FLASH_MODE_CFG			BIT(9)
+#define GPIO8_FLASH_MODE_CFG			BIT(8)
+#define GPIO7_FLASH_MODE_CFG			BIT(7)
+#define GPIO6_FLASH_MODE_CFG			BIT(6)
+#define GPIO5_FLASH_MODE_CFG			BIT(5)
+#define GPIO4_FLASH_MODE_CFG			BIT(4)
+#define GPIO3_FLASH_MODE_CFG			BIT(3)
+#define GPIO2_FLASH_MODE_CFG			BIT(2)
+#define GPIO1_FLASH_MODE_CFG			BIT(1)
+#define GPIO0_FLASH_MODE_CFG			BIT(0)
+
+#define REG_GPIO_CTRL2				0x0060
+#define REG_GPIO_CTRL3				0x0064
+
+/* PWM MODE CONF EXT */
+#define REG_GPIO_FLASH_MODE_CFG_EXT		0x0068
+#define GPIO51_FLASH_MODE_CFG			BIT(31)
+#define GPIO50_FLASH_MODE_CFG			BIT(30)
+#define GPIO49_FLASH_MODE_CFG			BIT(29)
+#define GPIO48_FLASH_MODE_CFG			BIT(28)
+#define GPIO47_FLASH_MODE_CFG			BIT(27)
+#define GPIO46_FLASH_MODE_CFG			BIT(26)
+#define GPIO45_FLASH_MODE_CFG			BIT(25)
+#define GPIO44_FLASH_MODE_CFG			BIT(24)
+#define GPIO43_FLASH_MODE_CFG			BIT(23)
+#define GPIO42_FLASH_MODE_CFG			BIT(22)
+#define GPIO41_FLASH_MODE_CFG			BIT(21)
+#define GPIO40_FLASH_MODE_CFG			BIT(20)
+#define GPIO39_FLASH_MODE_CFG			BIT(19)
+#define GPIO38_FLASH_MODE_CFG			BIT(18)
+#define GPIO37_FLASH_MODE_CFG			BIT(17)
+#define GPIO36_FLASH_MODE_CFG			BIT(16)
+#define GPIO31_FLASH_MODE_CFG			BIT(15)
+#define GPIO30_FLASH_MODE_CFG			BIT(14)
+#define GPIO29_FLASH_MODE_CFG			BIT(13)
+#define GPIO28_FLASH_MODE_CFG			BIT(12)
+#define GPIO27_FLASH_MODE_CFG			BIT(11)
+#define GPIO26_FLASH_MODE_CFG			BIT(10)
+#define GPIO25_FLASH_MODE_CFG			BIT(9)
+#define GPIO24_FLASH_MODE_CFG			BIT(8)
+#define GPIO23_FLASH_MODE_CFG			BIT(7)
+#define GPIO22_FLASH_MODE_CFG			BIT(6)
+#define GPIO21_FLASH_MODE_CFG			BIT(5)
+#define GPIO20_FLASH_MODE_CFG			BIT(4)
+#define GPIO19_FLASH_MODE_CFG			BIT(3)
+#define GPIO18_FLASH_MODE_CFG			BIT(2)
+#define GPIO17_FLASH_MODE_CFG			BIT(1)
+#define GPIO16_FLASH_MODE_CFG			BIT(0)
+
+#define REG_GPIO_DATA1				0x0070
+#define REG_GPIO_OE1				0x0078
+#define REG_GPIO_INT1				0x007c
+#define REG_GPIO_INT_EDGE1			0x0080
+#define REG_GPIO_INT_EDGE2			0x0084
+#define REG_GPIO_INT_EDGE3			0x0088
+#define REG_GPIO_INT_LEVEL1			0x008c
+#define REG_GPIO_INT_LEVEL2			0x0090
+#define REG_GPIO_INT_LEVEL3			0x0094
+
+#define AIROHA_NUM_GPIOS			64
+#define AIROHA_GPIO_BANK_SIZE			(AIROHA_NUM_GPIOS / 2)
+#define AIROHA_REG_GPIOCTRL_NUM_GPIO		(AIROHA_NUM_GPIOS / 4)
+
+struct airoha_pinctrl_reg {
+	u32 offset;
+	u32 mask;
+};
+
+enum airoha_pinctrl_mux_func {
+	AIROHA_FUNC_MUX,
+	AIROHA_FUNC_PWM_MUX,
+	AIROHA_FUNC_PWM_EXT_MUX,
+};
+
+struct airoha_pinctrl_func_group {
+	const char *name;
+	struct {
+		enum airoha_pinctrl_mux_func mux;
+		u32 offset;
+		u32 mask;
+		u32 val;
+	} regmap[2];
+	int regmap_size;
+};
+
+struct airoha_pinctrl_func {
+	const struct function_desc desc;
+	const struct airoha_pinctrl_func_group *groups;
+	u8 group_size;
+};
+
+struct airoha_pinctrl_conf {
+	u32 pin;
+	struct airoha_pinctrl_reg reg;
+};
+
+struct airoha_pinctrl_gpiochip {
+	struct gpio_chip chip;
+
+	void __iomem *data[2];
+	void __iomem *dir[4];
+	void __iomem *out[2];
+
+	/* protect concurrent register accesses */
+	spinlock_t lock;
+	void __iomem *status[2];
+	void __iomem *level[4];
+	void __iomem *edge[4];
+
+	u32 irq_type[AIROHA_NUM_GPIOS];
+};
+
+struct airoha_pinctrl {
+	struct pinctrl_dev *ctrl;
+
+	struct regmap *chip_scu;
+	/* protect concurrent register accesses */
+	struct mutex mutex;
+	void __iomem *base;
+
+	struct airoha_pinctrl_gpiochip gpiochip;
+};
+
+static struct pinctrl_pin_desc airoha_pinctrl_pins[] = {
+	PINCTRL_PIN(0, "uart1_txd"),
+	PINCTRL_PIN(1, "uart1_rxd"),
+	PINCTRL_PIN(2, "i2c_scl"),
+	PINCTRL_PIN(3, "i2c_sda"),
+	PINCTRL_PIN(4, "spi_cs0"),
+	PINCTRL_PIN(5, "spi_clk"),
+	PINCTRL_PIN(6, "spi_mosi"),
+	PINCTRL_PIN(7, "spi_miso"),
+	PINCTRL_PIN(13, "gpio0"),
+	PINCTRL_PIN(14, "gpio1"),
+	PINCTRL_PIN(15, "gpio2"),
+	PINCTRL_PIN(16, "gpio3"),
+	PINCTRL_PIN(17, "gpio4"),
+	PINCTRL_PIN(18, "gpio5"),
+	PINCTRL_PIN(19, "gpio6"),
+	PINCTRL_PIN(20, "gpio7"),
+	PINCTRL_PIN(21, "gpio8"),
+	PINCTRL_PIN(22, "gpio9"),
+	PINCTRL_PIN(23, "gpio10"),
+	PINCTRL_PIN(24, "gpio11"),
+	PINCTRL_PIN(25, "gpio12"),
+	PINCTRL_PIN(26, "gpio13"),
+	PINCTRL_PIN(27, "gpio14"),
+	PINCTRL_PIN(28, "gpio15"),
+	PINCTRL_PIN(29, "gpio16"),
+	PINCTRL_PIN(30, "gpio17"),
+	PINCTRL_PIN(31, "gpio18"),
+	PINCTRL_PIN(32, "gpio19"),
+	PINCTRL_PIN(33, "gpio20"),
+	PINCTRL_PIN(34, "gpio21"),
+	PINCTRL_PIN(35, "gpio22"),
+	PINCTRL_PIN(36, "gpio23"),
+	PINCTRL_PIN(37, "gpio24"),
+	PINCTRL_PIN(38, "gpio25"),
+	PINCTRL_PIN(39, "gpio26"),
+	PINCTRL_PIN(40, "gpio27"),
+	PINCTRL_PIN(41, "gpio28"),
+	PINCTRL_PIN(42, "gpio29"),
+	PINCTRL_PIN(43, "gpio30"),
+	PINCTRL_PIN(44, "gpio31"),
+	PINCTRL_PIN(45, "gpio32"),
+	PINCTRL_PIN(46, "gpio33"),
+	PINCTRL_PIN(47, "gpio34"),
+	PINCTRL_PIN(48, "gpio35"),
+	PINCTRL_PIN(49, "gpio36"),
+	PINCTRL_PIN(50, "gpio37"),
+	PINCTRL_PIN(51, "gpio38"),
+	PINCTRL_PIN(52, "gpio39"),
+	PINCTRL_PIN(53, "gpio40"),
+	PINCTRL_PIN(54, "gpio41"),
+	PINCTRL_PIN(55, "gpio42"),
+	PINCTRL_PIN(56, "gpio43"),
+	PINCTRL_PIN(57, "gpio44"),
+	PINCTRL_PIN(58, "gpio45"),
+	PINCTRL_PIN(59, "gpio46"),
+	PINCTRL_PIN(61, "pcie_reset0"),
+	PINCTRL_PIN(62, "pcie_reset1"),
+	PINCTRL_PIN(63, "pcie_reset2"),
+};
+
+static const int pon_pins[] = { 49, 50, 51, 52, 53, 54 };
+static const int pon_tod_1pps_pins[] = { 46 };
+static const int gsw_tod_1pps_pins[] = { 46 };
+static const int sipo_pins[] = { 16, 17 };
+static const int sipo_rclk_pins[] = { 16, 17, 43 };
+static const int mdio_pins[] = { 14, 15 };
+static const int uart2_pins[] = { 48, 55 };
+static const int uart2_cts_rts_pins[] = { 46, 47 };
+static const int hsuart_pins[] = { 28, 29 };
+static const int hsuart_cts_rts_pins[] = { 26, 27 };
+static const int uart4_pins[] = { 38, 39 };
+static const int uart5_pins[] = { 18, 19 };
+static const int i2c0_pins[] = { 2, 3 };
+static const int i2c1_pins[] = { 14, 15 };
+static const int jtag_udi_pins[] = { 16, 17, 18, 19, 20 };
+static const int jtag_dfd_pins[] = { 16, 17, 18, 19, 20 };
+static const int i2s_pins[] = { 26, 27, 28, 29 };
+static const int pcm1_pins[] = { 22, 23, 24, 25 };
+static const int pcm2_pins[] = { 18, 19, 20, 21 };
+static const int spi_quad_pins[] = { 32, 33 };
+static const int spi_pins[] = { 4, 5, 6, 7 };
+static const int spi_cs1_pins[] = { 34 };
+static const int pcm_spi_pins[] = { 18, 19, 20, 21, 22, 23, 24, 25 };
+static const int pcm_spi_int_pins[] = { 14 };
+static const int pcm_spi_rst_pins[] = { 15 };
+static const int pcm_spi_cs1_pins[] = { 43 };
+static const int pcm_spi_cs2_pins[] = { 40 };
+static const int pcm_spi_cs2_p128_pins[] = { 40 };
+static const int pcm_spi_cs2_p156_pins[] = { 40 };
+static const int pcm_spi_cs3_pins[] = { 41 };
+static const int pcm_spi_cs4_pins[] = { 42 };
+static const int emmc_pins[] = { 4, 5, 6, 30, 31, 32, 33, 34, 35, 36, 37 };
+static const int pnand_pins[] = { 4, 5, 6, 7, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 };
+static const int gpio0_pins[] = { 13 };
+static const int gpio1_pins[] = { 14 };
+static const int gpio2_pins[] = { 15 };
+static const int gpio3_pins[] = { 16 };
+static const int gpio4_pins[] = { 17 };
+static const int gpio5_pins[] = { 18 };
+static const int gpio6_pins[] = { 19 };
+static const int gpio7_pins[] = { 20 };
+static const int gpio8_pins[] = { 21 };
+static const int gpio9_pins[] = { 22 };
+static const int gpio10_pins[] = { 23 };
+static const int gpio11_pins[] = { 24 };
+static const int gpio12_pins[] = { 25 };
+static const int gpio13_pins[] = { 26 };
+static const int gpio14_pins[] = { 27 };
+static const int gpio15_pins[] = { 28 };
+static const int gpio16_pins[] = { 29 };
+static const int gpio17_pins[] = { 30 };
+static const int gpio18_pins[] = { 31 };
+static const int gpio19_pins[] = { 32 };
+static const int gpio20_pins[] = { 33 };
+static const int gpio21_pins[] = { 34 };
+static const int gpio22_pins[] = { 35 };
+static const int gpio23_pins[] = { 36 };
+static const int gpio24_pins[] = { 37 };
+static const int gpio25_pins[] = { 38 };
+static const int gpio26_pins[] = { 39 };
+static const int gpio27_pins[] = { 40 };
+static const int gpio28_pins[] = { 41 };
+static const int gpio29_pins[] = { 42 };
+static const int gpio30_pins[] = { 43 };
+static const int gpio31_pins[] = { 44 };
+static const int gpio33_pins[] = { 46 };
+static const int gpio34_pins[] = { 47 };
+static const int gpio35_pins[] = { 48 };
+static const int gpio36_pins[] = { 49 };
+static const int gpio37_pins[] = { 50 };
+static const int gpio38_pins[] = { 51 };
+static const int gpio39_pins[] = { 52 };
+static const int gpio40_pins[] = { 53 };
+static const int gpio41_pins[] = { 54 };
+static const int gpio42_pins[] = { 55 };
+static const int gpio43_pins[] = { 56 };
+static const int gpio44_pins[] = { 57 };
+static const int gpio45_pins[] = { 58 };
+static const int gpio46_pins[] = { 59 };
+static const int pcie_reset0_pins[] = { 61 };
+static const int pcie_reset1_pins[] = { 62 };
+static const int pcie_reset2_pins[] = { 63 };
+
+static const struct pingroup airoha_pinctrl_groups[] = {
+	PINCTRL_PIN_GROUP(pon),
+	PINCTRL_PIN_GROUP(pon_tod_1pps),
+	PINCTRL_PIN_GROUP(gsw_tod_1pps),
+	PINCTRL_PIN_GROUP(sipo),
+	PINCTRL_PIN_GROUP(sipo_rclk),
+	PINCTRL_PIN_GROUP(mdio),
+	PINCTRL_PIN_GROUP(uart2),
+	PINCTRL_PIN_GROUP(uart2_cts_rts),
+	PINCTRL_PIN_GROUP(hsuart),
+	PINCTRL_PIN_GROUP(hsuart_cts_rts),
+	PINCTRL_PIN_GROUP(uart4),
+	PINCTRL_PIN_GROUP(uart5),
+	PINCTRL_PIN_GROUP(i2c0),
+	PINCTRL_PIN_GROUP(i2c1),
+	PINCTRL_PIN_GROUP(jtag_udi),
+	PINCTRL_PIN_GROUP(jtag_dfd),
+	PINCTRL_PIN_GROUP(i2s),
+	PINCTRL_PIN_GROUP(pcm1),
+	PINCTRL_PIN_GROUP(pcm2),
+	PINCTRL_PIN_GROUP(spi),
+	PINCTRL_PIN_GROUP(spi_quad),
+	PINCTRL_PIN_GROUP(spi_cs1),
+	PINCTRL_PIN_GROUP(pcm_spi),
+	PINCTRL_PIN_GROUP(pcm_spi_int),
+	PINCTRL_PIN_GROUP(pcm_spi_rst),
+	PINCTRL_PIN_GROUP(pcm_spi_cs1),
+	PINCTRL_PIN_GROUP(pcm_spi_cs2_p128),
+	PINCTRL_PIN_GROUP(pcm_spi_cs2_p156),
+	PINCTRL_PIN_GROUP(pcm_spi_cs2),
+	PINCTRL_PIN_GROUP(pcm_spi_cs3),
+	PINCTRL_PIN_GROUP(pcm_spi_cs4),
+	PINCTRL_PIN_GROUP(emmc),
+	PINCTRL_PIN_GROUP(pnand),
+	PINCTRL_PIN_GROUP(gpio0),
+	PINCTRL_PIN_GROUP(gpio1),
+	PINCTRL_PIN_GROUP(gpio2),
+	PINCTRL_PIN_GROUP(gpio3),
+	PINCTRL_PIN_GROUP(gpio4),
+	PINCTRL_PIN_GROUP(gpio5),
+	PINCTRL_PIN_GROUP(gpio6),
+	PINCTRL_PIN_GROUP(gpio7),
+	PINCTRL_PIN_GROUP(gpio8),
+	PINCTRL_PIN_GROUP(gpio9),
+	PINCTRL_PIN_GROUP(gpio10),
+	PINCTRL_PIN_GROUP(gpio11),
+	PINCTRL_PIN_GROUP(gpio12),
+	PINCTRL_PIN_GROUP(gpio13),
+	PINCTRL_PIN_GROUP(gpio14),
+	PINCTRL_PIN_GROUP(gpio15),
+	PINCTRL_PIN_GROUP(gpio16),
+	PINCTRL_PIN_GROUP(gpio17),
+	PINCTRL_PIN_GROUP(gpio18),
+	PINCTRL_PIN_GROUP(gpio19),
+	PINCTRL_PIN_GROUP(gpio20),
+	PINCTRL_PIN_GROUP(gpio21),
+	PINCTRL_PIN_GROUP(gpio22),
+	PINCTRL_PIN_GROUP(gpio23),
+	PINCTRL_PIN_GROUP(gpio24),
+	PINCTRL_PIN_GROUP(gpio25),
+	PINCTRL_PIN_GROUP(gpio26),
+	PINCTRL_PIN_GROUP(gpio27),
+	PINCTRL_PIN_GROUP(gpio28),
+	PINCTRL_PIN_GROUP(gpio29),
+	PINCTRL_PIN_GROUP(gpio30),
+	PINCTRL_PIN_GROUP(gpio31),
+	PINCTRL_PIN_GROUP(gpio33),
+	PINCTRL_PIN_GROUP(gpio34),
+	PINCTRL_PIN_GROUP(gpio35),
+	PINCTRL_PIN_GROUP(gpio36),
+	PINCTRL_PIN_GROUP(gpio37),
+	PINCTRL_PIN_GROUP(gpio38),
+	PINCTRL_PIN_GROUP(gpio39),
+	PINCTRL_PIN_GROUP(gpio40),
+	PINCTRL_PIN_GROUP(gpio41),
+	PINCTRL_PIN_GROUP(gpio42),
+	PINCTRL_PIN_GROUP(gpio43),
+	PINCTRL_PIN_GROUP(gpio44),
+	PINCTRL_PIN_GROUP(gpio45),
+	PINCTRL_PIN_GROUP(gpio46),
+	PINCTRL_PIN_GROUP(pcie_reset0),
+	PINCTRL_PIN_GROUP(pcie_reset1),
+	PINCTRL_PIN_GROUP(pcie_reset2),
+};
+
+static const char *const pon_groups[] = { "pon" };
+static const char *const tod_1pps_groups[] = { "pon_tod_1pps", "gsw_tod_1pps" };
+static const char *const sipo_groups[] = { "sipo", "sipo_rclk" };
+static const char *const mdio_groups[] = { "mdio" };
+static const char *const uart_groups[] = { "uart2", "uart2_cts_rts", "hsuart",
+					   "hsuart_cts_rts", "uart4",
+					   "uart5" };
+static const char *const i2c_groups[] = { "i2c1" };
+static const char *const jtag_groups[] = { "jtag_udi", "jtag_dfd" };
+static const char *const pcm_groups[] = { "pcm1", "pcm2" };
+static const char *const spi_groups[] = { "spi_quad", "spi_cs1" };
+static const char *const pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int",
+					      "pcm_spi_rst", "pcm_spi_cs1",
+					      "pcm_spi_cs2_p156",
+					      "pcm_spi_cs2_p128",
+					      "pcm_spi_cs3", "pcm_spi_cs4" };
+static const char *const i2s_groups[] = { "i2s" };
+static const char *const emmc_groups[] = { "emmc" };
+static const char *const pnand_groups[] = { "pnand" };
+static const char *const pcie_reset_groups[] = { "pcie_reset0", "pcie_reset1",
+						 "pcie_reset2" };
+static const char *const pwm_groups[] = { "gpio0", "gpio1",
+					  "gpio2", "gpio3",
+					  "gpio4", "gpio5",
+					  "gpio6", "gpio7",
+					  "gpio8", "gpio9",
+					  "gpio10", "gpio11",
+					  "gpio12", "gpio13",
+					  "gpio14", "gpio15",
+					  "gpio16", "gpio17",
+					  "gpio18", "gpio19",
+					  "gpio20", "gpio21",
+					  "gpio22", "gpio23",
+					  "gpio24", "gpio25",
+					  "gpio26", "gpio27",
+					  "gpio28", "gpio29",
+					  "gpio30", "gpio31",
+					  "gpio36", "gpio37",
+					  "gpio38", "gpio39",
+					  "gpio40", "gpio41",
+					  "gpio42", "gpio43",
+					  "gpio44", "gpio45",
+					  "gpio46", "gpio47" };
+static const char *const phy1_led0_groups[] = { "gpio33", "gpio34",
+						"gpio35", "gpio42" };
+static const char *const phy2_led0_groups[] = { "gpio33", "gpio34",
+						"gpio35", "gpio42" };
+static const char *const phy3_led0_groups[] = { "gpio33", "gpio34",
+						"gpio35", "gpio42" };
+static const char *const phy4_led0_groups[] = { "gpio33", "gpio34",
+						"gpio35", "gpio42" };
+static const char *const phy1_led1_groups[] = { "gpio43", "gpio44",
+						"gpio45", "gpio46" };
+static const char *const phy2_led1_groups[] = { "gpio43", "gpio44",
+						"gpio45", "gpio46" };
+static const char *const phy3_led1_groups[] = { "gpio43", "gpio44",
+						"gpio45", "gpio46" };
+static const char *const phy4_led1_groups[] = { "gpio43", "gpio44",
+						"gpio45", "gpio46" };
+
+static const struct airoha_pinctrl_func_group pon_func_group[] = {
+	{
+		.name = "pon",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_PON_MODE_MASK,
+			GPIO_PON_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group tod_1pps_func_group[] = {
+	{
+		.name = "pon_tod_1pps",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			PON_TOD_1PPS_MODE_MASK,
+			PON_TOD_1PPS_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gsw_tod_1pps",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GSW_TOD_1PPS_MODE_MASK,
+			GSW_TOD_1PPS_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group sipo_func_group[] = {
+	{
+		.name = "sipo",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK,
+			GPIO_SIPO_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "sipo_rclk",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK,
+			GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group mdio_func_group[] = {
+	{
+		.name = "mdio",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_SGMII_MDIO_MODE_MASK,
+			GPIO_SGMII_MDIO_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_MDC_IO_MASTER_MODE_MODE,
+			GPIO_MDC_IO_MASTER_MODE_MODE
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func_group uart_func_group[] = {
+	{
+		.name = "uart2",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_UART2_MODE_MASK,
+			GPIO_UART2_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "uart2_cts_rts",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_UART2_MODE_MASK | GPIO_UART2_CTS_RTS_MODE_MASK,
+			GPIO_UART2_MODE_MASK | GPIO_UART2_CTS_RTS_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "hsuart",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK,
+			GPIO_HSUART_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+	{
+		.name = "hsuart_cts_rts",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK,
+			GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "uart4",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_UART4_MODE_MASK,
+			GPIO_UART4_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "uart5",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_UART5_MODE_MASK,
+			GPIO_UART5_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group i2c_func_group[] = {
+	{
+		.name = "i2c1",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_2ND_I2C_MODE_MASK,
+			GPIO_2ND_I2C_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group jtag_func_group[] = {
+	{
+		.name = "jtag_udi",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_NPU_UART_EN,
+			JTAG_UDI_EN_MASK,
+			JTAG_UDI_EN_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "jtag_dfd",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_NPU_UART_EN,
+			JTAG_DFD_EN_MASK,
+			JTAG_DFD_EN_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group pcm_func_group[] = {
+	{
+		.name = "pcm1",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM1_MODE_MASK,
+			GPIO_PCM1_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcm2",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM2_MODE_MASK,
+			GPIO_PCM2_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group spi_func_group[] = {
+	{
+		.name = "spi_quad",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_SPI_QUAD_MODE_MASK,
+			GPIO_SPI_QUAD_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "spi_cs1",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_SPI_CS1_MODE_MASK,
+			GPIO_SPI_CS1_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "spi_cs2",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_SPI_CS2_MODE_MASK,
+			GPIO_SPI_CS2_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "spi_cs3",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_SPI_CS3_MODE_MASK,
+			GPIO_SPI_CS3_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "spi_cs4",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_SPI_CS4_MODE_MASK,
+			GPIO_SPI_CS4_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group pcm_spi_func_group[] = {
+	{
+		.name = "pcm_spi",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM_SPI_MODE_MASK,
+			GPIO_PCM_SPI_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcm_spi_int",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM_INT_MODE_MASK,
+			GPIO_PCM_INT_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcm_spi_rst",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM_RESET_MODE_MASK,
+			GPIO_PCM_RESET_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcm_spi_cs1",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM_SPI_CS1_MODE_MASK,
+			GPIO_PCM_SPI_CS1_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcm_spi_cs2_p128",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM_SPI_CS2_MODE_P128_MASK,
+			GPIO_PCM_SPI_CS2_MODE_P128_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcm_spi_cs2_p156",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM_SPI_CS2_MODE_P156_MASK,
+			GPIO_PCM_SPI_CS2_MODE_P156_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcm_spi_cs3",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM_SPI_CS3_MODE_MASK,
+			GPIO_PCM_SPI_CS3_MODE_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcm_spi_cs4",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_SPI_CS1_MODE,
+			GPIO_PCM_SPI_CS4_MODE_MASK,
+			GPIO_PCM_SPI_CS4_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group i2s_func_group[] = {
+	{
+		.name = "i2s",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_I2S_MODE_MASK,
+			GPIO_I2S_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group emmc_func_group[] = {
+	{
+		.name = "emmc",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_EMMC_MODE_MASK,
+			GPIO_EMMC_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group pnand_func_group[] = {
+	{
+		.name = "pnand",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_PARALLEL_NAND_MODE_MASK,
+			GPIO_PARALLEL_NAND_MODE_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group pcie_reset_func_group[] = {
+	{
+		.name = "pcie_reset0",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_PCIE_RESET0_MASK,
+			GPIO_PCIE_RESET0_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcie_reset1",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_PCIE_RESET1_MASK,
+			GPIO_PCIE_RESET1_MASK
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "pcie_reset2",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_PON_MODE,
+			GPIO_PCIE_RESET2_MASK,
+			GPIO_PCIE_RESET2_MASK
+		},
+		.regmap_size = 1,
+	},
+};
+
+/* PWM */
+static const struct airoha_pinctrl_func_group pwm_func_group[] = {
+	{
+		.name = "gpio0",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO0_FLASH_MODE_CFG,
+			GPIO0_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio1",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO1_FLASH_MODE_CFG,
+			GPIO1_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio2",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO2_FLASH_MODE_CFG,
+			GPIO2_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio3",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO3_FLASH_MODE_CFG,
+			GPIO3_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio4",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO4_FLASH_MODE_CFG,
+			GPIO4_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio5",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO5_FLASH_MODE_CFG,
+			GPIO5_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio6",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO6_FLASH_MODE_CFG,
+			GPIO6_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio7",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO7_FLASH_MODE_CFG,
+			GPIO7_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio8",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO8_FLASH_MODE_CFG,
+			GPIO8_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio9",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO9_FLASH_MODE_CFG,
+			GPIO9_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio10",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO10_FLASH_MODE_CFG,
+			GPIO10_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio11",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO11_FLASH_MODE_CFG,
+			GPIO11_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio12",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO12_FLASH_MODE_CFG,
+			GPIO12_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio13",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO13_FLASH_MODE_CFG,
+			GPIO13_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio14",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO14_FLASH_MODE_CFG,
+			GPIO14_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio15",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_MUX,
+			REG_GPIO_FLASH_MODE_CFG,
+			GPIO15_FLASH_MODE_CFG,
+			GPIO15_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio16",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO16_FLASH_MODE_CFG,
+			GPIO16_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio17",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO17_FLASH_MODE_CFG,
+			GPIO17_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio18",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO18_FLASH_MODE_CFG,
+			GPIO18_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio19",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO19_FLASH_MODE_CFG,
+			GPIO19_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio20",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO20_FLASH_MODE_CFG,
+			GPIO20_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio21",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO21_FLASH_MODE_CFG,
+			GPIO21_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio22",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO22_FLASH_MODE_CFG,
+			GPIO22_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio23",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO23_FLASH_MODE_CFG,
+			GPIO23_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio24",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO24_FLASH_MODE_CFG,
+			GPIO24_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio25",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO25_FLASH_MODE_CFG,
+			GPIO25_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio26",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO26_FLASH_MODE_CFG,
+			GPIO26_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio27",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO27_FLASH_MODE_CFG,
+			GPIO27_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio28",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO28_FLASH_MODE_CFG,
+			GPIO28_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio29",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO29_FLASH_MODE_CFG,
+			GPIO29_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio30",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO30_FLASH_MODE_CFG,
+			GPIO30_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio31",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO31_FLASH_MODE_CFG,
+			GPIO31_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio36",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO36_FLASH_MODE_CFG,
+			GPIO36_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio37",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO37_FLASH_MODE_CFG,
+			GPIO37_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio38",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO38_FLASH_MODE_CFG,
+			GPIO38_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio39",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO39_FLASH_MODE_CFG,
+			GPIO39_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio40",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO40_FLASH_MODE_CFG,
+			GPIO40_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio41",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO41_FLASH_MODE_CFG,
+			GPIO41_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio42",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO42_FLASH_MODE_CFG,
+			GPIO42_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio43",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO43_FLASH_MODE_CFG,
+			GPIO43_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio44",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO44_FLASH_MODE_CFG,
+			GPIO44_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio45",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO45_FLASH_MODE_CFG,
+			GPIO45_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio46",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO46_FLASH_MODE_CFG,
+			GPIO46_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	}, {
+		.name = "gpio47",
+		.regmap[0] = {
+			AIROHA_FUNC_PWM_EXT_MUX,
+			REG_GPIO_FLASH_MODE_CFG_EXT,
+			GPIO47_FLASH_MODE_CFG,
+			GPIO47_FLASH_MODE_CFG
+		},
+		.regmap_size = 1,
+	},
+};
+
+static const struct airoha_pinctrl_func_group phy1_led0_func_group[] = {
+	{
+		.name = "gpio33",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN0_LED0_MODE_MASK,
+			GPIO_LAN0_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN1_LED_MAPPING_MASK,
+			LAN1_PHY1_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio34",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN1_LED0_MODE_MASK,
+			GPIO_LAN1_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN2_LED_MAPPING_MASK,
+			LAN2_PHY1_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio35",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN2_LED0_MODE_MASK,
+			GPIO_LAN2_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN3_LED_MAPPING_MASK,
+			LAN3_PHY1_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio42",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN3_LED0_MODE_MASK,
+			GPIO_LAN3_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN4_LED_MAPPING_MASK,
+			LAN4_PHY1_LED_MAP
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func_group phy2_led0_func_group[] = {
+	{
+		.name = "gpio33",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN0_LED0_MODE_MASK,
+			GPIO_LAN0_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN1_LED_MAPPING_MASK,
+			LAN1_PHY2_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio34",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN1_LED0_MODE_MASK,
+			GPIO_LAN1_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN2_LED_MAPPING_MASK,
+			LAN2_PHY2_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio35",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN2_LED0_MODE_MASK,
+			GPIO_LAN2_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN3_LED_MAPPING_MASK,
+			LAN3_PHY2_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio42",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN3_LED0_MODE_MASK,
+			GPIO_LAN3_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN4_LED_MAPPING_MASK,
+			LAN4_PHY2_LED_MAP
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func_group phy3_led0_func_group[] = {
+	{
+		.name = "gpio33",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN0_LED0_MODE_MASK,
+			GPIO_LAN0_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN1_LED_MAPPING_MASK,
+			LAN1_PHY3_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio34",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN1_LED0_MODE_MASK,
+			GPIO_LAN1_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN2_LED_MAPPING_MASK,
+			LAN2_PHY3_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio35",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN2_LED0_MODE_MASK,
+			GPIO_LAN2_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN3_LED_MAPPING_MASK,
+			LAN3_PHY3_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio42",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN3_LED0_MODE_MASK,
+			GPIO_LAN3_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN4_LED_MAPPING_MASK,
+			LAN4_PHY3_LED_MAP
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func_group phy4_led0_func_group[] = {
+	{
+		.name = "gpio33",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN0_LED0_MODE_MASK,
+			GPIO_LAN0_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN1_LED_MAPPING_MASK,
+			LAN1_PHY4_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio34",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN1_LED0_MODE_MASK,
+			GPIO_LAN1_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN2_LED_MAPPING_MASK,
+			LAN2_PHY4_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio35",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN2_LED0_MODE_MASK,
+			GPIO_LAN2_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN3_LED_MAPPING_MASK,
+			LAN3_PHY4_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio42",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN3_LED0_MODE_MASK,
+			GPIO_LAN3_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED0_MAPPING,
+			LAN4_LED_MAPPING_MASK,
+			LAN4_PHY4_LED_MAP
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func_group phy1_led1_func_group[] = {
+	{
+		.name = "gpio43",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN0_LED1_MODE_MASK,
+			GPIO_LAN0_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN1_LED_MAPPING_MASK,
+			LAN1_PHY1_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio44",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN1_LED1_MODE_MASK,
+			GPIO_LAN1_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN2_LED_MAPPING_MASK,
+			LAN2_PHY1_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio45",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN2_LED1_MODE_MASK,
+			GPIO_LAN2_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN3_LED_MAPPING_MASK,
+			LAN3_PHY1_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio46",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN3_LED0_MODE_MASK,
+			GPIO_LAN3_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN4_LED_MAPPING_MASK,
+			LAN4_PHY1_LED_MAP
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func_group phy2_led1_func_group[] = {
+	{
+		.name = "gpio43",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN0_LED1_MODE_MASK,
+			GPIO_LAN0_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN1_LED_MAPPING_MASK,
+			LAN1_PHY2_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio44",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN1_LED1_MODE_MASK,
+			GPIO_LAN1_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN2_LED_MAPPING_MASK,
+			LAN2_PHY2_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio45",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN2_LED1_MODE_MASK,
+			GPIO_LAN2_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN3_LED_MAPPING_MASK,
+			LAN3_PHY2_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio46",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN3_LED0_MODE_MASK,
+			GPIO_LAN3_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN4_LED_MAPPING_MASK,
+			LAN4_PHY2_LED_MAP
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func_group phy3_led1_func_group[] = {
+	{
+		.name = "gpio43",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN0_LED1_MODE_MASK,
+			GPIO_LAN0_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN1_LED_MAPPING_MASK,
+			LAN1_PHY3_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio44",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN1_LED1_MODE_MASK,
+			GPIO_LAN1_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN2_LED_MAPPING_MASK,
+			LAN2_PHY3_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio45",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN2_LED1_MODE_MASK,
+			GPIO_LAN2_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN3_LED_MAPPING_MASK,
+			LAN3_PHY3_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio46",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN3_LED0_MODE_MASK,
+			GPIO_LAN3_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN4_LED_MAPPING_MASK,
+			LAN4_PHY3_LED_MAP
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func_group phy4_led1_func_group[] = {
+	{
+		.name = "gpio43",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN0_LED1_MODE_MASK,
+			GPIO_LAN0_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN1_LED_MAPPING_MASK,
+			LAN1_PHY4_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio44",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN1_LED1_MODE_MASK,
+			GPIO_LAN1_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN2_LED_MAPPING_MASK,
+			LAN2_PHY4_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio45",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN2_LED1_MODE_MASK,
+			GPIO_LAN2_LED1_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN3_LED_MAPPING_MASK,
+			LAN3_PHY4_LED_MAP
+		},
+		.regmap_size = 2,
+	}, {
+		.name = "gpio46",
+		.regmap[0] = {
+			AIROHA_FUNC_MUX,
+			REG_GPIO_2ND_I2C_MODE,
+			GPIO_LAN3_LED0_MODE_MASK,
+			GPIO_LAN3_LED0_MODE_MASK
+		},
+		.regmap[1] = {
+			AIROHA_FUNC_MUX,
+			REG_LAN_LED1_MAPPING,
+			LAN4_LED_MAPPING_MASK,
+			LAN4_PHY4_LED_MAP
+		},
+		.regmap_size = 2,
+	},
+};
+
+static const struct airoha_pinctrl_func airoha_pinctrl_funcs[] = {
+	PINCTRL_FUNC_DESC(pon),
+	PINCTRL_FUNC_DESC(tod_1pps),
+	PINCTRL_FUNC_DESC(sipo),
+	PINCTRL_FUNC_DESC(mdio),
+	PINCTRL_FUNC_DESC(uart),
+	PINCTRL_FUNC_DESC(i2c),
+	PINCTRL_FUNC_DESC(jtag),
+	PINCTRL_FUNC_DESC(pcm),
+	PINCTRL_FUNC_DESC(spi),
+	PINCTRL_FUNC_DESC(pcm_spi),
+	PINCTRL_FUNC_DESC(i2s),
+	PINCTRL_FUNC_DESC(emmc),
+	PINCTRL_FUNC_DESC(pnand),
+	PINCTRL_FUNC_DESC(pcie_reset),
+	PINCTRL_FUNC_DESC(pwm),
+	PINCTRL_FUNC_DESC(phy1_led0),
+	PINCTRL_FUNC_DESC(phy2_led0),
+	PINCTRL_FUNC_DESC(phy3_led0),
+	PINCTRL_FUNC_DESC(phy4_led0),
+	PINCTRL_FUNC_DESC(phy1_led1),
+	PINCTRL_FUNC_DESC(phy2_led1),
+	PINCTRL_FUNC_DESC(phy3_led1),
+	PINCTRL_FUNC_DESC(phy4_led1),
+};
+
+static const struct airoha_pinctrl_conf airoha_pinctrl_pullup_conf[] = {
+	PINCTRL_CONF_DESC(0, REG_I2C_SDA_PU, UART1_TXD_PU_MASK),
+	PINCTRL_CONF_DESC(1, REG_I2C_SDA_PU, UART1_RXD_PU_MASK),
+	PINCTRL_CONF_DESC(2, REG_I2C_SDA_PU, I2C_SDA_PU_MASK),
+	PINCTRL_CONF_DESC(3, REG_I2C_SDA_PU, I2C_SCL_PU_MASK),
+	PINCTRL_CONF_DESC(4, REG_I2C_SDA_PU, SPI_CS0_PU_MASK),
+	PINCTRL_CONF_DESC(5, REG_I2C_SDA_PU, SPI_CLK_PU_MASK),
+	PINCTRL_CONF_DESC(6, REG_I2C_SDA_PU, SPI_MOSI_PU_MASK),
+	PINCTRL_CONF_DESC(7, REG_I2C_SDA_PU, SPI_MISO_PU_MASK),
+	PINCTRL_CONF_DESC(13, REG_GPIO_L_PU, BIT(0)),
+	PINCTRL_CONF_DESC(14, REG_GPIO_L_PU, BIT(1)),
+	PINCTRL_CONF_DESC(15, REG_GPIO_L_PU, BIT(2)),
+	PINCTRL_CONF_DESC(16, REG_GPIO_L_PU, BIT(3)),
+	PINCTRL_CONF_DESC(17, REG_GPIO_L_PU, BIT(4)),
+	PINCTRL_CONF_DESC(18, REG_GPIO_L_PU, BIT(5)),
+	PINCTRL_CONF_DESC(19, REG_GPIO_L_PU, BIT(6)),
+	PINCTRL_CONF_DESC(20, REG_GPIO_L_PU, BIT(7)),
+	PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(8)),
+	PINCTRL_CONF_DESC(22, REG_GPIO_L_PU, BIT(9)),
+	PINCTRL_CONF_DESC(23, REG_GPIO_L_PU, BIT(10)),
+	PINCTRL_CONF_DESC(24, REG_GPIO_L_PU, BIT(11)),
+	PINCTRL_CONF_DESC(25, REG_GPIO_L_PU, BIT(12)),
+	PINCTRL_CONF_DESC(26, REG_GPIO_L_PU, BIT(13)),
+	PINCTRL_CONF_DESC(27, REG_GPIO_L_PU, BIT(14)),
+	PINCTRL_CONF_DESC(28, REG_GPIO_L_PU, BIT(15)),
+	PINCTRL_CONF_DESC(29, REG_GPIO_L_PU, BIT(16)),
+	PINCTRL_CONF_DESC(30, REG_GPIO_L_PU, BIT(17)),
+	PINCTRL_CONF_DESC(31, REG_GPIO_L_PU, BIT(18)),
+	PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(18)),
+	PINCTRL_CONF_DESC(33, REG_GPIO_L_PU, BIT(20)),
+	PINCTRL_CONF_DESC(34, REG_GPIO_L_PU, BIT(21)),
+	PINCTRL_CONF_DESC(35, REG_GPIO_L_PU, BIT(22)),
+	PINCTRL_CONF_DESC(36, REG_GPIO_L_PU, BIT(23)),
+	PINCTRL_CONF_DESC(37, REG_GPIO_L_PU, BIT(24)),
+	PINCTRL_CONF_DESC(38, REG_GPIO_L_PU, BIT(25)),
+	PINCTRL_CONF_DESC(39, REG_GPIO_L_PU, BIT(26)),
+	PINCTRL_CONF_DESC(40, REG_GPIO_L_PU, BIT(27)),
+	PINCTRL_CONF_DESC(41, REG_GPIO_L_PU, BIT(28)),
+	PINCTRL_CONF_DESC(42, REG_GPIO_L_PU, BIT(29)),
+	PINCTRL_CONF_DESC(43, REG_GPIO_L_PU, BIT(30)),
+	PINCTRL_CONF_DESC(44, REG_GPIO_L_PU, BIT(31)),
+	PINCTRL_CONF_DESC(45, REG_GPIO_H_PU, BIT(0)),
+	PINCTRL_CONF_DESC(46, REG_GPIO_H_PU, BIT(1)),
+	PINCTRL_CONF_DESC(47, REG_GPIO_H_PU, BIT(2)),
+	PINCTRL_CONF_DESC(48, REG_GPIO_H_PU, BIT(3)),
+	PINCTRL_CONF_DESC(49, REG_GPIO_H_PU, BIT(4)),
+	PINCTRL_CONF_DESC(50, REG_GPIO_H_PU, BIT(5)),
+	PINCTRL_CONF_DESC(51, REG_GPIO_H_PU, BIT(6)),
+	PINCTRL_CONF_DESC(52, REG_GPIO_H_PU, BIT(7)),
+	PINCTRL_CONF_DESC(53, REG_GPIO_H_PU, BIT(8)),
+	PINCTRL_CONF_DESC(54, REG_GPIO_H_PU, BIT(9)),
+	PINCTRL_CONF_DESC(55, REG_GPIO_H_PU, BIT(10)),
+	PINCTRL_CONF_DESC(56, REG_GPIO_H_PU, BIT(11)),
+	PINCTRL_CONF_DESC(57, REG_GPIO_H_PU, BIT(12)),
+	PINCTRL_CONF_DESC(58, REG_GPIO_H_PU, BIT(13)),
+	PINCTRL_CONF_DESC(59, REG_GPIO_H_PU, BIT(14)),
+	PINCTRL_CONF_DESC(61, REG_I2C_SDA_PU, PCIE0_RESET_PU_MASK),
+	PINCTRL_CONF_DESC(62, REG_I2C_SDA_PU, PCIE1_RESET_PU_MASK),
+	PINCTRL_CONF_DESC(63, REG_I2C_SDA_PU, PCIE2_RESET_PU_MASK),
+};
+
+static const struct airoha_pinctrl_conf airoha_pinctrl_pulldown_conf[] = {
+	PINCTRL_CONF_DESC(0, REG_I2C_SDA_PD, UART1_TXD_PD_MASK),
+	PINCTRL_CONF_DESC(1, REG_I2C_SDA_PD, UART1_RXD_PD_MASK),
+	PINCTRL_CONF_DESC(2, REG_I2C_SDA_PD, I2C_SDA_PD_MASK),
+	PINCTRL_CONF_DESC(3, REG_I2C_SDA_PD, I2C_SCL_PD_MASK),
+	PINCTRL_CONF_DESC(4, REG_I2C_SDA_PD, SPI_CS0_PD_MASK),
+	PINCTRL_CONF_DESC(5, REG_I2C_SDA_PD, SPI_CLK_PD_MASK),
+	PINCTRL_CONF_DESC(6, REG_I2C_SDA_PD, SPI_MOSI_PD_MASK),
+	PINCTRL_CONF_DESC(7, REG_I2C_SDA_PD, SPI_MISO_PD_MASK),
+	PINCTRL_CONF_DESC(13, REG_GPIO_L_PD, BIT(0)),
+	PINCTRL_CONF_DESC(14, REG_GPIO_L_PD, BIT(1)),
+	PINCTRL_CONF_DESC(15, REG_GPIO_L_PD, BIT(2)),
+	PINCTRL_CONF_DESC(16, REG_GPIO_L_PD, BIT(3)),
+	PINCTRL_CONF_DESC(17, REG_GPIO_L_PD, BIT(4)),
+	PINCTRL_CONF_DESC(18, REG_GPIO_L_PD, BIT(5)),
+	PINCTRL_CONF_DESC(19, REG_GPIO_L_PD, BIT(6)),
+	PINCTRL_CONF_DESC(20, REG_GPIO_L_PD, BIT(7)),
+	PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(8)),
+	PINCTRL_CONF_DESC(22, REG_GPIO_L_PD, BIT(9)),
+	PINCTRL_CONF_DESC(23, REG_GPIO_L_PD, BIT(10)),
+	PINCTRL_CONF_DESC(24, REG_GPIO_L_PD, BIT(11)),
+	PINCTRL_CONF_DESC(25, REG_GPIO_L_PD, BIT(12)),
+	PINCTRL_CONF_DESC(26, REG_GPIO_L_PD, BIT(13)),
+	PINCTRL_CONF_DESC(27, REG_GPIO_L_PD, BIT(14)),
+	PINCTRL_CONF_DESC(28, REG_GPIO_L_PD, BIT(15)),
+	PINCTRL_CONF_DESC(29, REG_GPIO_L_PD, BIT(16)),
+	PINCTRL_CONF_DESC(30, REG_GPIO_L_PD, BIT(17)),
+	PINCTRL_CONF_DESC(31, REG_GPIO_L_PD, BIT(18)),
+	PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(18)),
+	PINCTRL_CONF_DESC(33, REG_GPIO_L_PD, BIT(20)),
+	PINCTRL_CONF_DESC(34, REG_GPIO_L_PD, BIT(21)),
+	PINCTRL_CONF_DESC(35, REG_GPIO_L_PD, BIT(22)),
+	PINCTRL_CONF_DESC(36, REG_GPIO_L_PD, BIT(23)),
+	PINCTRL_CONF_DESC(37, REG_GPIO_L_PD, BIT(24)),
+	PINCTRL_CONF_DESC(38, REG_GPIO_L_PD, BIT(25)),
+	PINCTRL_CONF_DESC(39, REG_GPIO_L_PD, BIT(26)),
+	PINCTRL_CONF_DESC(40, REG_GPIO_L_PD, BIT(27)),
+	PINCTRL_CONF_DESC(41, REG_GPIO_L_PD, BIT(28)),
+	PINCTRL_CONF_DESC(42, REG_GPIO_L_PD, BIT(29)),
+	PINCTRL_CONF_DESC(43, REG_GPIO_L_PD, BIT(30)),
+	PINCTRL_CONF_DESC(44, REG_GPIO_L_PD, BIT(31)),
+	PINCTRL_CONF_DESC(45, REG_GPIO_H_PD, BIT(0)),
+	PINCTRL_CONF_DESC(46, REG_GPIO_H_PD, BIT(1)),
+	PINCTRL_CONF_DESC(47, REG_GPIO_H_PD, BIT(2)),
+	PINCTRL_CONF_DESC(48, REG_GPIO_H_PD, BIT(3)),
+	PINCTRL_CONF_DESC(49, REG_GPIO_H_PD, BIT(4)),
+	PINCTRL_CONF_DESC(50, REG_GPIO_H_PD, BIT(5)),
+	PINCTRL_CONF_DESC(51, REG_GPIO_H_PD, BIT(6)),
+	PINCTRL_CONF_DESC(52, REG_GPIO_H_PD, BIT(7)),
+	PINCTRL_CONF_DESC(53, REG_GPIO_H_PD, BIT(8)),
+	PINCTRL_CONF_DESC(54, REG_GPIO_H_PD, BIT(9)),
+	PINCTRL_CONF_DESC(55, REG_GPIO_H_PD, BIT(10)),
+	PINCTRL_CONF_DESC(56, REG_GPIO_H_PD, BIT(11)),
+	PINCTRL_CONF_DESC(57, REG_GPIO_H_PD, BIT(12)),
+	PINCTRL_CONF_DESC(58, REG_GPIO_H_PD, BIT(13)),
+	PINCTRL_CONF_DESC(59, REG_GPIO_H_PD, BIT(14)),
+	PINCTRL_CONF_DESC(61, REG_I2C_SDA_PD, PCIE0_RESET_PD_MASK),
+	PINCTRL_CONF_DESC(62, REG_I2C_SDA_PD, PCIE1_RESET_PD_MASK),
+	PINCTRL_CONF_DESC(63, REG_I2C_SDA_PD, PCIE2_RESET_PD_MASK),
+};
+
+static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e2_conf[] = {
+	PINCTRL_CONF_DESC(0, REG_I2C_SDA_E2, UART1_TXD_E2_MASK),
+	PINCTRL_CONF_DESC(1, REG_I2C_SDA_E2, UART1_RXD_E2_MASK),
+	PINCTRL_CONF_DESC(2, REG_I2C_SDA_E2, I2C_SDA_E2_MASK),
+	PINCTRL_CONF_DESC(3, REG_I2C_SDA_E2, I2C_SCL_E2_MASK),
+	PINCTRL_CONF_DESC(4, REG_I2C_SDA_E2, SPI_CS0_E2_MASK),
+	PINCTRL_CONF_DESC(5, REG_I2C_SDA_E2, SPI_CLK_E2_MASK),
+	PINCTRL_CONF_DESC(6, REG_I2C_SDA_E2, SPI_MOSI_E2_MASK),
+	PINCTRL_CONF_DESC(7, REG_I2C_SDA_E2, SPI_MISO_E2_MASK),
+	PINCTRL_CONF_DESC(13, REG_GPIO_L_E2, BIT(0)),
+	PINCTRL_CONF_DESC(14, REG_GPIO_L_E2, BIT(1)),
+	PINCTRL_CONF_DESC(15, REG_GPIO_L_E2, BIT(2)),
+	PINCTRL_CONF_DESC(16, REG_GPIO_L_E2, BIT(3)),
+	PINCTRL_CONF_DESC(17, REG_GPIO_L_E2, BIT(4)),
+	PINCTRL_CONF_DESC(18, REG_GPIO_L_E2, BIT(5)),
+	PINCTRL_CONF_DESC(19, REG_GPIO_L_E2, BIT(6)),
+	PINCTRL_CONF_DESC(20, REG_GPIO_L_E2, BIT(7)),
+	PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(8)),
+	PINCTRL_CONF_DESC(22, REG_GPIO_L_E2, BIT(9)),
+	PINCTRL_CONF_DESC(23, REG_GPIO_L_E2, BIT(10)),
+	PINCTRL_CONF_DESC(24, REG_GPIO_L_E2, BIT(11)),
+	PINCTRL_CONF_DESC(25, REG_GPIO_L_E2, BIT(12)),
+	PINCTRL_CONF_DESC(26, REG_GPIO_L_E2, BIT(13)),
+	PINCTRL_CONF_DESC(27, REG_GPIO_L_E2, BIT(14)),
+	PINCTRL_CONF_DESC(28, REG_GPIO_L_E2, BIT(15)),
+	PINCTRL_CONF_DESC(29, REG_GPIO_L_E2, BIT(16)),
+	PINCTRL_CONF_DESC(30, REG_GPIO_L_E2, BIT(17)),
+	PINCTRL_CONF_DESC(31, REG_GPIO_L_E2, BIT(18)),
+	PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(18)),
+	PINCTRL_CONF_DESC(33, REG_GPIO_L_E2, BIT(20)),
+	PINCTRL_CONF_DESC(34, REG_GPIO_L_E2, BIT(21)),
+	PINCTRL_CONF_DESC(35, REG_GPIO_L_E2, BIT(22)),
+	PINCTRL_CONF_DESC(36, REG_GPIO_L_E2, BIT(23)),
+	PINCTRL_CONF_DESC(37, REG_GPIO_L_E2, BIT(24)),
+	PINCTRL_CONF_DESC(38, REG_GPIO_L_E2, BIT(25)),
+	PINCTRL_CONF_DESC(39, REG_GPIO_L_E2, BIT(26)),
+	PINCTRL_CONF_DESC(40, REG_GPIO_L_E2, BIT(27)),
+	PINCTRL_CONF_DESC(41, REG_GPIO_L_E2, BIT(28)),
+	PINCTRL_CONF_DESC(42, REG_GPIO_L_E2, BIT(29)),
+	PINCTRL_CONF_DESC(43, REG_GPIO_L_E2, BIT(30)),
+	PINCTRL_CONF_DESC(44, REG_GPIO_L_E2, BIT(31)),
+	PINCTRL_CONF_DESC(45, REG_GPIO_H_E2, BIT(0)),
+	PINCTRL_CONF_DESC(46, REG_GPIO_H_E2, BIT(1)),
+	PINCTRL_CONF_DESC(47, REG_GPIO_H_E2, BIT(2)),
+	PINCTRL_CONF_DESC(48, REG_GPIO_H_E2, BIT(3)),
+	PINCTRL_CONF_DESC(49, REG_GPIO_H_E2, BIT(4)),
+	PINCTRL_CONF_DESC(50, REG_GPIO_H_E2, BIT(5)),
+	PINCTRL_CONF_DESC(51, REG_GPIO_H_E2, BIT(6)),
+	PINCTRL_CONF_DESC(52, REG_GPIO_H_E2, BIT(7)),
+	PINCTRL_CONF_DESC(53, REG_GPIO_H_E2, BIT(8)),
+	PINCTRL_CONF_DESC(54, REG_GPIO_H_E2, BIT(9)),
+	PINCTRL_CONF_DESC(55, REG_GPIO_H_E2, BIT(10)),
+	PINCTRL_CONF_DESC(56, REG_GPIO_H_E2, BIT(11)),
+	PINCTRL_CONF_DESC(57, REG_GPIO_H_E2, BIT(12)),
+	PINCTRL_CONF_DESC(58, REG_GPIO_H_E2, BIT(13)),
+	PINCTRL_CONF_DESC(59, REG_GPIO_H_E2, BIT(14)),
+	PINCTRL_CONF_DESC(61, REG_I2C_SDA_E2, PCIE0_RESET_E2_MASK),
+	PINCTRL_CONF_DESC(62, REG_I2C_SDA_E2, PCIE1_RESET_E2_MASK),
+	PINCTRL_CONF_DESC(63, REG_I2C_SDA_E2, PCIE2_RESET_E2_MASK),
+};
+
+static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e4_conf[] = {
+	PINCTRL_CONF_DESC(0, REG_I2C_SDA_E4, UART1_TXD_E4_MASK),
+	PINCTRL_CONF_DESC(1, REG_I2C_SDA_E4, UART1_RXD_E4_MASK),
+	PINCTRL_CONF_DESC(2, REG_I2C_SDA_E4, I2C_SDA_E4_MASK),
+	PINCTRL_CONF_DESC(3, REG_I2C_SDA_E4, I2C_SCL_E4_MASK),
+	PINCTRL_CONF_DESC(4, REG_I2C_SDA_E4, SPI_CS0_E4_MASK),
+	PINCTRL_CONF_DESC(5, REG_I2C_SDA_E4, SPI_CLK_E4_MASK),
+	PINCTRL_CONF_DESC(6, REG_I2C_SDA_E4, SPI_MOSI_E4_MASK),
+	PINCTRL_CONF_DESC(7, REG_I2C_SDA_E4, SPI_MISO_E4_MASK),
+	PINCTRL_CONF_DESC(13, REG_GPIO_L_E4, BIT(0)),
+	PINCTRL_CONF_DESC(14, REG_GPIO_L_E4, BIT(1)),
+	PINCTRL_CONF_DESC(15, REG_GPIO_L_E4, BIT(2)),
+	PINCTRL_CONF_DESC(16, REG_GPIO_L_E4, BIT(3)),
+	PINCTRL_CONF_DESC(17, REG_GPIO_L_E4, BIT(4)),
+	PINCTRL_CONF_DESC(18, REG_GPIO_L_E4, BIT(5)),
+	PINCTRL_CONF_DESC(19, REG_GPIO_L_E4, BIT(6)),
+	PINCTRL_CONF_DESC(20, REG_GPIO_L_E4, BIT(7)),
+	PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(8)),
+	PINCTRL_CONF_DESC(22, REG_GPIO_L_E4, BIT(9)),
+	PINCTRL_CONF_DESC(23, REG_GPIO_L_E4, BIT(10)),
+	PINCTRL_CONF_DESC(24, REG_GPIO_L_E4, BIT(11)),
+	PINCTRL_CONF_DESC(25, REG_GPIO_L_E4, BIT(12)),
+	PINCTRL_CONF_DESC(26, REG_GPIO_L_E4, BIT(13)),
+	PINCTRL_CONF_DESC(27, REG_GPIO_L_E4, BIT(14)),
+	PINCTRL_CONF_DESC(28, REG_GPIO_L_E4, BIT(15)),
+	PINCTRL_CONF_DESC(29, REG_GPIO_L_E4, BIT(16)),
+	PINCTRL_CONF_DESC(30, REG_GPIO_L_E4, BIT(17)),
+	PINCTRL_CONF_DESC(31, REG_GPIO_L_E4, BIT(18)),
+	PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(18)),
+	PINCTRL_CONF_DESC(33, REG_GPIO_L_E4, BIT(20)),
+	PINCTRL_CONF_DESC(34, REG_GPIO_L_E4, BIT(21)),
+	PINCTRL_CONF_DESC(35, REG_GPIO_L_E4, BIT(22)),
+	PINCTRL_CONF_DESC(36, REG_GPIO_L_E4, BIT(23)),
+	PINCTRL_CONF_DESC(37, REG_GPIO_L_E4, BIT(24)),
+	PINCTRL_CONF_DESC(38, REG_GPIO_L_E4, BIT(25)),
+	PINCTRL_CONF_DESC(39, REG_GPIO_L_E4, BIT(26)),
+	PINCTRL_CONF_DESC(40, REG_GPIO_L_E4, BIT(27)),
+	PINCTRL_CONF_DESC(41, REG_GPIO_L_E4, BIT(28)),
+	PINCTRL_CONF_DESC(42, REG_GPIO_L_E4, BIT(29)),
+	PINCTRL_CONF_DESC(43, REG_GPIO_L_E4, BIT(30)),
+	PINCTRL_CONF_DESC(44, REG_GPIO_L_E4, BIT(31)),
+	PINCTRL_CONF_DESC(45, REG_GPIO_H_E4, BIT(0)),
+	PINCTRL_CONF_DESC(46, REG_GPIO_H_E4, BIT(1)),
+	PINCTRL_CONF_DESC(47, REG_GPIO_H_E4, BIT(2)),
+	PINCTRL_CONF_DESC(48, REG_GPIO_H_E4, BIT(3)),
+	PINCTRL_CONF_DESC(49, REG_GPIO_H_E4, BIT(4)),
+	PINCTRL_CONF_DESC(50, REG_GPIO_H_E4, BIT(5)),
+	PINCTRL_CONF_DESC(51, REG_GPIO_H_E4, BIT(6)),
+	PINCTRL_CONF_DESC(52, REG_GPIO_H_E4, BIT(7)),
+	PINCTRL_CONF_DESC(53, REG_GPIO_H_E4, BIT(8)),
+	PINCTRL_CONF_DESC(54, REG_GPIO_H_E4, BIT(9)),
+	PINCTRL_CONF_DESC(55, REG_GPIO_H_E4, BIT(10)),
+	PINCTRL_CONF_DESC(56, REG_GPIO_H_E4, BIT(11)),
+	PINCTRL_CONF_DESC(57, REG_GPIO_H_E4, BIT(12)),
+	PINCTRL_CONF_DESC(58, REG_GPIO_H_E4, BIT(13)),
+	PINCTRL_CONF_DESC(59, REG_GPIO_H_E4, BIT(14)),
+	PINCTRL_CONF_DESC(61, REG_I2C_SDA_E4, PCIE0_RESET_E4_MASK),
+	PINCTRL_CONF_DESC(62, REG_I2C_SDA_E4, PCIE1_RESET_E4_MASK),
+	PINCTRL_CONF_DESC(63, REG_I2C_SDA_E4, PCIE2_RESET_E4_MASK),
+};
+
+static const struct airoha_pinctrl_conf airoha_pinctrl_pcie_rst_od_conf[] = {
+	PINCTRL_CONF_DESC(61, REG_PCIE_RESET_OD, PCIE0_RESET_OD_MASK),
+	PINCTRL_CONF_DESC(62, REG_PCIE_RESET_OD, PCIE1_RESET_OD_MASK),
+	PINCTRL_CONF_DESC(63, REG_PCIE_RESET_OD, PCIE2_RESET_OD_MASK),
+};
+
+static u32 airoha_pinctrl_rmw_unlock(void __iomem *addr, u32 mask, u32 val)
+{
+	val |= (readl(addr) & ~mask);
+	writel(val, addr);
+
+	return val;
+}
+
+#define airoha_pinctrl_set_unlock(addr, val)					\
+	airoha_pinctrl_rmw_unlock((addr), 0, (val))
+#define airoha_pinctrl_clear_unlock(addr, mask)					\
+	airoha_pinctrl_rmw_unlock((addr), (mask), (0))
+
+static u32 airoha_pinctrl_rmw(struct airoha_pinctrl *pinctrl,
+			      void __iomem *addr, u32 mask, u32 val)
+{
+	mutex_lock(&pinctrl->mutex);
+	val = airoha_pinctrl_rmw_unlock(addr, mask, val);
+	mutex_unlock(&pinctrl->mutex);
+
+	return val;
+}
+
+static void airoha_pinctrl_gpio_set_direction(struct airoha_pinctrl *pinctrl,
+					      unsigned int gpio, bool input)
+{
+	u32 mask, index;
+
+	/* set output enable */
+	mask = BIT(gpio % AIROHA_GPIO_BANK_SIZE);
+	index = gpio / AIROHA_GPIO_BANK_SIZE;
+	airoha_pinctrl_rmw(pinctrl, pinctrl->gpiochip.out[index],
+			   mask, !input ? mask : 0);
+
+	/* set gpio direction */
+	mask = BIT(2 * (gpio % AIROHA_REG_GPIOCTRL_NUM_GPIO));
+	index = gpio / AIROHA_REG_GPIOCTRL_NUM_GPIO;
+	airoha_pinctrl_rmw(pinctrl, pinctrl->gpiochip.dir[index],
+			   mask, !input ? mask : 0);
+}
+
+static void airoha_pinctrl_gpio_set_value(struct airoha_pinctrl *pinctrl,
+					  unsigned int gpio, bool value)
+{
+	u8 index = gpio / AIROHA_GPIO_BANK_SIZE;
+	u32 pin = gpio % AIROHA_GPIO_BANK_SIZE;
+
+	airoha_pinctrl_rmw(pinctrl, pinctrl->gpiochip.data[index],
+			   BIT(pin), value ? BIT(pin) : 0);
+}
+
+static int airoha_pinctrl_gpio_get_direction(struct airoha_pinctrl *pinctrl,
+					     unsigned int gpio)
+{
+	u32 val, mask = BIT(2 * (gpio % AIROHA_REG_GPIOCTRL_NUM_GPIO));
+	u8 index = gpio / AIROHA_REG_GPIOCTRL_NUM_GPIO;
+
+	val = (readl(pinctrl->gpiochip.dir[index]) & mask);
+
+	return val ? PIN_CONFIG_OUTPUT_ENABLE : PIN_CONFIG_INPUT_ENABLE;
+}
+
+static int airoha_pinmux_set_mux(struct pinctrl_dev *pctrl_dev,
+				 unsigned int selector,
+				 unsigned int group)
+{
+	struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	const struct airoha_pinctrl_func *func;
+	struct function_desc *desc;
+	struct group_desc *grp;
+	int i;
+
+	desc = pinmux_generic_get_function(pctrl_dev, selector);
+	if (!desc)
+		return -EINVAL;
+
+	grp = pinctrl_generic_get_group(pctrl_dev, group);
+	if (!grp)
+		return -EINVAL;
+
+	dev_dbg(pctrl_dev->dev, "enable function %s group %s\n",
+		desc->func.name, grp->grp.name);
+
+	func = desc->data;
+	for (i = 0; i < func->group_size; i++) {
+		const struct airoha_pinctrl_func_group *group;
+		int j;
+
+		group = &func->groups[i];
+		if (strcmp(group->name, grp->grp.name))
+			continue;
+
+		for (j = 0; j < group->regmap_size; j++) {
+			switch (group->regmap[j].mux) {
+			case AIROHA_FUNC_PWM_EXT_MUX:
+			case AIROHA_FUNC_PWM_MUX: {
+				void __iomem *addr;
+
+				addr = pinctrl->base + group->regmap[j].offset;
+				airoha_pinctrl_rmw(pinctrl, addr,
+						   group->regmap[j].mask,
+						   group->regmap[j].val);
+				break;
+			}
+			default:
+				regmap_update_bits(pinctrl->chip_scu,
+						   group->regmap[j].offset,
+						   group->regmap[j].mask,
+						   group->regmap[j].val);
+				break;
+			}
+		}
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int airoha_pinmux_gpio_set_direction(struct pinctrl_dev *pctrl_dev,
+					    struct pinctrl_gpio_range *range,
+					    unsigned int pin, bool input)
+{
+	struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	int gpio = pin - range->pin_base;
+
+	airoha_pinctrl_gpio_set_direction(pinctrl, gpio, input);
+
+	return 0;
+}
+
+static int airoha_pinctrl_get_gpio_from_pin(struct pinctrl_dev *pctrl_dev,
+					    int pin)
+{
+	struct pinctrl_gpio_range *range;
+	int gpio;
+
+	range = pinctrl_find_gpio_range_from_pin_nolock(pctrl_dev, pin);
+	if (!range)
+		return -EINVAL;
+
+	gpio = pin - range->pin_base;
+	if (gpio < 0)
+		return -EINVAL;
+
+	return gpio;
+}
+
+static const struct airoha_pinctrl_reg *
+airoha_pinctrl_get_conf_reg(const struct airoha_pinctrl_conf *conf,
+			    int conf_size, int pin)
+{
+	int i;
+
+	for (i = 0; i < conf_size; i++) {
+		if (conf[i].pin == pin)
+			return &conf[i].reg;
+	}
+
+	return NULL;
+}
+
+static int airoha_pinctrl_get_conf(struct airoha_pinctrl *pinctrl,
+				   const struct airoha_pinctrl_conf *conf,
+				   int conf_size, int pin, u32 *val)
+{
+	const struct airoha_pinctrl_reg *reg;
+
+	reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin);
+	if (!reg)
+		return -EINVAL;
+
+	if (regmap_read(pinctrl->chip_scu, reg->offset, val))
+		return -EINVAL;
+
+	*val = (*val & reg->mask) >> __bf_shf(reg->mask);
+
+	return 0;
+}
+
+static int airoha_pinctrl_set_conf(struct airoha_pinctrl *pinctrl,
+				   const struct airoha_pinctrl_conf *conf,
+				   int conf_size, int pin, u32 val)
+{
+	const struct airoha_pinctrl_reg *reg = NULL;
+
+	reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin);
+	if (!reg)
+		return -EINVAL;
+
+
+	if (regmap_update_bits(pinctrl->chip_scu, reg->offset, reg->mask,
+			       val << __bf_shf(reg->mask)))
+		return -EINVAL;
+
+	return 0;
+}
+
+#define airoha_pinctrl_get_pullup_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pullup_conf,		\
+				ARRAY_SIZE(airoha_pinctrl_pullup_conf),		\
+				(pin), (val))
+#define airoha_pinctrl_get_pulldown_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pulldown_conf,	\
+				ARRAY_SIZE(airoha_pinctrl_pulldown_conf),	\
+				(pin), (val))
+#define airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e2_conf,	\
+				ARRAY_SIZE(airoha_pinctrl_drive_e2_conf),	\
+				(pin), (val))
+#define airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e4_conf,	\
+				ARRAY_SIZE(airoha_pinctrl_drive_e4_conf),	\
+				(pin), (val))
+#define airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf,	\
+				ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf),	\
+				(pin), (val))
+#define airoha_pinctrl_set_pullup_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pullup_conf,		\
+				ARRAY_SIZE(airoha_pinctrl_pullup_conf),		\
+				(pin), (val))
+#define airoha_pinctrl_set_pulldown_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pulldown_conf,	\
+				ARRAY_SIZE(airoha_pinctrl_pulldown_conf),	\
+				(pin), (val))
+#define airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e2_conf,	\
+				ARRAY_SIZE(airoha_pinctrl_drive_e2_conf),	\
+				(pin), (val))
+#define airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e4_conf,	\
+				ARRAY_SIZE(airoha_pinctrl_drive_e4_conf),	\
+				(pin), (val))
+#define airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, val)			\
+	airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf,	\
+				ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf),	\
+				(pin), (val))
+
+static int airoha_pinconf_get(struct pinctrl_dev *pctrl_dev,
+			      unsigned int pin, unsigned long *config)
+{
+	struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	u32 arg;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+	case PIN_CONFIG_BIAS_DISABLE:
+	case PIN_CONFIG_BIAS_PULL_UP: {
+		u32 pull_up, pull_down;
+
+		if (airoha_pinctrl_get_pullup_conf(pinctrl, pin, &pull_up) ||
+		    airoha_pinctrl_get_pulldown_conf(pinctrl, pin, &pull_down))
+			return -EINVAL;
+
+		if (param == PIN_CONFIG_BIAS_PULL_UP &&
+		    !(pull_up && !pull_down))
+			return -EINVAL;
+		else if (param == PIN_CONFIG_BIAS_PULL_DOWN &&
+			 !(pull_down && !pull_up))
+			return -EINVAL;
+		else if (pull_up || pull_down)
+			return -EINVAL;
+
+		arg = 1;
+		break;
+	}
+	case PIN_CONFIG_DRIVE_STRENGTH: {
+		u32 e2, e4;
+
+		if (airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, &e2) ||
+		    airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, &e4))
+			return -EINVAL;
+
+		arg = e4 << 1 | e2;
+		break;
+	}
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		if (airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, &arg))
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_OUTPUT_ENABLE:
+	case PIN_CONFIG_INPUT_ENABLE: {
+		int gpio = airoha_pinctrl_get_gpio_from_pin(pctrl_dev, pin);
+
+		if (gpio < 0)
+			return gpio;
+
+		arg = airoha_pinctrl_gpio_get_direction(pinctrl, gpio);
+		if (arg != param)
+			return -EINVAL;
+
+		arg = 1;
+		break;
+	}
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int airoha_pinconf_set(struct pinctrl_dev *pctrl_dev,
+			      unsigned int pin, unsigned long *configs,
+			      unsigned int num_configs)
+{
+	struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	int i;
+
+	for (i = 0; i < num_configs; i++) {
+		u32 param = pinconf_to_config_param(configs[i]);
+		u32 arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_BIAS_DISABLE:
+			airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0);
+			airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0);
+			break;
+		case PIN_CONFIG_BIAS_PULL_UP:
+			airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0);
+			airoha_pinctrl_set_pullup_conf(pinctrl, pin, 1);
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 1);
+			airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0);
+			break;
+		case PIN_CONFIG_DRIVE_STRENGTH: {
+			u32 e2 = 0, e4 = 0;
+
+			switch (arg) {
+			case MTK_DRIVE_2mA:
+				break;
+			case MTK_DRIVE_4mA:
+				e2 = 1;
+				break;
+			case MTK_DRIVE_6mA:
+				e4 = 1;
+				break;
+			case MTK_DRIVE_8mA:
+				e2 = 1;
+				e4 = 1;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, e2);
+			airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, e4);
+			break;
+		}
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+			airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, !!arg);
+			break;
+		case PIN_CONFIG_OUTPUT_ENABLE:
+		case PIN_CONFIG_INPUT_ENABLE:
+		case PIN_CONFIG_OUTPUT: {
+			int gpio = airoha_pinctrl_get_gpio_from_pin(pctrl_dev, pin);
+			bool input = param == PIN_CONFIG_INPUT_ENABLE;
+
+			if (gpio < 0)
+				return gpio;
+
+			airoha_pinctrl_gpio_set_direction(pinctrl, gpio, input);
+			if (param == PIN_CONFIG_OUTPUT)
+				airoha_pinctrl_gpio_set_value(pinctrl, gpio, !!arg);
+			break;
+		}
+		default:
+			return -EOPNOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
+static int airoha_pinconf_group_get(struct pinctrl_dev *pctrl_dev,
+				    unsigned int group, unsigned long *config)
+{
+	u32 cur_config = 0;
+	int i;
+
+	for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) {
+		if (airoha_pinconf_get(pctrl_dev,
+				       airoha_pinctrl_groups[group].pins[i],
+				       config))
+			return -EOPNOTSUPP;
+
+		if (i && cur_config != *config)
+			return -EOPNOTSUPP;
+
+		cur_config = *config;
+	}
+
+	return 0;
+}
+
+static int airoha_pinconf_group_set(struct pinctrl_dev *pctrl_dev,
+				    unsigned int group, unsigned long *configs,
+				    unsigned int num_configs)
+{
+	int i;
+
+	for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) {
+		int err;
+
+		err = airoha_pinconf_set(pctrl_dev,
+					 airoha_pinctrl_groups[group].pins[i],
+					 configs, num_configs);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static const struct pinconf_ops airoha_confops = {
+	.is_generic = true,
+	.pin_config_get = airoha_pinconf_get,
+	.pin_config_set = airoha_pinconf_set,
+	.pin_config_group_get = airoha_pinconf_group_get,
+	.pin_config_group_set = airoha_pinconf_group_set,
+	.pin_config_config_dbg_show = pinconf_generic_dump_config,
+};
+
+static const struct pinctrl_ops airoha_pctlops = {
+	.get_groups_count = pinctrl_generic_get_group_count,
+	.get_group_name = pinctrl_generic_get_group_name,
+	.get_group_pins = pinctrl_generic_get_group_pins,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+	.dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static const struct pinmux_ops airoha_pmxops = {
+	.get_functions_count = pinmux_generic_get_function_count,
+	.get_function_name = pinmux_generic_get_function_name,
+	.get_function_groups = pinmux_generic_get_function_groups,
+	.gpio_set_direction = airoha_pinmux_gpio_set_direction,
+	.set_mux = airoha_pinmux_set_mux,
+	.strict = true,
+};
+
+static struct pinctrl_desc airoha_pinctrl_desc = {
+	.name = KBUILD_MODNAME,
+	.owner = THIS_MODULE,
+	.pctlops = &airoha_pctlops,
+	.pmxops = &airoha_pmxops,
+	.confops = &airoha_confops,
+	.pins = airoha_pinctrl_pins,
+	.npins = ARRAY_SIZE(airoha_pinctrl_pins),
+};
+
+static void airoha_pinctrl_gpio_set(struct gpio_chip *chip, unsigned int gpio,
+				    int value)
+{
+	struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip);
+
+	airoha_pinctrl_gpio_set_value(pinctrl, gpio, value);
+}
+
+static int airoha_pinctrl_gpio_get(struct gpio_chip *chip, unsigned int gpio)
+{
+	struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip);
+	u8 index = gpio / AIROHA_GPIO_BANK_SIZE;
+	u32 pin = gpio % AIROHA_GPIO_BANK_SIZE;
+
+	return !!(readl(pinctrl->gpiochip.data[index]) & BIT(pin));
+}
+
+static int airoha_pinctrl_gpio_direction_output(struct gpio_chip *chip,
+						unsigned int gpio, int value)
+{
+	int err;
+
+	err = pinctrl_gpio_direction_output(chip, gpio);
+	if (err)
+		return err;
+
+	airoha_pinctrl_gpio_set(chip, gpio, value);
+
+	return 0;
+}
+
+static void airoha_pinctrl_gpio_irq_unmask(struct irq_data *data)
+{
+	u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_GPIO;
+	u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_GPIO;
+	u32 mask = GENMASK(2 * offset + 1, 2 * offset);
+	struct airoha_pinctrl_gpiochip *gpiochip;
+	u32 val = BIT(2 * offset);
+	unsigned long flags;
+
+	gpiochip = irq_data_get_irq_chip_data(data);
+	if (WARN_ON_ONCE(data->hwirq >= ARRAY_SIZE(gpiochip->irq_type)))
+		return;
+
+	spin_lock_irqsave(&gpiochip->lock, flags);
+
+	switch (gpiochip->irq_type[data->hwirq] & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_LEVEL_LOW:
+		val = val << 1;
+		fallthrough;
+	case IRQ_TYPE_LEVEL_HIGH:
+		airoha_pinctrl_rmw_unlock(gpiochip->level[index], mask, val);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		val = val << 1;
+		fallthrough;
+	case IRQ_TYPE_EDGE_RISING:
+		airoha_pinctrl_rmw_unlock(gpiochip->edge[index], mask, val);
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		airoha_pinctrl_set_unlock(gpiochip->edge[index], mask);
+		break;
+	default:
+		break;
+	}
+
+	spin_unlock_irqrestore(&gpiochip->lock, flags);
+}
+
+static void airoha_pinctrl_gpio_irq_mask(struct irq_data *data)
+{
+	u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_GPIO;
+	u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_GPIO;
+	u32 mask = GENMASK(2 * offset + 1, 2 * offset);
+	struct airoha_pinctrl_gpiochip *gpiochip;
+	unsigned long flags;
+
+	gpiochip = irq_data_get_irq_chip_data(data);
+
+	spin_lock_irqsave(&gpiochip->lock, flags);
+
+	airoha_pinctrl_clear_unlock(gpiochip->edge[index], mask);
+	airoha_pinctrl_clear_unlock(gpiochip->level[index], mask);
+
+	spin_unlock_irqrestore(&gpiochip->lock, flags);
+}
+
+static int airoha_pinctrl_gpio_irq_type(struct irq_data *data,
+					unsigned int type)
+{
+	struct airoha_pinctrl_gpiochip *gpiochip;
+	unsigned long flags;
+
+	gpiochip = irq_data_get_irq_chip_data(data);
+	if (data->hwirq >= ARRAY_SIZE(gpiochip->irq_type))
+		return -EINVAL;
+
+	spin_lock_irqsave(&gpiochip->lock, flags);
+
+	if (type == IRQ_TYPE_PROBE) {
+		if (gpiochip->irq_type[data->hwirq])
+			goto unlock;
+
+		type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+	}
+	gpiochip->irq_type[data->hwirq] = type & IRQ_TYPE_SENSE_MASK;
+unlock:
+	spin_unlock_irqrestore(&gpiochip->lock, flags);
+
+	return 0;
+}
+
+static irqreturn_t airoha_pinctrl_gpio_irq_handler(int irq, void *data)
+{
+	struct airoha_pinctrl_gpiochip *gpiochip;
+	bool handled = false;
+	int i;
+
+	gpiochip = container_of(data, struct airoha_pinctrl_gpiochip, chip);
+	for (i = 0; i < ARRAY_SIZE(gpiochip->status); i++) {
+		unsigned long status = readl(gpiochip->status[i]);
+		struct gpio_irq_chip *girq = &gpiochip->chip.irq;
+		int irq;
+
+		for_each_set_bit(irq, &status, AIROHA_GPIO_BANK_SIZE) {
+			u32 offset = irq + i * AIROHA_GPIO_BANK_SIZE;
+
+			generic_handle_irq(irq_find_mapping(girq->domain,
+							    offset));
+			writel(BIT(irq), gpiochip->status[i]);
+		}
+		handled |= !!status;
+	}
+
+	return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int airoha_pinctrl_add_gpiochip(struct airoha_pinctrl *pinctrl,
+				       struct platform_device *pdev)
+{
+	struct gpio_chip *chip = &pinctrl->gpiochip.chip;
+	struct gpio_irq_chip *girq = &chip->irq;
+	struct device *dev = &pdev->dev;
+	int irq, err;
+
+	pinctrl->gpiochip.data[0] = pinctrl->base + REG_GPIO_DATA;
+	pinctrl->gpiochip.data[1] = pinctrl->base + REG_GPIO_DATA1;
+	pinctrl->gpiochip.dir[0] = pinctrl->base + REG_GPIO_CTRL;
+	pinctrl->gpiochip.dir[1] = pinctrl->base + REG_GPIO_CTRL1;
+	pinctrl->gpiochip.dir[2] = pinctrl->base + REG_GPIO_CTRL2;
+	pinctrl->gpiochip.dir[3] = pinctrl->base + REG_GPIO_CTRL3;
+	pinctrl->gpiochip.out[0] = pinctrl->base + REG_GPIO_OE;
+	pinctrl->gpiochip.out[1] = pinctrl->base + REG_GPIO_OE1;
+
+	chip->parent = dev;
+	chip->label = dev_name(dev);
+	chip->request = gpiochip_generic_request;
+	chip->free = gpiochip_generic_free;
+	chip->direction_input = pinctrl_gpio_direction_input;
+	chip->direction_output = airoha_pinctrl_gpio_direction_output;
+	chip->set = airoha_pinctrl_gpio_set;
+	chip->get = airoha_pinctrl_gpio_get;
+	chip->base = -1;
+	chip->ngpio = AIROHA_NUM_GPIOS;
+
+	if (!of_property_read_bool(dev->of_node, "interrupt-controller"))
+		goto out;
+
+	pinctrl->gpiochip.status[0] = pinctrl->base + REG_GPIO_INT;
+	pinctrl->gpiochip.status[1] = pinctrl->base + REG_GPIO_INT1;
+	pinctrl->gpiochip.level[0] = pinctrl->base + REG_GPIO_INT_LEVEL;
+	pinctrl->gpiochip.level[1] = pinctrl->base + REG_GPIO_INT_LEVEL1;
+	pinctrl->gpiochip.level[2] = pinctrl->base + REG_GPIO_INT_LEVEL2;
+	pinctrl->gpiochip.level[3] = pinctrl->base + REG_GPIO_INT_LEVEL3;
+	pinctrl->gpiochip.edge[0] = pinctrl->base + REG_GPIO_INT_EDGE;
+	pinctrl->gpiochip.edge[1] = pinctrl->base + REG_GPIO_INT_EDGE1;
+	pinctrl->gpiochip.edge[2] = pinctrl->base + REG_GPIO_INT_EDGE2;
+	pinctrl->gpiochip.edge[3] = pinctrl->base + REG_GPIO_INT_EDGE3;
+
+	spin_lock_init(&pinctrl->gpiochip.lock);
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	err = devm_request_irq(dev, irq, airoha_pinctrl_gpio_irq_handler,
+			       IRQF_SHARED, dev_name(dev), chip);
+	if (err) {
+		dev_err(dev, "error requesting irq %d: %d\n", irq, err);
+		return err;
+	}
+
+	girq->chip = devm_kzalloc(dev, sizeof(*girq->chip), GFP_KERNEL);
+	if (!girq->chip)
+		return -ENOMEM;
+
+	girq->chip->name = dev_name(dev);
+	girq->chip->irq_unmask = airoha_pinctrl_gpio_irq_unmask;
+	girq->chip->irq_mask = airoha_pinctrl_gpio_irq_mask;
+	girq->chip->irq_mask_ack = airoha_pinctrl_gpio_irq_mask;
+	girq->chip->irq_set_type = airoha_pinctrl_gpio_irq_type;
+	girq->chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_IMMUTABLE;
+	girq->default_type = IRQ_TYPE_NONE;
+	girq->handler = handle_simple_irq;
+out:
+	return devm_gpiochip_add_data(dev, chip, pinctrl);
+}
+
+static int airoha_pinctrl_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct airoha_pinctrl *pinctrl;
+	struct airoha_mfd *mfd;
+	struct regmap *map;
+	int err, i;
+
+	/* Assign parent MFD of_node to dev */
+	dev->of_node = of_node_get(dev->parent->of_node);
+	mfd = dev_get_drvdata(dev->parent);
+
+	pinctrl = devm_kzalloc(dev, sizeof(*pinctrl), GFP_KERNEL);
+	if (!pinctrl)
+		return -ENOMEM;
+
+	mutex_init(&pinctrl->mutex);
+	pinctrl->base = mfd->base;
+
+	map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu");
+	if (IS_ERR(map))
+		return PTR_ERR(map);
+
+	pinctrl->chip_scu = map;
+
+	err = devm_pinctrl_register_and_init(dev, &airoha_pinctrl_desc,
+					     pinctrl, &pinctrl->ctrl);
+	if (err)
+		return err;
+
+	/* build pin groups */
+	for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_groups); i++) {
+		const struct pingroup *grp = &airoha_pinctrl_groups[i];
+
+		err = pinctrl_generic_add_group(pinctrl->ctrl, grp->name,
+						grp->pins, grp->npins,
+						(void *)grp);
+		if (err < 0) {
+			dev_err(&pdev->dev, "Failed to register group %s\n",
+				grp->name);
+			return err;
+		}
+	}
+
+	/* build functions */
+	for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_funcs); i++) {
+		const struct airoha_pinctrl_func *func;
+
+		func = &airoha_pinctrl_funcs[i];
+		err = pinmux_generic_add_function(pinctrl->ctrl,
+						  func->desc.func.name,
+						  func->desc.func.groups,
+						  func->desc.func.ngroups,
+						  (void *)func);
+		if (err < 0) {
+			dev_err(dev, "Failed to register function %s\n",
+				func->desc.func.name);
+			return err;
+		}
+	}
+
+	err = pinctrl_enable(pinctrl->ctrl);
+	if (err)
+		return err;
+
+	/* build gpio-chip */
+	return airoha_pinctrl_add_gpiochip(pinctrl, pdev);
+}
+
+static struct platform_driver airoha_pinctrl_driver = {
+	.probe = airoha_pinctrl_probe,
+	.driver = {
+		.name = KBUILD_MODNAME,
+	},
+};
+module_platform_driver(airoha_pinctrl_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
+MODULE_AUTHOR("Benjamin Larsson <benjamin.larsson@genexis.eu>");
+MODULE_AUTHOR("Markus Gothe <markus.gothe@genexis.eu>");
+MODULE_DESCRIPTION("Pinctrl driver for Airoha SoC");

-- 
2.46.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-08-31 14:27 [PATCH v3 0/5] Add mfd, pinctrl and pwm support to EN7581 SoC Lorenzo Bianconi
                   ` (3 preceding siblings ...)
  2024-08-31 14:27 ` [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC Lorenzo Bianconi
@ 2024-08-31 14:27 ` Lorenzo Bianconi
  2024-09-03 10:46   ` Uwe Kleine-König
  4 siblings, 1 reply; 24+ messages in thread
From: Lorenzo Bianconi @ 2024-08-31 14:27 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: linux-mediatek, linux-gpio, devicetree, linux-arm-kernel,
	upstream, benjamin.larsson, ansuelsmth, linux-pwm

From: Benjamin Larsson <benjamin.larsson@genexis.eu>

Introduce driver for PWM module available on EN7581 SoC.

Signed-off-by: Benjamin Larsson <benjamin.larsson@genexis.eu>
Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 drivers/pwm/Kconfig      |  10 ++
 drivers/pwm/Makefile     |   1 +
 drivers/pwm/pwm-airoha.c | 435 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 446 insertions(+)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 3e53838990f5..0a78bda0707d 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -47,6 +47,16 @@ config PWM_AB8500
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-ab8500.
 
+config PWM_AIROHA
+	tristate "Airoha PWM support"
+	depends on ARCH_AIROHA || COMPILE_TEST
+	depends on OF
+	help
+	  Generic PWM framework driver for Airoha SoC.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-airoha.
+
 config PWM_APPLE
 	tristate "Apple SoC PWM support"
 	depends on ARCH_APPLE || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 0be4f3e6dd43..7ee61822d88d 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_AB8500)	+= pwm-ab8500.o
+obj-$(CONFIG_PWM_AIROHA)	+= pwm-airoha.o
 obj-$(CONFIG_PWM_APPLE)		+= pwm-apple.o
 obj-$(CONFIG_PWM_ATMEL)		+= pwm-atmel.o
 obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
diff --git a/drivers/pwm/pwm-airoha.c b/drivers/pwm/pwm-airoha.c
new file mode 100644
index 000000000000..54dc12d20da4
--- /dev/null
+++ b/drivers/pwm/pwm-airoha.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2022 Markus Gothe <markus.gothe@genexis.eu>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/airoha-en7581-mfd.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/gpio.h>
+#include <linux/bitops.h>
+#include <asm/div64.h>
+
+#define REG_SGPIO_CFG			0x0024
+#define REG_FLASH_CFG			0x0038
+#define REG_CYCLE_CFG			0x0098
+
+#define REG_SGPIO_LED_DATE		0x0000
+#define SGPIO_LED_SHIFT_FLAG		BIT(31)
+#define SGPIO_LED_DATA			GENMASK(16, 0)
+
+#define REG_SGPIO_CLK_DIVR		0x0004
+#define REG_SGPIO_CLK_DLY		0x0008
+
+#define REG_SIPO_FLASH_MODE_CFG		0x000c
+#define SERIAL_GPIO_FLASH_MODE		BIT(1)
+#define SERIAL_GPIO_MODE		BIT(0)
+
+#define REG_GPIO_FLASH_PRD_SET(_n)	(0x0004 + ((_n) << 2))
+#define GPIO_FLASH_PRD_MASK(_n)		GENMASK(15 + ((_n) << 4), ((_n) << 4))
+
+#define REG_GPIO_FLASH_MAP(_n)		(0x0014 + ((_n) << 2))
+#define GPIO_FLASH_SETID_MASK(_n)	GENMASK(2 + ((_n) << 2), ((_n) << 2))
+#define GPIO_FLASH_EN(_n)		BIT(3 + ((_n) << 2))
+
+#define REG_SIPO_FLASH_MAP(_n)		(0x001c + ((_n) << 2))
+
+#define REG_CYCLE_CFG_VALUE(_n)		(0x0000 + ((_n) << 2))
+#define WAVE_GEN_CYCLE_MASK(_n)		GENMASK(7 + ((_n) << 3), ((_n) << 3))
+
+struct airoha_pwm {
+	void __iomem *sgpio_cfg;
+	void __iomem *flash_cfg;
+	void __iomem *cycle_cfg;
+
+	struct device_node *np;
+	u64 initialized;
+
+	struct {
+		/* Bitmask of PWM channels using this bucket */
+		u64 used;
+		u64 period_ns;
+		u64 duty_ns;
+		enum pwm_polarity polarity;
+	} bucket[8];
+};
+
+/*
+ * The first 16 GPIO pins, GPIO0-GPIO15, are mapped into 16 PWM channels, 0-15.
+ * The SIPO GPIO pins are 16 pins which are mapped into 17 PWM channels, 16-32.
+ * However, we've only got 8 concurrent waveform generators and can therefore
+ * only use up to 8 different combinations of duty cycle and period at a time.
+ */
+#define PWM_NUM_GPIO	16
+#define PWM_NUM_SIPO	17
+
+/* The PWM hardware supports periods between 4 ms and 1 s */
+#define PERIOD_MIN_NS	4000000
+#define PERIOD_MAX_NS	1000000000
+/* It is represented internally as 1/250 s between 1 and 250 */
+#define PERIOD_MIN	1
+#define PERIOD_MAX	250
+/* Duty cycle is relative with 255 corresponding to 100% */
+#define DUTY_FULL	255
+
+static u32 airoha_pwm_rmw(struct airoha_pwm *pc, void __iomem *addr,
+			  u32 mask, u32 val)
+{
+	val |= (readl(addr) & ~mask);
+	writel(val, addr);
+
+	return val;
+}
+
+#define airoha_pwm_sgpio_rmw(pc, offset, mask, val)				\
+	airoha_pwm_rmw((pc), (pc)->sgpio_cfg + (offset), (mask), (val))
+#define airoha_pwm_flash_rmw(pc, offset, mask, val)				\
+	airoha_pwm_rmw((pc), (pc)->flash_cfg + (offset), (mask), (val))
+#define airoha_pwm_cycle_rmw(pc, offset, mask, val)				\
+	airoha_pwm_rmw((pc), (pc)->cycle_cfg + (offset), (mask), (val))
+
+#define airoha_pwm_sgpio_set(pc, offset, val)					\
+	airoha_pwm_sgpio_rmw((pc), (offset), 0, (val))
+#define airoha_pwm_sgpio_clear(pc, offset, mask)				\
+	airoha_pwm_sgpio_rmw((pc), (offset), (mask), 0)
+#define airoha_pwm_flash_set(pc, offset, val)					\
+	airoha_pwm_flash_rmw((pc), (offset), 0, (val))
+#define airoha_pwm_flash_clear(pc, offset, mask)				\
+	airoha_pwm_flash_rmw((pc), (offset), (mask), 0)
+
+static int airoha_pwm_get_waveform(struct airoha_pwm *pc, u64 duty_ns,
+				   u64 period_ns)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) {
+		if (!pc->bucket[i].used)
+			continue;
+
+		if (duty_ns == pc->bucket[i].duty_ns &&
+		    period_ns == pc->bucket[i].period_ns)
+			return i;
+
+		/*
+		 * Unlike duty cycle zero, which can be handled by
+		 * disabling PWM, a generator is needed for full duty
+		 * cycle but it can be reused regardless of period
+		 */
+		if (duty_ns == DUTY_FULL && pc->bucket[i].duty_ns == DUTY_FULL)
+			return i;
+	}
+
+	return -1;
+}
+
+static void airoha_pwm_free_waveform(struct airoha_pwm *pc, unsigned int hwpwm)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pc->bucket); i++)
+		pc->bucket[i].used &= ~BIT_ULL(hwpwm);
+}
+
+static int airoha_pwm_consume_waveform(struct airoha_pwm *pc,
+				       u64 duty_ns, u64 period_ns,
+				       enum pwm_polarity polarity,
+				       unsigned int hwpwm)
+{
+	int id = airoha_pwm_get_waveform(pc, duty_ns, period_ns);
+
+	if (id < 0) {
+		int i;
+
+		/* find an unused waveform generator */
+		for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) {
+			if (!(pc->bucket[i].used & ~BIT_ULL(hwpwm))) {
+				id = i;
+				break;
+			}
+		}
+	}
+
+	if (id >= 0) {
+		airoha_pwm_free_waveform(pc, hwpwm);
+		pc->bucket[id].used |= BIT_ULL(hwpwm);
+		pc->bucket[id].period_ns = period_ns;
+		pc->bucket[id].duty_ns = duty_ns;
+		pc->bucket[id].polarity = polarity;
+	}
+
+	return id;
+}
+
+static int airoha_pwm_sipo_init(struct airoha_pwm *pc)
+{
+	u32 clk_divr_val = 3, sipo_clock_delay = 1;
+	u32 val, sipo_clock_divisor = 32;
+
+	if (!(pc->initialized >> PWM_NUM_GPIO))
+		return 0;
+
+	/* Select the right shift register chip */
+	if (of_property_read_bool(pc->np, "hc74595"))
+		airoha_pwm_sgpio_set(pc, REG_SIPO_FLASH_MODE_CFG,
+				     SERIAL_GPIO_MODE);
+	else
+		airoha_pwm_sgpio_clear(pc, REG_SIPO_FLASH_MODE_CFG,
+				       SERIAL_GPIO_MODE);
+
+	if (!of_property_read_u32(pc->np, "sipo-clock-divisor",
+				  &sipo_clock_divisor)) {
+		switch (sipo_clock_divisor) {
+		case 4:
+			clk_divr_val = 0;
+			break;
+		case 8:
+			clk_divr_val = 1;
+			break;
+		case 16:
+			clk_divr_val = 2;
+			break;
+		case 32:
+			clk_divr_val = 3;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	/* Configure shift register timings */
+	writel(clk_divr_val, pc->sgpio_cfg + REG_SGPIO_CLK_DIVR);
+
+	of_property_read_u32(pc->np, "sipo-clock-delay", &sipo_clock_delay);
+	if (sipo_clock_delay < 1 || sipo_clock_delay > sipo_clock_divisor / 2)
+		return -EINVAL;
+
+	/*
+	 * The actual delay is sclkdly + 1 so subtract 1 from
+	 * sipo-clock-delay to calculate the register value
+	 */
+	sipo_clock_delay--;
+	writel(sipo_clock_delay, pc->sgpio_cfg + REG_SGPIO_CLK_DLY);
+
+	/*
+	 * It it necessary to after muxing explicitly shift out all
+	 * zeroes to initialize the shift register before enabling PWM
+	 * mode because in PWM mode SIPO will not start shifting until
+	 * it needs to output a non-zero value (bit 31 of led_data
+	 * indicates shifting in progress and it must return to zero
+	 * before led_data can be written or PWM mode can be set)
+	 */
+	if (readl_poll_timeout(pc->sgpio_cfg + REG_SGPIO_LED_DATE, val,
+			       !(val & SGPIO_LED_SHIFT_FLAG), 10,
+			       200 * USEC_PER_MSEC))
+		return -ETIMEDOUT;
+
+	airoha_pwm_sgpio_clear(pc, REG_SGPIO_LED_DATE, SGPIO_LED_DATA);
+	if (readl_poll_timeout(pc->sgpio_cfg + REG_SGPIO_LED_DATE, val,
+			       !(val & SGPIO_LED_SHIFT_FLAG), 10,
+			       200 * USEC_PER_MSEC))
+		return -ETIMEDOUT;
+
+	/* Set SIPO in PWM mode */
+	airoha_pwm_sgpio_set(pc, REG_SIPO_FLASH_MODE_CFG,
+			     SERIAL_GPIO_FLASH_MODE);
+
+	return 0;
+}
+
+static void airoha_pwm_config_waveform(struct airoha_pwm *pc, int index,
+				       u64 duty_ns, u64 period_ns,
+				       enum pwm_polarity polarity)
+{
+	u32 period, duty, mask, val;
+
+	duty = clamp_val(div64_u64(DUTY_FULL * duty_ns, period_ns), 0,
+			 DUTY_FULL);
+	if (polarity == PWM_POLARITY_INVERSED)
+		duty = DUTY_FULL - duty;
+
+	period = clamp_val(div64_u64(25 * period_ns, 100000000), PERIOD_MIN,
+			   PERIOD_MAX);
+
+	/* Configure frequency divisor */
+	mask = WAVE_GEN_CYCLE_MASK(index % 4);
+	val = (period << __ffs(mask)) & mask;
+	airoha_pwm_cycle_rmw(pc, REG_CYCLE_CFG_VALUE(index / 4), mask, val);
+
+	/* Configure duty cycle */
+	duty = ((DUTY_FULL - duty) << 8) | duty;
+	mask = GPIO_FLASH_PRD_MASK(index % 2);
+	val = (duty << __ffs(mask)) & mask;
+	airoha_pwm_flash_rmw(pc, REG_GPIO_FLASH_PRD_SET(index / 2), mask, val);
+}
+
+static void airoha_pwm_config_flash_map(struct airoha_pwm *pc,
+					unsigned int hwpwm, int index)
+{
+	u32 addr, mask, val;
+
+	if (hwpwm < PWM_NUM_GPIO) {
+		addr = REG_GPIO_FLASH_MAP(hwpwm / 8);
+	} else {
+		addr = REG_SIPO_FLASH_MAP(hwpwm / 8);
+		hwpwm -= PWM_NUM_GPIO;
+	}
+
+	if (index < 0) {
+		/*
+		 * Change of waveform takes effect immediately but
+		 * disabling has some delay so to prevent glitching
+		 * only the enable bit is touched when disabling
+		 */
+		airoha_pwm_flash_clear(pc, addr, GPIO_FLASH_EN(hwpwm % 8));
+		return;
+	}
+
+	mask = GPIO_FLASH_SETID_MASK(hwpwm % 8);
+	val = ((index & 7) << __ffs(mask)) & mask;
+	airoha_pwm_flash_rmw(pc, addr, mask, val);
+	airoha_pwm_flash_set(pc, addr, GPIO_FLASH_EN(hwpwm % 8));
+}
+
+static int airoha_pwm_config(struct airoha_pwm *pc, struct pwm_device *pwm,
+			     u64 duty_ns, u64 period_ns,
+			     enum pwm_polarity polarity)
+{
+	int index = -1;
+
+	index = airoha_pwm_consume_waveform(pc, duty_ns, period_ns, polarity,
+					    pwm->hwpwm);
+	if (index < 0)
+		return -EBUSY;
+
+	if (!(pc->initialized & BIT_ULL(pwm->hwpwm)) &&
+	    pwm->hwpwm >= PWM_NUM_GPIO)
+		airoha_pwm_sipo_init(pc);
+
+	if (index >= 0) {
+		airoha_pwm_config_waveform(pc, index, duty_ns, period_ns,
+					   polarity);
+		airoha_pwm_config_flash_map(pc, pwm->hwpwm, index);
+	} else {
+		airoha_pwm_config_flash_map(pc, pwm->hwpwm, index);
+		airoha_pwm_free_waveform(pc, pwm->hwpwm);
+	}
+
+	pc->initialized |= BIT_ULL(pwm->hwpwm);
+
+	return 0;
+}
+
+static void airoha_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct airoha_pwm *pc = pwmchip_get_drvdata(chip);
+
+	/* Disable PWM and release the waveform */
+	airoha_pwm_config_flash_map(pc, pwm->hwpwm, -1);
+	airoha_pwm_free_waveform(pc, pwm->hwpwm);
+
+	pc->initialized &= ~BIT_ULL(pwm->hwpwm);
+	if (!(pc->initialized >> PWM_NUM_GPIO))
+		airoha_pwm_sgpio_clear(pc, REG_SIPO_FLASH_MODE_CFG,
+				       SERIAL_GPIO_FLASH_MODE);
+
+	/*
+	 * Clear the state to force re-initialization the next time
+	 * this PWM channel is used since we cannot retain state in
+	 * hardware due to limited number of waveform generators
+	 */
+	memset(&pwm->state, 0, sizeof(pwm->state));
+}
+
+static int airoha_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			    const struct pwm_state *state)
+{
+	struct airoha_pwm *pc = pwmchip_get_drvdata(chip);
+	u64 duty = state->enabled ? state->duty_cycle : 0;
+
+	if (!state->enabled) {
+		airoha_pwm_free(chip, pwm);
+		return 0;
+	}
+
+	if (state->period < PERIOD_MIN_NS || state->period > PERIOD_MAX_NS)
+		return -EINVAL;
+
+	return airoha_pwm_config(pc, pwm, duty, state->period,
+				 state->polarity);
+}
+
+static int airoha_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+				struct pwm_state *state)
+{
+	struct airoha_pwm *pc = pwmchip_get_drvdata(chip);
+	int i;
+
+	/* find hwpwm in waveform generator bucket */
+	for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) {
+		if (pc->bucket[i].used & BIT_ULL(pwm->hwpwm)) {
+			state->enabled = pc->initialized & BIT_ULL(pwm->hwpwm);
+			state->polarity = pc->bucket[i].polarity;
+			state->period = pc->bucket[i].period_ns;
+			state->duty_cycle = pc->bucket[i].duty_ns;
+			break;
+		}
+	}
+
+	if (i == ARRAY_SIZE(pc->bucket))
+		state->enabled = false;
+
+	return 0;
+}
+
+static const struct pwm_ops airoha_pwm_ops = {
+	.get_state = airoha_pwm_get_state,
+	.apply = airoha_pwm_apply,
+	.free = airoha_pwm_free,
+};
+
+static int airoha_pwm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct airoha_mfd *mfd;
+	struct airoha_pwm *pc;
+	struct pwm_chip *chip;
+
+	/* Assign parent MFD of_node to dev */
+	dev->of_node = of_node_get(dev->parent->of_node);
+	mfd = dev_get_drvdata(dev->parent);
+
+	chip = devm_pwmchip_alloc(dev, PWM_NUM_GPIO + PWM_NUM_SIPO,
+				  sizeof(*pc));
+	if (IS_ERR(chip))
+		return PTR_ERR(chip);
+
+	pc = pwmchip_get_drvdata(chip);
+	pc->np = dev->of_node;
+	pc->sgpio_cfg = mfd->base + REG_SGPIO_CFG;
+	pc->flash_cfg = mfd->base + REG_FLASH_CFG;
+	pc->cycle_cfg = mfd->base + REG_CYCLE_CFG;
+
+	chip->ops = &airoha_pwm_ops;
+	chip->of_xlate = of_pwm_xlate_with_flags;
+
+	return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+static struct platform_driver airoha_pwm_driver = {
+	.driver = {
+		.name = "airoha-pwm",
+	},
+	.probe = airoha_pwm_probe,
+};
+module_platform_driver(airoha_pwm_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
+MODULE_AUTHOR("Markus Gothe <markus.gothe@genexis.eu>");
+MODULE_AUTHOR("Benjamin Larsson <benjamin.larsson@genexis.eu>");
+MODULE_DESCRIPTION("Airoha EN7581 PWM driver");
+MODULE_LICENSE("GPL");

-- 
2.46.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC
  2024-08-31 14:27 ` [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC Lorenzo Bianconi
@ 2024-08-31 17:35   ` kernel test robot
  2024-09-01 12:46   ` kernel test robot
  2024-09-01 13:28   ` kernel test robot
  2 siblings, 0 replies; 24+ messages in thread
From: kernel test robot @ 2024-08-31 17:35 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: oe-kbuild-all, linux-mediatek, linux-gpio, devicetree,
	linux-arm-kernel, upstream, benjamin.larsson, ansuelsmth,
	linux-pwm

Hi Lorenzo,

kernel test robot noticed the following build warnings:

[auto build test WARNING on defaf1a2113a22b00dfa1abc0fd2014820eaf065]

url:    https://github.com/intel-lab-lkp/linux/commits/Lorenzo-Bianconi/dt-bindings-arm-airoha-Add-the-chip-scu-node-for-EN7581-SoC/20240831-222958
base:   defaf1a2113a22b00dfa1abc0fd2014820eaf065
patch link:    https://lore.kernel.org/r/20240831-en7581-pinctrl-v3-4-98eebfb4da66%40kernel.org
patch subject: [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC
reproduce: (https://download.01.org/0day-ci/archive/20240901/202409010106.kqFZrO3g-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202409010106.kqFZrO3g-lkp@intel.com/

All warnings (new ones prefixed by >>):

   Warning: Documentation/devicetree/bindings/power/wakeup-source.txt references a file that doesn't exist: Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
   Warning: Documentation/devicetree/bindings/regulator/siliconmitus,sm5703-regulator.yaml references a file that doesn't exist: Documentation/devicetree/bindings/mfd/siliconmitus,sm5703.yaml
   Warning: Documentation/hwmon/g762.rst references a file that doesn't exist: Documentation/devicetree/bindings/hwmon/g762.txt
   Warning: MAINTAINERS references a file that doesn't exist: Documentation/devicetree/bindings/reserved-memory/qcom
   Warning: MAINTAINERS references a file that doesn't exist: Documentation/devicetree/bindings/display/exynos/
>> Warning: MAINTAINERS references a file that doesn't exist: Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml
   Warning: MAINTAINERS references a file that doesn't exist: Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt
   Using alabaster theme

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC
  2024-08-31 14:27 ` [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC Lorenzo Bianconi
  2024-08-31 17:35   ` kernel test robot
@ 2024-09-01 12:46   ` kernel test robot
  2024-09-01 13:28   ` kernel test robot
  2 siblings, 0 replies; 24+ messages in thread
From: kernel test robot @ 2024-09-01 12:46 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: oe-kbuild-all, linux-mediatek, linux-gpio, devicetree,
	linux-arm-kernel, upstream, benjamin.larsson, ansuelsmth,
	linux-pwm

Hi Lorenzo,

kernel test robot noticed the following build errors:

[auto build test ERROR on defaf1a2113a22b00dfa1abc0fd2014820eaf065]

url:    https://github.com/intel-lab-lkp/linux/commits/Lorenzo-Bianconi/dt-bindings-arm-airoha-Add-the-chip-scu-node-for-EN7581-SoC/20240831-222958
base:   defaf1a2113a22b00dfa1abc0fd2014820eaf065
patch link:    https://lore.kernel.org/r/20240831-en7581-pinctrl-v3-4-98eebfb4da66%40kernel.org
patch subject: [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC
config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20240901/202409012015.isPMHBiL-lkp@intel.com/config)
compiler: sh4-linux-gcc (GCC) 14.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240901/202409012015.isPMHBiL-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202409012015.isPMHBiL-lkp@intel.com/

All errors (new ones prefixed by >>, old ones prefixed by <<):

WARNING: modpost: missing MODULE_DESCRIPTION() in kernel/locking/test-ww_mutex.o
WARNING: modpost: missing MODULE_DESCRIPTION() in lib/test_objpool.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/usb/serial/ch341.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/usb/serial/usb_debug.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/usb/serial/mxuport.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/usb/serial/navman.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/usb/serial/qcaux.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/usb/serial/usb-serial-simple.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/usb/serial/symbolserial.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/devfreq/governor_simpleondemand.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/devfreq/governor_performance.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/devfreq/governor_powersave.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/devfreq/governor_userspace.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/fsi/fsi-core.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/fsi/fsi-master-hub.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/fsi/fsi-master-aspeed.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/fsi/fsi-master-gpio.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/fsi/fsi-master-ast-cf.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/fsi/fsi-scom.o
>> ERROR: modpost: "__ffsdi2" [drivers/pinctrl/mediatek/pinctrl-airoha.ko] undefined!
ERROR: modpost: "__delay" [drivers/net/mdio/mdio-cavium.ko] undefined!
ERROR: modpost: "devm_of_clk_add_hw_provider" [drivers/media/i2c/tc358746.ko] undefined!
ERROR: modpost: "devm_clk_hw_register" [drivers/media/i2c/tc358746.ko] undefined!
ERROR: modpost: "of_clk_hw_simple_get" [drivers/media/i2c/tc358746.ko] undefined!

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC
  2024-08-31 14:27 ` [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC Lorenzo Bianconi
  2024-08-31 17:35   ` kernel test robot
  2024-09-01 12:46   ` kernel test robot
@ 2024-09-01 13:28   ` kernel test robot
  2 siblings, 0 replies; 24+ messages in thread
From: kernel test robot @ 2024-09-01 13:28 UTC (permalink / raw)
  To: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, Uwe Kleine-König
  Cc: oe-kbuild-all, linux-mediatek, linux-gpio, devicetree,
	linux-arm-kernel, upstream, benjamin.larsson, ansuelsmth,
	linux-pwm

Hi Lorenzo,

kernel test robot noticed the following build errors:

[auto build test ERROR on defaf1a2113a22b00dfa1abc0fd2014820eaf065]

url:    https://github.com/intel-lab-lkp/linux/commits/Lorenzo-Bianconi/dt-bindings-arm-airoha-Add-the-chip-scu-node-for-EN7581-SoC/20240831-222958
base:   defaf1a2113a22b00dfa1abc0fd2014820eaf065
patch link:    https://lore.kernel.org/r/20240831-en7581-pinctrl-v3-4-98eebfb4da66%40kernel.org
patch subject: [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20240901/202409012045.91oDLtEE-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 14.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240901/202409012045.91oDLtEE-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202409012045.91oDLtEE-lkp@intel.com/

All errors (new ones prefixed by >>):

   m68k-linux-ld: drivers/pinctrl/mediatek/pinctrl-airoha.o: in function `airoha_pinconf_get':
   pinctrl-airoha.c:(.text+0xa7a): undefined reference to `__ffsdi2'
>> m68k-linux-ld: pinctrl-airoha.c:(.text+0xb5e): undefined reference to `__ffsdi2'
   m68k-linux-ld: pinctrl-airoha.c:(.text+0xc2a): undefined reference to `__ffsdi2'
   m68k-linux-ld: drivers/pinctrl/mediatek/pinctrl-airoha.o: in function `airoha_pinconf_set':
   pinctrl-airoha.c:(.text+0xf84): undefined reference to `__ffsdi2'
   m68k-linux-ld: pinctrl-airoha.c:(.text+0xfda): undefined reference to `__ffsdi2'
   m68k-linux-ld: drivers/pinctrl/mediatek/pinctrl-airoha.o:pinctrl-airoha.c:(.text+0x10b0): more undefined references to `__ffsdi2' follow

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-08-31 14:27 ` [PATCH v3 5/5] pwm: " Lorenzo Bianconi
@ 2024-09-03 10:46   ` Uwe Kleine-König
  2024-09-03 11:58     ` Benjamin Larsson
  0 siblings, 1 reply; 24+ messages in thread
From: Uwe Kleine-König @ 2024-09-03 10:46 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
	Lee Jones, linux-mediatek, linux-gpio, devicetree,
	linux-arm-kernel, upstream, benjamin.larsson, ansuelsmth,
	linux-pwm

[-- Attachment #1: Type: text/plain, Size: 18952 bytes --]

Hello,

On Sat, Aug 31, 2024 at 04:27:50PM +0200, Lorenzo Bianconi wrote:
> From: Benjamin Larsson <benjamin.larsson@genexis.eu>
> 
> Introduce driver for PWM module available on EN7581 SoC.
> 
> Signed-off-by: Benjamin Larsson <benjamin.larsson@genexis.eu>
> Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  drivers/pwm/Kconfig      |  10 ++
>  drivers/pwm/Makefile     |   1 +
>  drivers/pwm/pwm-airoha.c | 435 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 446 insertions(+)
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 3e53838990f5..0a78bda0707d 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -47,6 +47,16 @@ config PWM_AB8500
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called pwm-ab8500.
>  
> +config PWM_AIROHA
> +	tristate "Airoha PWM support"
> +	depends on ARCH_AIROHA || COMPILE_TEST
> +	depends on OF
> +	help
> +	  Generic PWM framework driver for Airoha SoC.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called pwm-airoha.
> +
>  config PWM_APPLE
>  	tristate "Apple SoC PWM support"
>  	depends on ARCH_APPLE || COMPILE_TEST
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 0be4f3e6dd43..7ee61822d88d 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -1,6 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0
>  obj-$(CONFIG_PWM)		+= core.o
>  obj-$(CONFIG_PWM_AB8500)	+= pwm-ab8500.o
> +obj-$(CONFIG_PWM_AIROHA)	+= pwm-airoha.o
>  obj-$(CONFIG_PWM_APPLE)		+= pwm-apple.o
>  obj-$(CONFIG_PWM_ATMEL)		+= pwm-atmel.o
>  obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
> diff --git a/drivers/pwm/pwm-airoha.c b/drivers/pwm/pwm-airoha.c
> new file mode 100644
> index 000000000000..54dc12d20da4
> --- /dev/null
> +++ b/drivers/pwm/pwm-airoha.c
> @@ -0,0 +1,435 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2022 Markus Gothe <markus.gothe@genexis.eu>
> + */

Would you please add a "Limitations" paragraph here covering the
following questions:

 - How does the hardware behave on changes of configuration (does it
   complete the currently running period? Are there any glitches?)
 - How does the hardware behave on disabling?

Please stick to the format used in several other drivers such that 

	sed -rn '/Limitations:/,/\*\/?$/p' drivers/pwm/*.c

emits the informations.

> +
> +#include <linux/bitfield.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/mfd/airoha-en7581-mfd.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/gpio.h>
> +#include <linux/bitops.h>
> +#include <asm/div64.h>
> +
> +#define REG_SGPIO_CFG			0x0024
> +#define REG_FLASH_CFG			0x0038
> +#define REG_CYCLE_CFG			0x0098
> +
> +#define REG_SGPIO_LED_DATE		0x0000
> +#define SGPIO_LED_SHIFT_FLAG		BIT(31)
> +#define SGPIO_LED_DATA			GENMASK(16, 0)

Please make the bit fields's names start with the register name.

> +#define REG_SGPIO_CLK_DIVR		0x0004
> +#define REG_SGPIO_CLK_DLY		0x0008
> +
> +#define REG_SIPO_FLASH_MODE_CFG		0x000c
> +#define SERIAL_GPIO_FLASH_MODE		BIT(1)
> +#define SERIAL_GPIO_MODE		BIT(0)
> +
> +#define REG_GPIO_FLASH_PRD_SET(_n)	(0x0004 + ((_n) << 2))
> +#define GPIO_FLASH_PRD_MASK(_n)		GENMASK(15 + ((_n) << 4), ((_n) << 4))
> +
> +#define REG_GPIO_FLASH_MAP(_n)		(0x0014 + ((_n) << 2))
> +#define GPIO_FLASH_SETID_MASK(_n)	GENMASK(2 + ((_n) << 2), ((_n) << 2))
> +#define GPIO_FLASH_EN(_n)		BIT(3 + ((_n) << 2))
> +
> +#define REG_SIPO_FLASH_MAP(_n)		(0x001c + ((_n) << 2))
> +
> +#define REG_CYCLE_CFG_VALUE(_n)		(0x0000 + ((_n) << 2))
> +#define WAVE_GEN_CYCLE_MASK(_n)		GENMASK(7 + ((_n) << 3), ((_n) << 3))
> +
> +struct airoha_pwm {
> +	void __iomem *sgpio_cfg;
> +	void __iomem *flash_cfg;
> +	void __iomem *cycle_cfg;
> +
> +	struct device_node *np;
> +	u64 initialized;
> +
> +	struct {
> +		/* Bitmask of PWM channels using this bucket */
> +		u64 used;
> +		u64 period_ns;
> +		u64 duty_ns;
> +		enum pwm_polarity polarity;
> +	} bucket[8];
> +};
> +
> +/*
> + * The first 16 GPIO pins, GPIO0-GPIO15, are mapped into 16 PWM channels, 0-15.
> + * The SIPO GPIO pins are 16 pins which are mapped into 17 PWM channels, 16-32.

How can 16 pins be mapped to 17 PWM channels?

> + * However, we've only got 8 concurrent waveform generators and can therefore
> + * only use up to 8 different combinations of duty cycle and period at a time.

That's an information to add to the Limitations paragraph.

> + */
> +#define PWM_NUM_GPIO	16
> +#define PWM_NUM_SIPO	17
> +
> +/* The PWM hardware supports periods between 4 ms and 1 s */
> +#define PERIOD_MIN_NS	4000000
> +#define PERIOD_MAX_NS	1000000000
> +/* It is represented internally as 1/250 s between 1 and 250 */
> +#define PERIOD_MIN	1
> +#define PERIOD_MAX	250
> +/* Duty cycle is relative with 255 corresponding to 100% */
> +#define DUTY_FULL	255
> +
> +static u32 airoha_pwm_rmw(struct airoha_pwm *pc, void __iomem *addr,
> +			  u32 mask, u32 val)
> +{
> +	val |= (readl(addr) & ~mask);
> +	writel(val, addr);
> +
> +	return val;
> +}

pc is unused here, please drop it.

> +#define airoha_pwm_sgpio_rmw(pc, offset, mask, val)				\
> +	airoha_pwm_rmw((pc), (pc)->sgpio_cfg + (offset), (mask), (val))
> +#define airoha_pwm_flash_rmw(pc, offset, mask, val)				\
> +	airoha_pwm_rmw((pc), (pc)->flash_cfg + (offset), (mask), (val))
> +#define airoha_pwm_cycle_rmw(pc, offset, mask, val)				\
> +	airoha_pwm_rmw((pc), (pc)->cycle_cfg + (offset), (mask), (val))
> +
> +#define airoha_pwm_sgpio_set(pc, offset, val)					\
> +	airoha_pwm_sgpio_rmw((pc), (offset), 0, (val))

That does the right thing, but I'd consider

	#define airoha_pwm_sgpio_set(pc, offset, val)					\
		airoha_pwm_sgpio_rmw((pc), (offset), (val), (val))

to be more understandable (or ~0 instead of the last (val)?)

> +#define airoha_pwm_sgpio_clear(pc, offset, mask)				\
> +	airoha_pwm_sgpio_rmw((pc), (offset), (mask), 0)
> +#define airoha_pwm_flash_set(pc, offset, val)					\
> +	airoha_pwm_flash_rmw((pc), (offset), 0, (val))
> +#define airoha_pwm_flash_clear(pc, offset, mask)				\
> +	airoha_pwm_flash_rmw((pc), (offset), (mask), 0)
> +
> +static int airoha_pwm_get_waveform(struct airoha_pwm *pc, u64 duty_ns,
> +				   u64 period_ns)

Given that "waveform" will gain some specific semantic soon, but this
usage is different, I'd like to see this function renamed.

> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) {
> +		if (!pc->bucket[i].used)
> +			continue;
> +
> +		if (duty_ns == pc->bucket[i].duty_ns &&
> +		    period_ns == pc->bucket[i].period_ns)
> +			return i;
> +
> +		/*
> +		 * Unlike duty cycle zero, which can be handled by
> +		 * disabling PWM, a generator is needed for full duty
> +		 * cycle but it can be reused regardless of period
> +		 */
> +		if (duty_ns == DUTY_FULL && pc->bucket[i].duty_ns == DUTY_FULL)
> +			return i;
> +	}
> +
> +	return -1;
> +}
> +
> +static void airoha_pwm_free_waveform(struct airoha_pwm *pc, unsigned int hwpwm)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(pc->bucket); i++)
> +		pc->bucket[i].used &= ~BIT_ULL(hwpwm);
> +}
> +
> +static int airoha_pwm_consume_waveform(struct airoha_pwm *pc,
> +				       u64 duty_ns, u64 period_ns,
> +				       enum pwm_polarity polarity,
> +				       unsigned int hwpwm)
> +{
> +	int id = airoha_pwm_get_waveform(pc, duty_ns, period_ns);
> +
> +	if (id < 0) {
> +		int i;
> +
> +		/* find an unused waveform generator */
> +		for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) {
> +			if (!(pc->bucket[i].used & ~BIT_ULL(hwpwm))) {
> +				id = i;
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (id >= 0) {
> +		airoha_pwm_free_waveform(pc, hwpwm);
> +		pc->bucket[id].used |= BIT_ULL(hwpwm);
> +		pc->bucket[id].period_ns = period_ns;
> +		pc->bucket[id].duty_ns = duty_ns;
> +		pc->bucket[id].polarity = polarity;
> +	}

One downside of the (nearly) maximal flexibility implemented here is
that if you have 9 (or more) requested pwm devices configuration is
quite limited.  So it might happen that a consumer changes the
configuration for pwm#2 from pwm_state A to pwm_state B but cannot
change back to A later.

If you limit the number of requested pwm devices to 8, the code gets a
tad simpler (because you can map a fixed bucket to each pwm device and
don't need to search during .apply()) and each consumer has maximal
freedom to configure its device.

> +
> +	return id;
> +}
> +
> +static int airoha_pwm_sipo_init(struct airoha_pwm *pc)
> +{
> +	u32 clk_divr_val = 3, sipo_clock_delay = 1;
> +	u32 val, sipo_clock_divisor = 32;
> +
> +	if (!(pc->initialized >> PWM_NUM_GPIO))
> +		return 0;
> +
> +	/* Select the right shift register chip */
> +	if (of_property_read_bool(pc->np, "hc74595"))
> +		airoha_pwm_sgpio_set(pc, REG_SIPO_FLASH_MODE_CFG,
> +				     SERIAL_GPIO_MODE);
> +	else
> +		airoha_pwm_sgpio_clear(pc, REG_SIPO_FLASH_MODE_CFG,
> +				       SERIAL_GPIO_MODE);
> +
> +	if (!of_property_read_u32(pc->np, "sipo-clock-divisor",
> +				  &sipo_clock_divisor)) {
> +		switch (sipo_clock_divisor) {
> +		case 4:
> +			clk_divr_val = 0;
> +			break;
> +		case 8:
> +			clk_divr_val = 1;
> +			break;
> +		case 16:
> +			clk_divr_val = 2;
> +			break;
> +		case 32:
> +			clk_divr_val = 3;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +	/* Configure shift register timings */
> +	writel(clk_divr_val, pc->sgpio_cfg + REG_SGPIO_CLK_DIVR);
> +
> +	of_property_read_u32(pc->np, "sipo-clock-delay", &sipo_clock_delay);
> +	if (sipo_clock_delay < 1 || sipo_clock_delay > sipo_clock_divisor / 2)
> +		return -EINVAL;
> +
> +	/*
> +	 * The actual delay is sclkdly + 1 so subtract 1 from
> +	 * sipo-clock-delay to calculate the register value
> +	 */
> +	sipo_clock_delay--;
> +	writel(sipo_clock_delay, pc->sgpio_cfg + REG_SGPIO_CLK_DLY);
> +
> +	/*
> +	 * It it necessary to after muxing explicitly shift out all
> +	 * zeroes to initialize the shift register before enabling PWM
> +	 * mode because in PWM mode SIPO will not start shifting until
> +	 * it needs to output a non-zero value (bit 31 of led_data
> +	 * indicates shifting in progress and it must return to zero
> +	 * before led_data can be written or PWM mode can be set)
> +	 */
> +	if (readl_poll_timeout(pc->sgpio_cfg + REG_SGPIO_LED_DATE, val,
> +			       !(val & SGPIO_LED_SHIFT_FLAG), 10,
> +			       200 * USEC_PER_MSEC))
> +		return -ETIMEDOUT;
> +
> +	airoha_pwm_sgpio_clear(pc, REG_SGPIO_LED_DATE, SGPIO_LED_DATA);
> +	if (readl_poll_timeout(pc->sgpio_cfg + REG_SGPIO_LED_DATE, val,
> +			       !(val & SGPIO_LED_SHIFT_FLAG), 10,
> +			       200 * USEC_PER_MSEC))
> +		return -ETIMEDOUT;
> +
> +	/* Set SIPO in PWM mode */
> +	airoha_pwm_sgpio_set(pc, REG_SIPO_FLASH_MODE_CFG,
> +			     SERIAL_GPIO_FLASH_MODE);
> +
> +	return 0;
> +}
> +
> +static void airoha_pwm_config_waveform(struct airoha_pwm *pc, int index,
> +				       u64 duty_ns, u64 period_ns,
> +				       enum pwm_polarity polarity)
> +{
> +	u32 period, duty, mask, val;
> +
> +	duty = clamp_val(div64_u64(DUTY_FULL * duty_ns, period_ns), 0,
> +			 DUTY_FULL);

DUTY_FULL * duty_ns might overflow. Also the calculation is wrong.
For example if the following is requested:

	.period = 23999999,
	.duty_cycle = 8000000,

you're supposed to configure period = 16000000 ns and duty_cycle =
8000000 ns.

(I.e.: Pick the biggest possible period not bigger than the requested
period. For that pick the biggest possible duty_cycle not bigger than
the requested duty_cycle.)

If you implement .get_state() in a way to return the actually configured
state, enabling PWM_DEBUG and intensive testing helps to get this right.

> +	if (polarity == PWM_POLARITY_INVERSED)
> +		duty = DUTY_FULL - duty;

This alone doesn't switch the polarity of the signal. Please only claim
to be able to implement the settings that the hardware actually can do.

> +	period = clamp_val(div64_u64(25 * period_ns, 100000000), PERIOD_MIN,
> +			   PERIOD_MAX);
> +
> +	/* Configure frequency divisor */
> +	mask = WAVE_GEN_CYCLE_MASK(index % 4);
> +	val = (period << __ffs(mask)) & mask;
> +	airoha_pwm_cycle_rmw(pc, REG_CYCLE_CFG_VALUE(index / 4), mask, val);
> +
> +	/* Configure duty cycle */
> +	duty = ((DUTY_FULL - duty) << 8) | duty;
> +	mask = GPIO_FLASH_PRD_MASK(index % 2);
> +	val = (duty << __ffs(mask)) & mask;
> +	airoha_pwm_flash_rmw(pc, REG_GPIO_FLASH_PRD_SET(index / 2), mask, val);
> +}
> +
> +static void airoha_pwm_config_flash_map(struct airoha_pwm *pc,
> +					unsigned int hwpwm, int index)
> +{
> +	u32 addr, mask, val;
> +
> +	if (hwpwm < PWM_NUM_GPIO) {
> +		addr = REG_GPIO_FLASH_MAP(hwpwm / 8);
> +	} else {
> +		addr = REG_SIPO_FLASH_MAP(hwpwm / 8);
> +		hwpwm -= PWM_NUM_GPIO;
> +	}
> +
> +	if (index < 0) {
> +		/*
> +		 * Change of waveform takes effect immediately but
> +		 * disabling has some delay so to prevent glitching
> +		 * only the enable bit is touched when disabling
> +		 */
> +		airoha_pwm_flash_clear(pc, addr, GPIO_FLASH_EN(hwpwm % 8));
> +		return;
> +	}
> +
> +	mask = GPIO_FLASH_SETID_MASK(hwpwm % 8);
> +	val = ((index & 7) << __ffs(mask)) & mask;
> +	airoha_pwm_flash_rmw(pc, addr, mask, val);
> +	airoha_pwm_flash_set(pc, addr, GPIO_FLASH_EN(hwpwm % 8));
> +}
> +
> +static int airoha_pwm_config(struct airoha_pwm *pc, struct pwm_device *pwm,
> +			     u64 duty_ns, u64 period_ns,
> +			     enum pwm_polarity polarity)
> +{
> +	int index = -1;
> +
> +	index = airoha_pwm_consume_waveform(pc, duty_ns, period_ns, polarity,
> +					    pwm->hwpwm);
> +	if (index < 0)
> +		return -EBUSY;
> +
> +	if (!(pc->initialized & BIT_ULL(pwm->hwpwm)) &&
> +	    pwm->hwpwm >= PWM_NUM_GPIO)
> +		airoha_pwm_sipo_init(pc);
> +
> +	if (index >= 0) {
> +		airoha_pwm_config_waveform(pc, index, duty_ns, period_ns,
> +					   polarity);
> +		airoha_pwm_config_flash_map(pc, pwm->hwpwm, index);
> +	} else {
> +		airoha_pwm_config_flash_map(pc, pwm->hwpwm, index);
> +		airoha_pwm_free_waveform(pc, pwm->hwpwm);
> +	}
> +
> +	pc->initialized |= BIT_ULL(pwm->hwpwm);
> +
> +	return 0;
> +}
> +
> +static void airoha_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> +	struct airoha_pwm *pc = pwmchip_get_drvdata(chip);
> +
> +	/* Disable PWM and release the waveform */
> +	airoha_pwm_config_flash_map(pc, pwm->hwpwm, -1);
> +	airoha_pwm_free_waveform(pc, pwm->hwpwm);
> +
> +	pc->initialized &= ~BIT_ULL(pwm->hwpwm);
> +	if (!(pc->initialized >> PWM_NUM_GPIO))
> +		airoha_pwm_sgpio_clear(pc, REG_SIPO_FLASH_MODE_CFG,
> +				       SERIAL_GPIO_FLASH_MODE);
> +
> +	/*
> +	 * Clear the state to force re-initialization the next time
> +	 * this PWM channel is used since we cannot retain state in
> +	 * hardware due to limited number of waveform generators
> +	 */
> +	memset(&pwm->state, 0, sizeof(pwm->state));

Please don't reconfigure the hardware in .free(). Instead assume that
the consumer disabled the PWM before releasing it or that they know what
they do.

Also don't write to pwm->state, that is supposed to only happen in the
core.

> +}
> +
> +static int airoha_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> +			    const struct pwm_state *state)
> +{
> +	struct airoha_pwm *pc = pwmchip_get_drvdata(chip);
> +	u64 duty = state->enabled ? state->duty_cycle : 0;
> +
> +	if (!state->enabled) {
> +		airoha_pwm_free(chip, pwm);
> +		return 0;
> +	}
> +
> +	if (state->period < PERIOD_MIN_NS || state->period > PERIOD_MAX_NS)
> +		return -EINVAL;

Please handle state->period > PERIOD_MAX_NS by configuring a period of
PERIOD_MAX_NS.

> +	return airoha_pwm_config(pc, pwm, duty, state->period,
> +				 state->polarity);
> +}
> +
> +static int airoha_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
> +				struct pwm_state *state)
> +{
> +	struct airoha_pwm *pc = pwmchip_get_drvdata(chip);
> +	int i;
> +
> +	/* find hwpwm in waveform generator bucket */
> +	for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) {
> +		if (pc->bucket[i].used & BIT_ULL(pwm->hwpwm)) {
> +			state->enabled = pc->initialized & BIT_ULL(pwm->hwpwm);
> +			state->polarity = pc->bucket[i].polarity;
> +			state->period = pc->bucket[i].period_ns;
> +			state->duty_cycle = pc->bucket[i].duty_ns;
> +			break;
> +		}
> +	}
> +
> +	if (i == ARRAY_SIZE(pc->bucket))
> +		state->enabled = false;
> +
> +	return 0;
> +}
> +
> +static const struct pwm_ops airoha_pwm_ops = {
> +	.get_state = airoha_pwm_get_state,
> +	.apply = airoha_pwm_apply,
> +	.free = airoha_pwm_free,
> +};
> +
> +static int airoha_pwm_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct airoha_mfd *mfd;
> +	struct airoha_pwm *pc;
> +	struct pwm_chip *chip;
> +
> +	/* Assign parent MFD of_node to dev */
> +	dev->of_node = of_node_get(dev->parent->of_node);

I think you want
device_set_of_node_from_dev(dev->parent, dev)
here.

> +	mfd = dev_get_drvdata(dev->parent);
> +
> +	chip = devm_pwmchip_alloc(dev, PWM_NUM_GPIO + PWM_NUM_SIPO,
> +				  sizeof(*pc));
> +	if (IS_ERR(chip))
> +		return PTR_ERR(chip);
> +
> +	pc = pwmchip_get_drvdata(chip);
> +	pc->np = dev->of_node;
> +	pc->sgpio_cfg = mfd->base + REG_SGPIO_CFG;
> +	pc->flash_cfg = mfd->base + REG_FLASH_CFG;
> +	pc->cycle_cfg = mfd->base + REG_CYCLE_CFG;

Is it really worth to store these values in the private data struct? My
intuition would be to only store .base in pc and then define the
register accessors macros like:

	#define airoha_pwm_cycle_rmw(pc, offset, mask, val)				\
		airoha_pwm_rmw((pc), (pc)->base + REG_CYCLE_CFG + (offset), (mask), (val))

> +	chip->ops = &airoha_pwm_ops;
> +	chip->of_xlate = of_pwm_xlate_with_flags;

Please don't assign .of_xlate, the core does

        if (!chip->of_xlate)
                chip->of_xlate = of_pwm_xlate_with_flags;

> +
> +	return devm_pwmchip_add(&pdev->dev, chip);
> +}
> +
> +static struct platform_driver airoha_pwm_driver = {
> +	.driver = {
> +		.name = "airoha-pwm",
> +	},
> +	.probe = airoha_pwm_probe,
> +};
> +module_platform_driver(airoha_pwm_driver);
> +
> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
> +MODULE_AUTHOR("Markus Gothe <markus.gothe@genexis.eu>");
> +MODULE_AUTHOR("Benjamin Larsson <benjamin.larsson@genexis.eu>");
> +MODULE_DESCRIPTION("Airoha EN7581 PWM driver");
> +MODULE_LICENSE("GPL");

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-09-03 10:46   ` Uwe Kleine-König
@ 2024-09-03 11:58     ` Benjamin Larsson
  2024-09-03 15:47       ` Uwe Kleine-König
  0 siblings, 1 reply; 24+ messages in thread
From: Benjamin Larsson @ 2024-09-03 11:58 UTC (permalink / raw)
  To: Uwe Kleine-König, Lorenzo Bianconi
  Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
	Lee Jones, linux-mediatek, linux-gpio, devicetree,
	linux-arm-kernel, upstream, ansuelsmth, linux-pwm

Hi.

On 2024-09-03 12:46, Uwe Kleine-König wrote:
> Hello,
>
> On Sat, Aug 31, 2024 at 04:27:50PM +0200, Lorenzo Bianconi wrote:
>> From: Benjamin Larsson <benjamin.larsson@genexis.eu>
>>
>> Introduce driver for PWM module available on EN7581 SoC.
>>
>> Signed-off-by: Benjamin Larsson <benjamin.larsson@genexis.eu>
>> Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
>> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
>> ---
>>   drivers/pwm/Kconfig      |  10 ++
>>   drivers/pwm/Makefile     |   1 +
>>   drivers/pwm/pwm-airoha.c | 435 +++++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 446 insertions(+)
>>
>> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
>> index 3e53838990f5..0a78bda0707d 100644
>> --- a/drivers/pwm/Kconfig
>> +++ b/drivers/pwm/Kconfig
>> @@ -47,6 +47,16 @@ config PWM_AB8500
>>   	  To compile this driver as a module, choose M here: the module
>>   	  will be called pwm-ab8500.
>>   
>> +config PWM_AIROHA
>> +	tristate "Airoha PWM support"
>> +	depends on ARCH_AIROHA || COMPILE_TEST
>> +	depends on OF
>> +	help
>> +	  Generic PWM framework driver for Airoha SoC.
>> +
>> +	  To compile this driver as a module, choose M here: the module
>> +	  will be called pwm-airoha.
>> +
>>   config PWM_APPLE
>>   	tristate "Apple SoC PWM support"
>>   	depends on ARCH_APPLE || COMPILE_TEST
>> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
>> index 0be4f3e6dd43..7ee61822d88d 100644
>> --- a/drivers/pwm/Makefile
>> +++ b/drivers/pwm/Makefile
>> @@ -1,6 +1,7 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   obj-$(CONFIG_PWM)		+= core.o
>>   obj-$(CONFIG_PWM_AB8500)	+= pwm-ab8500.o
>> +obj-$(CONFIG_PWM_AIROHA)	+= pwm-airoha.o
>>   obj-$(CONFIG_PWM_APPLE)		+= pwm-apple.o
>>   obj-$(CONFIG_PWM_ATMEL)		+= pwm-atmel.o
>>   obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
>> diff --git a/drivers/pwm/pwm-airoha.c b/drivers/pwm/pwm-airoha.c
>> new file mode 100644
>> index 000000000000..54dc12d20da4
>> --- /dev/null
>> +++ b/drivers/pwm/pwm-airoha.c
>> @@ -0,0 +1,435 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright 2022 Markus Gothe <markus.gothe@genexis.eu>
>> + */
> Would you please add a "Limitations" paragraph here covering the
> following questions:
>
>   - How does the hardware behave on changes of configuration (does it
>     complete the currently running period? Are there any glitches?)
>   - How does the hardware behave on disabling?
>
> Please stick to the format used in several other drivers such that
>
> 	sed -rn '/Limitations:/,/\*\/?$/p' drivers/pwm/*.c
>
> emits the informations.


The answer to your questions are currently unknown. Is this information 
needed for a merge of the driver ?


>
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/mfd/airoha-en7581-mfd.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pwm.h>
>> +#include <linux/gpio.h>
>> +#include <linux/bitops.h>
>> +#include <asm/div64.h>
>> +
>> +#define REG_SGPIO_CFG			0x0024
>> +#define REG_FLASH_CFG			0x0038
>> +#define REG_CYCLE_CFG			0x0098
>> +
>> +#define REG_SGPIO_LED_DATE		0x0000
>> +#define SGPIO_LED_SHIFT_FLAG		BIT(31)
>> +#define SGPIO_LED_DATA			GENMASK(16, 0)
> Please make the bit fields's names start with the register name.
>
I noticed REG_SGPIO_LED_DATE

DATE should be DATA.


>> +#define REG_SGPIO_CLK_DIVR		0x0004
>> +#define REG_SGPIO_CLK_DLY		0x0008
>> +
>> +#define REG_SIPO_FLASH_MODE_CFG		0x000c
>> +#define SERIAL_GPIO_FLASH_MODE		BIT(1)
>> +#define SERIAL_GPIO_MODE		BIT(0)
>> +
>> +#define REG_GPIO_FLASH_PRD_SET(_n)	(0x0004 + ((_n) << 2))
>> +#define GPIO_FLASH_PRD_MASK(_n)		GENMASK(15 + ((_n) << 4), ((_n) << 4))
>> +
>> +#define REG_GPIO_FLASH_MAP(_n)		(0x0014 + ((_n) << 2))
>> +#define GPIO_FLASH_SETID_MASK(_n)	GENMASK(2 + ((_n) << 2), ((_n) << 2))
>> +#define GPIO_FLASH_EN(_n)		BIT(3 + ((_n) << 2))
>> +
>> +#define REG_SIPO_FLASH_MAP(_n)		(0x001c + ((_n) << 2))
>> +
>> +#define REG_CYCLE_CFG_VALUE(_n)		(0x0000 + ((_n) << 2))
>> +#define WAVE_GEN_CYCLE_MASK(_n)		GENMASK(7 + ((_n) << 3), ((_n) << 3))
>> +
>> +struct airoha_pwm {
>> +	void __iomem *sgpio_cfg;
>> +	void __iomem *flash_cfg;
>> +	void __iomem *cycle_cfg;
>> +
>> +	struct device_node *np;
>> +	u64 initialized;
>> +
>> +	struct {
>> +		/* Bitmask of PWM channels using this bucket */
>> +		u64 used;
>> +		u64 period_ns;
>> +		u64 duty_ns;
>> +		enum pwm_polarity polarity;
>> +	} bucket[8];
>> +};
>> +
>> +/*
>> + * The first 16 GPIO pins, GPIO0-GPIO15, are mapped into 16 PWM channels, 0-15.
>> + * The SIPO GPIO pins are 16 pins which are mapped into 17 PWM channels, 16-32.
> How can 16 pins be mapped to 17 PWM channels?


The text is incorrect. There can be 17 pins in 17 slots.


>
>> + * However, we've only got 8 concurrent waveform generators and can therefore
>> + * only use up to 8 different combinations of duty cycle and period at a time.
> That's an information to add to the Limitations paragraph.
>
>> + */
>> +#define PWM_NUM_GPIO	16
>> +#define PWM_NUM_SIPO	17
>> +
>> +/* The PWM hardware supports periods between 4 ms and 1 s */
>> +#define PERIOD_MIN_NS	4000000
>> +#define PERIOD_MAX_NS	1000000000
>> +/* It is represented internally as 1/250 s between 1 and 250 */
>> +#define PERIOD_MIN	1
>> +#define PERIOD_MAX	250
>> +/* Duty cycle is relative with 255 corresponding to 100% */
>> +#define DUTY_FULL	255
>> +
>> +static u32 airoha_pwm_rmw(struct airoha_pwm *pc, void __iomem *addr,
>> +			  u32 mask, u32 val)
>> +{
>> +	val |= (readl(addr) & ~mask);
>> +	writel(val, addr);
>> +
>> +	return val;
>> +}
> pc is unused here, please drop it.
>
>> +#define airoha_pwm_sgpio_rmw(pc, offset, mask, val)				\
>> +	airoha_pwm_rmw((pc), (pc)->sgpio_cfg + (offset), (mask), (val))
>> +#define airoha_pwm_flash_rmw(pc, offset, mask, val)				\
>> +	airoha_pwm_rmw((pc), (pc)->flash_cfg + (offset), (mask), (val))
>> +#define airoha_pwm_cycle_rmw(pc, offset, mask, val)				\
>> +	airoha_pwm_rmw((pc), (pc)->cycle_cfg + (offset), (mask), (val))
>> +
>> +#define airoha_pwm_sgpio_set(pc, offset, val)					\
>> +	airoha_pwm_sgpio_rmw((pc), (offset), 0, (val))
> That does the right thing, but I'd consider
>
> 	#define airoha_pwm_sgpio_set(pc, offset, val)					\
> 		airoha_pwm_sgpio_rmw((pc), (offset), (val), (val))
>
> to be more understandable (or ~0 instead of the last (val)?)
>
>> +#define airoha_pwm_sgpio_clear(pc, offset, mask)				\
>> +	airoha_pwm_sgpio_rmw((pc), (offset), (mask), 0)
>> +#define airoha_pwm_flash_set(pc, offset, val)					\
>> +	airoha_pwm_flash_rmw((pc), (offset), 0, (val))
>> +#define airoha_pwm_flash_clear(pc, offset, mask)				\
>> +	airoha_pwm_flash_rmw((pc), (offset), (mask), 0)
>> +
>> +static int airoha_pwm_get_waveform(struct airoha_pwm *pc, u64 duty_ns,
>> +				   u64 period_ns)
> Given that "waveform" will gain some specific semantic soon, but this
> usage is different, I'd like to see this function renamed.

I suggest pwm_generator. Is that acceptable ?


>
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) {
>> +		if (!pc->bucket[i].used)
>> +			continue;
>> +
>> +		if (duty_ns == pc->bucket[i].duty_ns &&
>> +		    period_ns == pc->bucket[i].period_ns)
>> +			return i;
>> +
>> +		/*
>> +		 * Unlike duty cycle zero, which can be handled by
>> +		 * disabling PWM, a generator is needed for full duty
>> +		 * cycle but it can be reused regardless of period
>> +		 */
>> +		if (duty_ns == DUTY_FULL && pc->bucket[i].duty_ns == DUTY_FULL)
>> +			return i;
>> +	}
>> +
>> +	return -1;
>> +}
>> +
>> +static void airoha_pwm_free_waveform(struct airoha_pwm *pc, unsigned int hwpwm)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(pc->bucket); i++)
>> +		pc->bucket[i].used &= ~BIT_ULL(hwpwm);
>> +}
>> +
>> +static int airoha_pwm_consume_waveform(struct airoha_pwm *pc,
>> +				       u64 duty_ns, u64 period_ns,
>> +				       enum pwm_polarity polarity,
>> +				       unsigned int hwpwm)
>> +{
>> +	int id = airoha_pwm_get_waveform(pc, duty_ns, period_ns);
>> +
>> +	if (id < 0) {
>> +		int i;
>> +
>> +		/* find an unused waveform generator */
>> +		for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) {
>> +			if (!(pc->bucket[i].used & ~BIT_ULL(hwpwm))) {
>> +				id = i;
>> +				break;
>> +			}
>> +		}
>> +	}
>> +
>> +	if (id >= 0) {
>> +		airoha_pwm_free_waveform(pc, hwpwm);
>> +		pc->bucket[id].used |= BIT_ULL(hwpwm);
>> +		pc->bucket[id].period_ns = period_ns;
>> +		pc->bucket[id].duty_ns = duty_ns;
>> +		pc->bucket[id].polarity = polarity;
>> +	}
> One downside of the (nearly) maximal flexibility implemented here is
> that if you have 9 (or more) requested pwm devices configuration is
> quite limited.  So it might happen that a consumer changes the
> configuration for pwm#2 from pwm_state A to pwm_state B but cannot
> change back to A later.


Correct.


>
> If you limit the number of requested pwm devices to 8, the code gets a
> tad simpler (because you can map a fixed bucket to each pwm device and
> don't need to search during .apply()) and each consumer has maximal
> freedom to configure its device.


The main use for this solution is for led-dimming which uses the same 
timing among groups of leds. Most (of our) products have more then 8 
leds in total, with a limit of only 8 pwm devices it would not be 
possible to use the mainline driver with our hardware. I suggest that 
the current logic is kept but properly documented in the limitations 
section.


>
>> +
>> +	return id;
>> +}
>> +
>> +static int airoha_pwm_sipo_init(struct airoha_pwm *pc)
>> +{
>> +	u32 clk_divr_val = 3, sipo_clock_delay = 1;
>> +	u32 val, sipo_clock_divisor = 32;
>> +
>> +	if (!(pc->initialized >> PWM_NUM_GPIO))
>> +		return 0;
>> +
>> +	/* Select the right shift register chip */
>> +	if (of_property_read_bool(pc->np, "hc74595"))
>> +		airoha_pwm_sgpio_set(pc, REG_SIPO_FLASH_MODE_CFG,
>> +				     SERIAL_GPIO_MODE);
>> +	else
>> +		airoha_pwm_sgpio_clear(pc, REG_SIPO_FLASH_MODE_CFG,
>> +				       SERIAL_GPIO_MODE);
>> +
>> +	if (!of_property_read_u32(pc->np, "sipo-clock-divisor",
>> +				  &sipo_clock_divisor)) {
>> +		switch (sipo_clock_divisor) {
>> +		case 4:
>> +			clk_divr_val = 0;
>> +			break;
>> +		case 8:
>> +			clk_divr_val = 1;
>> +			break;
>> +		case 16:
>> +			clk_divr_val = 2;
>> +			break;
>> +		case 32:
>> +			clk_divr_val = 3;
>> +			break;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	}
>> +	/* Configure shift register timings */
>> +	writel(clk_divr_val, pc->sgpio_cfg + REG_SGPIO_CLK_DIVR);
>> +
>> +	of_property_read_u32(pc->np, "sipo-clock-delay", &sipo_clock_delay);
>> +	if (sipo_clock_delay < 1 || sipo_clock_delay > sipo_clock_divisor / 2)
>> +		return -EINVAL;
>> +
>> +	/*
>> +	 * The actual delay is sclkdly + 1 so subtract 1 from
>> +	 * sipo-clock-delay to calculate the register value
>> +	 */
>> +	sipo_clock_delay--;
>> +	writel(sipo_clock_delay, pc->sgpio_cfg + REG_SGPIO_CLK_DLY);
>> +
>> +	/*
>> +	 * It it necessary to after muxing explicitly shift out all
>> +	 * zeroes to initialize the shift register before enabling PWM
>> +	 * mode because in PWM mode SIPO will not start shifting until
>> +	 * it needs to output a non-zero value (bit 31 of led_data
>> +	 * indicates shifting in progress and it must return to zero
>> +	 * before led_data can be written or PWM mode can be set)
>> +	 */
>> +	if (readl_poll_timeout(pc->sgpio_cfg + REG_SGPIO_LED_DATE, val,
>> +			       !(val & SGPIO_LED_SHIFT_FLAG), 10,
>> +			       200 * USEC_PER_MSEC))
>> +		return -ETIMEDOUT;
>> +
>> +	airoha_pwm_sgpio_clear(pc, REG_SGPIO_LED_DATE, SGPIO_LED_DATA);
>> +	if (readl_poll_timeout(pc->sgpio_cfg + REG_SGPIO_LED_DATE, val,
>> +			       !(val & SGPIO_LED_SHIFT_FLAG), 10,
>> +			       200 * USEC_PER_MSEC))
>> +		return -ETIMEDOUT;
>> +
>> +	/* Set SIPO in PWM mode */
>> +	airoha_pwm_sgpio_set(pc, REG_SIPO_FLASH_MODE_CFG,
>> +			     SERIAL_GPIO_FLASH_MODE);
>> +
>> +	return 0;
>> +}
>> +
>> +static void airoha_pwm_config_waveform(struct airoha_pwm *pc, int index,
>> +				       u64 duty_ns, u64 period_ns,
>> +				       enum pwm_polarity polarity)
>> +{
>> +	u32 period, duty, mask, val;
>> +
>> +	duty = clamp_val(div64_u64(DUTY_FULL * duty_ns, period_ns), 0,
>> +			 DUTY_FULL);
> DUTY_FULL * duty_ns might overflow. Also the calculation is wrong.
> For example if the following is requested:
>
> 	.period = 23999999,
> 	.duty_cycle = 8000000,
>
> you're supposed to configure period = 16000000 ns and duty_cycle =
> 8000000 ns.
>
> (I.e.: Pick the biggest possible period not bigger than the requested
> period. For that pick the biggest possible duty_cycle not bigger than
> the requested duty_cycle.)
>
> If you implement .get_state() in a way to return the actually configured
> state, enabling PWM_DEBUG and intensive testing helps to get this right.
>
>> +	if (polarity == PWM_POLARITY_INVERSED)
>> +		duty = DUTY_FULL - duty;
> This alone doesn't switch the polarity of the signal. Please only claim
> to be able to implement the settings that the hardware actually can do.

I am not sure I agree, but I will investigate this further.


MvH

Benjamin Larsson


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller
  2024-09-03 15:33   ` Lee Jones
@ 2024-09-03 14:00     ` Christian Marangi
  2024-09-03 16:42       ` Lee Jones
  0 siblings, 1 reply; 24+ messages in thread
From: Christian Marangi @ 2024-09-03 14:00 UTC (permalink / raw)
  To: Lee Jones
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Uwe Kleine-König, linux-mediatek,
	linux-gpio, devicetree, linux-arm-kernel, upstream,
	benjamin.larsson, linux-pwm

On Tue, Sep 03, 2024 at 04:33:53PM +0100, Lee Jones wrote:
> On Sat, 31 Aug 2024, Lorenzo Bianconi wrote:
> 
> > From: Christian Marangi <ansuelsmth@gmail.com>
> > 
> > Add support for Airoha EN7581 GPIO System Controller which provide a
> > register map for controlling the GPIO, pinctrl and PWM of the SoC.
> > 
> > Schema define cells for both gpio/interrupt controller and PWM.
> > Moreover it provides a dedicated pinctrl node for pins and config
> > definitions.
> > 
> > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > ---
> >  .../bindings/mfd/airoha,en7581-gpio-sysctl.yaml    | 433 +++++++++++++++++++++
> >  1 file changed, 433 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> > new file mode 100644
> > index 000000000000..a9080c7f50f9
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> > @@ -0,0 +1,433 @@
> > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/mfd/airoha,en7581-gpio-sysctl.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Airoha EN7581 GPIO System Controller
> > +
> > +maintainers:
> > +  - Christian Marangi <ansuelsmth@gmail.com>
> > +  - Lorenzo Bianconi <lorenzo@kernel.org>
> > +
> > +description:
> > +  Airoha EN7581 SoC GPIO system controller which provided a register map
> > +  for controlling the GPIO, pins and PWM of the SoC.
> 
> This whole thing is just about pins.
> 
> The MFD portion of this submission doesn't do anything.
>

Hi Lee,

thanks for the review. I think you missed the other series as it was
requested to use MFD implementation due to shared register map.

> Please rework this to omit the MFD driver.

I'm a bit confused by this you mean in the schema? Putting PWM property
in a pinctrl schema looks wrong to me :(

> 
> After just a glance, it looks like simple-mfd _might_ work.

Simple-mfd works if register map are well defined and you can have
something like
- parent define the whole register
- child can user reg property to register offset and subsection of the
  parent register

Here it's all mixed and scrambled and also it was requested to have a
very simple node that include both pwm and pinctrl property (cause that
is how the HW register block is designed and schema must reflect HW)

Hope you can understand these reasons.

> 
> > +properties:
> > +  compatible:
> > +    const: airoha,en7581-gpio-sysctl
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +
> > +  gpio-controller: true
> > +
> > +  '#gpio-cells':
> > +    const: 2
> > +
> > +  interrupt-controller: true
> > +
> > +  '#interrupt-cells':
> > +    const: 2
> > +
> > +  "#pwm-cells":
> > +    const: 3
> > +
> > +  pinctrl:
> > +    type: object
> > +
> > +    $ref: /schemas/pinctrl/pinctrl.yaml
> > +
> > +    patternProperties:
> > +      '-pins$':
> > +        type: object
> > +
> > +        patternProperties:
> > +          '^.*mux.*$':
> > +            type: object
> > +
> > +            description:
> > +              pinmux configuration nodes.
> > +
> > +            $ref: /schemas/pinctrl/pinmux-node.yaml
> > +
> > +            properties:
> > +              function:
> > +                description:
> > +                  A string containing the name of the function to mux to the group.
> > +                enum: [pon, tod_1pps, sipo, mdio, uart, i2c, jtag, pcm, spi,
> > +                       pcm_spi, i2s, emmc, pnand, pcie_reset, pwm, phy1_led0,
> > +                       phy2_led0, phy3_led0, phy4_led0, phy1_led1, phy2_led1,
> > +                       phy3_led1, phy4_led1]
> > +
> > +              groups:
> > +                description:
> > +                  An array of strings. Each string contains the name of a group.
> > +
> > +            required:
> > +              - function
> > +              - groups
> > +
> > +            allOf:
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: pon
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [pon]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: tod_1pps
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [pon_tod_1pps, gsw_tod_1pps]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: sipo
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [sipo, sipo_rclk]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: mdio
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [mdio]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: uart
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      items:
> > +                        enum: [uart2, uart2_cts_rts, hsuart, hsuart_cts_rts, uart4,
> > +                               uart5]
> > +                      maxItems: 2
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: i2c
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [i2c1]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: jtag
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [jtag_udi, jtag_dfd]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: pcm
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [pcm1, pcm2]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: spi
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      items:
> > +                        enum: [spi_quad, spi_cs1]
> > +                      maxItems: 2
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: pcm_spi
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      items:
> > +                        enum: [pcm_spi, pcm_spi_int, pcm_spi_rst, pcm_spi_cs1,
> > +                               pcm_spi_cs2_p156, pcm_spi_cs2_p128, pcm_spi_cs3,
> > +                               pcm_spi_cs4]
> > +                      maxItems: 7
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: i2c
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [i2s]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: emmc
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [emmc]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: pnand
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [pnand]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: pcie_reset
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [pcie_reset0, pcie_reset1, pcie_reset2]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: pwm
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio0, gpio1, gpio2, gpio3, gpio4, gpio5, gpio6,
> > +                             gpio7, gpio8, gpio9, gpio10, gpio11, gpio12, gpio13,
> > +                             gpio14, gpio15, gpio16, gpio17, gpio18, gpio19,
> > +                             gpio20, gpio21, gpio22, gpio23, gpio24, gpio25,
> > +                             gpio26, gpio27, gpio28, gpio29, gpio30, gpio31,
> > +                             gpio36, gpio37, gpio38, gpio39, gpio40, gpio41,
> > +                             gpio42, gpio43, gpio44, gpio45, gpio46, gpio47]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: phy1_led0
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio33, gpio34, gpio35, gpio42]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: phy2_led0
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio33, gpio34, gpio35, gpio42]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: phy3_led0
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio33, gpio34, gpio35, gpio42]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: phy4_led0
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio33, gpio34, gpio35, gpio42]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: phy1_led1
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio43, gpio44, gpio45, gpio46]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: phy2_led1
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio43, gpio44, gpio45, gpio46]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: phy3_led1
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio43, gpio44, gpio45, gpio46]
> > +              - if:
> > +                  properties:
> > +                    function:
> > +                      const: phy4_led1
> > +                then:
> > +                  properties:
> > +                    groups:
> > +                      enum: [gpio43, gpio44, gpio45, gpio46]
> > +
> > +            additionalProperties: false
> > +
> > +          '^.*conf.*$':
> > +            type: object
> > +
> > +            description:
> > +              pinconf configuration nodes.
> > +
> > +            $ref: /schemas/pinctrl/pincfg-node.yaml
> > +
> > +            properties:
> > +              pins:
> > +                description:
> > +                  An array of strings. Each string contains the name of a pin.
> > +                items:
> > +                  enum: [uart1_txd, uart1_rxd, i2c_scl, i2c_sda, spi_cs0, spi_clk,
> > +                         spi_mosi, spi_miso, gpio0, gpio1, gpio2, gpio3, gpio4,
> > +                         gpio5, gpio6, gpio7, gpio8, gpio9, gpio10, gpio11, gpio12,
> > +                         gpio13, gpio14, gpio15, gpio16, gpio17, gpio18, gpio19,
> > +                         gpio20, gpio21, gpio22, gpio23, gpio24, gpio25, gpio26,
> > +                         gpio27, gpio28, gpio29, gpio30, gpio31, gpio32, gpio33,
> > +                         gpio34, gpio35, gpio36, gpio37, gpio38, gpio39, gpio40,
> > +                         gpio41, gpio42, gpio43, gpio44, gpio45, gpio46,
> > +                         pcie_reset0, pcie_reset1, pcie_reset2]
> > +                minItems: 1
> > +                maxItems: 58
> > +
> > +              bias-disable: true
> > +
> > +              bias-pull-up: true
> > +
> > +              bias-pull-down: true
> > +
> > +              input-enable: true
> > +
> > +              output-enable: true
> > +
> > +              output-low: true
> > +
> > +              output-high: true
> > +
> > +              drive-open-drain: true
> > +
> > +              drive-strength:
> > +                description:
> > +                  Selects the drive strength for MIO pins, in mA.
> > +                enum: [2, 4, 6, 8]
> > +
> > +            required:
> > +              - pins
> > +
> > +            additionalProperties: false
> > +
> > +        additionalProperties: false
> > +
> > +    additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupts
> > +  - gpio-controller
> > +  - "#gpio-cells"
> > +  - "#pwm-cells"
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > +
> > +    mfd@1fbf0200 {
> > +        compatible = "airoha,en7581-gpio-sysctl";
> > +        reg = <0x1fbf0200 0xc0>;
> > +
> > +        interrupt-parent = <&gic>;
> > +        interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
> > +
> > +        gpio-controller;
> > +        #gpio-cells = <2>;
> > +
> > +        interrupt-controller;
> > +        #interrupt-cells = <2>;
> > +
> > +        #pwm-cells = <3>;
> > +
> > +        pinctrl {
> > +            pcie1-rst-pins {
> > +                conf {
> > +                    pins = "pcie_reset1";
> > +                    drive-open-drain = <1>;
> > +                };
> > +            };
> > +
> > +            pwm-pins {
> > +                mux {
> > +                    function = "pwm";
> > +                    groups = "gpio18";
> > +                };
> > +            };
> > +
> > +            spi-pins {
> > +                mux {
> > +                    function = "spi";
> > +                    groups = "spi_quad", "spi_cs1";
> > +                };
> > +            };
> > +
> > +            uart2-pins {
> > +                mux {
> > +                    function = "uart";
> > +                    groups = "uart2", "uart2_cts_rts";
> > +                };
> > +            };
> > +
> > +            uar5-pins {
> > +                mux {
> > +                    function = "uart";
> > +                    groups = "uart5";
> > +                };
> > +            };
> > +
> > +            mmc-pins {
> > +                mux {
> > +                    function = "emmc";
> > +                    groups = "emmc";
> > +                };
> > +            };
> > +
> > +            mdio-pins {
> > +                mux {
> > +                    function = "mdio";
> > +                    groups = "mdio";
> > +                };
> > +
> > +                conf {
> > +                    pins = "gpio2";
> > +                    output-enable;
> > +                };
> > +            };
> > +
> > +            gswp1-led0-pins {
> > +                mux {
> > +                    function = "phy1_led0";
> > +                    groups = "gpio33";
> > +                };
> > +            };
> > +
> > +            gswp2-led1-pins {
> > +                mux {
> > +                    function = "phy2_led1";
> > +                    groups = "gpio44";
> > +                };
> > +            };
> > +        };
> > +    };
> > +
> > +...
> > 
> > -- 
> > 2.46.0
> > 
> 
> -- 
> Lee Jones [李琼斯]

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 1/5] dt-bindings: arm: airoha: Add the chip-scu node for EN7581 SoC
  2024-08-31 14:27 ` [PATCH v3 1/5] dt-bindings: arm: airoha: Add the chip-scu node for " Lorenzo Bianconi
@ 2024-09-03 15:05   ` Rob Herring (Arm)
  0 siblings, 0 replies; 24+ messages in thread
From: Rob Herring (Arm) @ 2024-09-03 15:05 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: devicetree, Uwe Kleine-König, linux-pwm, Conor Dooley,
	Lee Jones, linux-mediatek, upstream, Sean Wang, Linus Walleij,
	Matthias Brugger, Krzysztof Kozlowski, linux-gpio,
	benjamin.larsson, ansuelsmth, AngeloGioacchino Del Regno,
	linux-arm-kernel


On Sat, 31 Aug 2024 16:27:46 +0200, Lorenzo Bianconi wrote:
> This patch adds the chip-scu document bindings for EN7581 SoC.
> The airoha chip-scu block provides a configuration interface for clock,
> io-muxing and other functionalities used by multiple controllers (e.g.
> clock, pinctrl, ecc.) on EN7581 SoC.
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  .../bindings/arm/airoha,en7581-chip-scu.yaml       | 42 ++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
> 

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller
  2024-08-31 14:27 ` [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller Lorenzo Bianconi
@ 2024-09-03 15:09   ` Rob Herring
  2024-09-03 15:33   ` Lee Jones
  1 sibling, 0 replies; 24+ messages in thread
From: Rob Herring @ 2024-09-03 15:09 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: Linus Walleij, Krzysztof Kozlowski, Conor Dooley, Sean Wang,
	Matthias Brugger, AngeloGioacchino Del Regno, Lee Jones,
	Uwe Kleine-König, linux-mediatek, linux-gpio, devicetree,
	linux-arm-kernel, upstream, benjamin.larsson, ansuelsmth,
	linux-pwm

On Sat, Aug 31, 2024 at 04:27:47PM +0200, Lorenzo Bianconi wrote:
> From: Christian Marangi <ansuelsmth@gmail.com>
> 
> Add support for Airoha EN7581 GPIO System Controller which provide a
> register map for controlling the GPIO, pinctrl and PWM of the SoC.
> 
> Schema define cells for both gpio/interrupt controller and PWM.
> Moreover it provides a dedicated pinctrl node for pins and config
> definitions.
> 
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  .../bindings/mfd/airoha,en7581-gpio-sysctl.yaml    | 433 +++++++++++++++++++++
>  1 file changed, 433 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> new file mode 100644
> index 000000000000..a9080c7f50f9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> @@ -0,0 +1,433 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/airoha,en7581-gpio-sysctl.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Airoha EN7581 GPIO System Controller
> +
> +maintainers:
> +  - Christian Marangi <ansuelsmth@gmail.com>
> +  - Lorenzo Bianconi <lorenzo@kernel.org>
> +
> +description:
> +  Airoha EN7581 SoC GPIO system controller which provided a register map
> +  for controlling the GPIO, pins and PWM of the SoC.
> +
> +properties:
> +  compatible:
> +    const: airoha,en7581-gpio-sysctl
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  gpio-controller: true
> +
> +  '#gpio-cells':
> +    const: 2
> +
> +  interrupt-controller: true
> +
> +  '#interrupt-cells':
> +    const: 2
> +
> +  "#pwm-cells":
> +    const: 3
> +
> +  pinctrl:
> +    type: object
> +
> +    $ref: /schemas/pinctrl/pinctrl.yaml
> +
> +    patternProperties:
> +      '-pins$':
> +        type: object
> +
> +        patternProperties:
> +          '^.*mux.*$':

Do you really need 'mux' anywhere in the node names? Isn't either a 
prefix or a suffix enough?

> +            type: object
> +
> +            description:
> +              pinmux configuration nodes.
> +
> +            $ref: /schemas/pinctrl/pinmux-node.yaml
> +
> +            properties:
> +              function:
> +                description:
> +                  A string containing the name of the function to mux to the group.
> +                enum: [pon, tod_1pps, sipo, mdio, uart, i2c, jtag, pcm, spi,
> +                       pcm_spi, i2s, emmc, pnand, pcie_reset, pwm, phy1_led0,
> +                       phy2_led0, phy3_led0, phy4_led0, phy1_led1, phy2_led1,
> +                       phy3_led1, phy4_led1]
> +
> +              groups:
> +                description:
> +                  An array of strings. Each string contains the name of a group.
> +
> +            required:
> +              - function
> +              - groups
> +
> +            allOf:
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pon
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pon]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: tod_1pps
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pon_tod_1pps, gsw_tod_1pps]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: sipo
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [sipo, sipo_rclk]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: mdio
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [mdio]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: uart
> +                then:
> +                  properties:
> +                    groups:
> +                      items:
> +                        enum: [uart2, uart2_cts_rts, hsuart, hsuart_cts_rts, uart4,
> +                               uart5]
> +                      maxItems: 2
> +              - if:
> +                  properties:
> +                    function:
> +                      const: i2c
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [i2c1]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: jtag
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [jtag_udi, jtag_dfd]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pcm
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pcm1, pcm2]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: spi
> +                then:
> +                  properties:
> +                    groups:
> +                      items:
> +                        enum: [spi_quad, spi_cs1]
> +                      maxItems: 2
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pcm_spi
> +                then:
> +                  properties:
> +                    groups:
> +                      items:
> +                        enum: [pcm_spi, pcm_spi_int, pcm_spi_rst, pcm_spi_cs1,
> +                               pcm_spi_cs2_p156, pcm_spi_cs2_p128, pcm_spi_cs3,
> +                               pcm_spi_cs4]
> +                      maxItems: 7
> +              - if:
> +                  properties:
> +                    function:
> +                      const: i2c
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [i2s]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: emmc
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [emmc]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pnand
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pnand]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pcie_reset
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pcie_reset0, pcie_reset1, pcie_reset2]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pwm
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio0, gpio1, gpio2, gpio3, gpio4, gpio5, gpio6,
> +                             gpio7, gpio8, gpio9, gpio10, gpio11, gpio12, gpio13,
> +                             gpio14, gpio15, gpio16, gpio17, gpio18, gpio19,
> +                             gpio20, gpio21, gpio22, gpio23, gpio24, gpio25,
> +                             gpio26, gpio27, gpio28, gpio29, gpio30, gpio31,
> +                             gpio36, gpio37, gpio38, gpio39, gpio40, gpio41,
> +                             gpio42, gpio43, gpio44, gpio45, gpio46, gpio47]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy1_led0
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio33, gpio34, gpio35, gpio42]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy2_led0
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio33, gpio34, gpio35, gpio42]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy3_led0
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio33, gpio34, gpio35, gpio42]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy4_led0
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio33, gpio34, gpio35, gpio42]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy1_led1
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio43, gpio44, gpio45, gpio46]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy2_led1
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio43, gpio44, gpio45, gpio46]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy3_led1
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio43, gpio44, gpio45, gpio46]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy4_led1
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio43, gpio44, gpio45, gpio46]
> +
> +            additionalProperties: false
> +
> +          '^.*conf.*$':

Same here.

Otherwise, LGTM.

Rob

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller
  2024-08-31 14:27 ` [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller Lorenzo Bianconi
  2024-09-03 15:09   ` Rob Herring
@ 2024-09-03 15:33   ` Lee Jones
  2024-09-03 14:00     ` Christian Marangi
  1 sibling, 1 reply; 24+ messages in thread
From: Lee Jones @ 2024-09-03 15:33 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
	Uwe Kleine-König, linux-mediatek, linux-gpio, devicetree,
	linux-arm-kernel, upstream, benjamin.larsson, ansuelsmth,
	linux-pwm

On Sat, 31 Aug 2024, Lorenzo Bianconi wrote:

> From: Christian Marangi <ansuelsmth@gmail.com>
> 
> Add support for Airoha EN7581 GPIO System Controller which provide a
> register map for controlling the GPIO, pinctrl and PWM of the SoC.
> 
> Schema define cells for both gpio/interrupt controller and PWM.
> Moreover it provides a dedicated pinctrl node for pins and config
> definitions.
> 
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  .../bindings/mfd/airoha,en7581-gpio-sysctl.yaml    | 433 +++++++++++++++++++++
>  1 file changed, 433 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> new file mode 100644
> index 000000000000..a9080c7f50f9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> @@ -0,0 +1,433 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/airoha,en7581-gpio-sysctl.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Airoha EN7581 GPIO System Controller
> +
> +maintainers:
> +  - Christian Marangi <ansuelsmth@gmail.com>
> +  - Lorenzo Bianconi <lorenzo@kernel.org>
> +
> +description:
> +  Airoha EN7581 SoC GPIO system controller which provided a register map
> +  for controlling the GPIO, pins and PWM of the SoC.

This whole thing is just about pins.

The MFD portion of this submission doesn't do anything.

Please rework this to omit the MFD driver.

After just a glance, it looks like simple-mfd _might_ work.

> +properties:
> +  compatible:
> +    const: airoha,en7581-gpio-sysctl
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  gpio-controller: true
> +
> +  '#gpio-cells':
> +    const: 2
> +
> +  interrupt-controller: true
> +
> +  '#interrupt-cells':
> +    const: 2
> +
> +  "#pwm-cells":
> +    const: 3
> +
> +  pinctrl:
> +    type: object
> +
> +    $ref: /schemas/pinctrl/pinctrl.yaml
> +
> +    patternProperties:
> +      '-pins$':
> +        type: object
> +
> +        patternProperties:
> +          '^.*mux.*$':
> +            type: object
> +
> +            description:
> +              pinmux configuration nodes.
> +
> +            $ref: /schemas/pinctrl/pinmux-node.yaml
> +
> +            properties:
> +              function:
> +                description:
> +                  A string containing the name of the function to mux to the group.
> +                enum: [pon, tod_1pps, sipo, mdio, uart, i2c, jtag, pcm, spi,
> +                       pcm_spi, i2s, emmc, pnand, pcie_reset, pwm, phy1_led0,
> +                       phy2_led0, phy3_led0, phy4_led0, phy1_led1, phy2_led1,
> +                       phy3_led1, phy4_led1]
> +
> +              groups:
> +                description:
> +                  An array of strings. Each string contains the name of a group.
> +
> +            required:
> +              - function
> +              - groups
> +
> +            allOf:
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pon
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pon]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: tod_1pps
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pon_tod_1pps, gsw_tod_1pps]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: sipo
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [sipo, sipo_rclk]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: mdio
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [mdio]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: uart
> +                then:
> +                  properties:
> +                    groups:
> +                      items:
> +                        enum: [uart2, uart2_cts_rts, hsuart, hsuart_cts_rts, uart4,
> +                               uart5]
> +                      maxItems: 2
> +              - if:
> +                  properties:
> +                    function:
> +                      const: i2c
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [i2c1]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: jtag
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [jtag_udi, jtag_dfd]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pcm
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pcm1, pcm2]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: spi
> +                then:
> +                  properties:
> +                    groups:
> +                      items:
> +                        enum: [spi_quad, spi_cs1]
> +                      maxItems: 2
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pcm_spi
> +                then:
> +                  properties:
> +                    groups:
> +                      items:
> +                        enum: [pcm_spi, pcm_spi_int, pcm_spi_rst, pcm_spi_cs1,
> +                               pcm_spi_cs2_p156, pcm_spi_cs2_p128, pcm_spi_cs3,
> +                               pcm_spi_cs4]
> +                      maxItems: 7
> +              - if:
> +                  properties:
> +                    function:
> +                      const: i2c
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [i2s]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: emmc
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [emmc]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pnand
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pnand]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pcie_reset
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [pcie_reset0, pcie_reset1, pcie_reset2]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: pwm
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio0, gpio1, gpio2, gpio3, gpio4, gpio5, gpio6,
> +                             gpio7, gpio8, gpio9, gpio10, gpio11, gpio12, gpio13,
> +                             gpio14, gpio15, gpio16, gpio17, gpio18, gpio19,
> +                             gpio20, gpio21, gpio22, gpio23, gpio24, gpio25,
> +                             gpio26, gpio27, gpio28, gpio29, gpio30, gpio31,
> +                             gpio36, gpio37, gpio38, gpio39, gpio40, gpio41,
> +                             gpio42, gpio43, gpio44, gpio45, gpio46, gpio47]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy1_led0
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio33, gpio34, gpio35, gpio42]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy2_led0
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio33, gpio34, gpio35, gpio42]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy3_led0
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio33, gpio34, gpio35, gpio42]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy4_led0
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio33, gpio34, gpio35, gpio42]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy1_led1
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio43, gpio44, gpio45, gpio46]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy2_led1
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio43, gpio44, gpio45, gpio46]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy3_led1
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio43, gpio44, gpio45, gpio46]
> +              - if:
> +                  properties:
> +                    function:
> +                      const: phy4_led1
> +                then:
> +                  properties:
> +                    groups:
> +                      enum: [gpio43, gpio44, gpio45, gpio46]
> +
> +            additionalProperties: false
> +
> +          '^.*conf.*$':
> +            type: object
> +
> +            description:
> +              pinconf configuration nodes.
> +
> +            $ref: /schemas/pinctrl/pincfg-node.yaml
> +
> +            properties:
> +              pins:
> +                description:
> +                  An array of strings. Each string contains the name of a pin.
> +                items:
> +                  enum: [uart1_txd, uart1_rxd, i2c_scl, i2c_sda, spi_cs0, spi_clk,
> +                         spi_mosi, spi_miso, gpio0, gpio1, gpio2, gpio3, gpio4,
> +                         gpio5, gpio6, gpio7, gpio8, gpio9, gpio10, gpio11, gpio12,
> +                         gpio13, gpio14, gpio15, gpio16, gpio17, gpio18, gpio19,
> +                         gpio20, gpio21, gpio22, gpio23, gpio24, gpio25, gpio26,
> +                         gpio27, gpio28, gpio29, gpio30, gpio31, gpio32, gpio33,
> +                         gpio34, gpio35, gpio36, gpio37, gpio38, gpio39, gpio40,
> +                         gpio41, gpio42, gpio43, gpio44, gpio45, gpio46,
> +                         pcie_reset0, pcie_reset1, pcie_reset2]
> +                minItems: 1
> +                maxItems: 58
> +
> +              bias-disable: true
> +
> +              bias-pull-up: true
> +
> +              bias-pull-down: true
> +
> +              input-enable: true
> +
> +              output-enable: true
> +
> +              output-low: true
> +
> +              output-high: true
> +
> +              drive-open-drain: true
> +
> +              drive-strength:
> +                description:
> +                  Selects the drive strength for MIO pins, in mA.
> +                enum: [2, 4, 6, 8]
> +
> +            required:
> +              - pins
> +
> +            additionalProperties: false
> +
> +        additionalProperties: false
> +
> +    additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - gpio-controller
> +  - "#gpio-cells"
> +  - "#pwm-cells"
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    mfd@1fbf0200 {
> +        compatible = "airoha,en7581-gpio-sysctl";
> +        reg = <0x1fbf0200 0xc0>;
> +
> +        interrupt-parent = <&gic>;
> +        interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
> +
> +        gpio-controller;
> +        #gpio-cells = <2>;
> +
> +        interrupt-controller;
> +        #interrupt-cells = <2>;
> +
> +        #pwm-cells = <3>;
> +
> +        pinctrl {
> +            pcie1-rst-pins {
> +                conf {
> +                    pins = "pcie_reset1";
> +                    drive-open-drain = <1>;
> +                };
> +            };
> +
> +            pwm-pins {
> +                mux {
> +                    function = "pwm";
> +                    groups = "gpio18";
> +                };
> +            };
> +
> +            spi-pins {
> +                mux {
> +                    function = "spi";
> +                    groups = "spi_quad", "spi_cs1";
> +                };
> +            };
> +
> +            uart2-pins {
> +                mux {
> +                    function = "uart";
> +                    groups = "uart2", "uart2_cts_rts";
> +                };
> +            };
> +
> +            uar5-pins {
> +                mux {
> +                    function = "uart";
> +                    groups = "uart5";
> +                };
> +            };
> +
> +            mmc-pins {
> +                mux {
> +                    function = "emmc";
> +                    groups = "emmc";
> +                };
> +            };
> +
> +            mdio-pins {
> +                mux {
> +                    function = "mdio";
> +                    groups = "mdio";
> +                };
> +
> +                conf {
> +                    pins = "gpio2";
> +                    output-enable;
> +                };
> +            };
> +
> +            gswp1-led0-pins {
> +                mux {
> +                    function = "phy1_led0";
> +                    groups = "gpio33";
> +                };
> +            };
> +
> +            gswp2-led1-pins {
> +                mux {
> +                    function = "phy2_led1";
> +                    groups = "gpio44";
> +                };
> +            };
> +        };
> +    };
> +
> +...
> 
> -- 
> 2.46.0
> 

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-09-03 11:58     ` Benjamin Larsson
@ 2024-09-03 15:47       ` Uwe Kleine-König
  2024-09-04 23:09         ` Benjamin Larsson
  0 siblings, 1 reply; 24+ messages in thread
From: Uwe Kleine-König @ 2024-09-03 15:47 UTC (permalink / raw)
  To: Benjamin Larsson
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-mediatek, linux-gpio,
	devicetree, linux-arm-kernel, upstream, ansuelsmth, linux-pwm

[-- Attachment #1: Type: text/plain, Size: 3170 bytes --]

Hello Benjamin,

On Tue, Sep 03, 2024 at 01:58:30PM +0200, Benjamin Larsson wrote:
> On 2024-09-03 12:46, Uwe Kleine-König wrote:
> > Would you please add a "Limitations" paragraph here covering the
> > following questions:
> > 
> >   - How does the hardware behave on changes of configuration (does it
> >     complete the currently running period? Are there any glitches?)
> >   - How does the hardware behave on disabling?
> > 
> > Please stick to the format used in several other drivers such that
> > 
> > 	sed -rn '/Limitations:/,/\*\/?$/p' drivers/pwm/*.c
> > 
> > emits the informations.
> 
> The answer to your questions are currently unknown. Is this information
> needed for a merge of the driver ?

It would be very welcome and typically isn't that hard to work out if
you have an LED connected to the output or a similar means to observe
the output. An oscilloscope makes it still easier.

For example to check if the current period is completed configure the
PWM with period = 1s and duty_cycle = 0 disabling the LED. (I leave it
as an exercise for the reader what to do if duty_cycle = 0 enables the
LED :-) Then do:

	pwm_apply_might_sleep(mypwm, &(struct pwm_state){
		.period = NSEC_PER_SEC,
		.duty_cycle = NSEC_PER_SEC,
		.enabled = true,
	});
	pwm_apply_might_sleep(mypwm, &(struct pwm_state){
		.period = NSEC_PER_SEC,
		.duty_cycle = 0,
		.enabled = true,
	});

Iff that enables the LED for a second, the period is completed. The
question about glitches is a bit harder to answer, but with a tool like
memtool should be possible to answer. Alternatively add delays and
printk output to .apply() in the critical places.

> > > +#define airoha_pwm_sgpio_clear(pc, offset, mask)				\
> > > +	airoha_pwm_sgpio_rmw((pc), (offset), (mask), 0)
> > > +#define airoha_pwm_flash_set(pc, offset, val)					\
> > > +	airoha_pwm_flash_rmw((pc), (offset), 0, (val))
> > > +#define airoha_pwm_flash_clear(pc, offset, mask)				\
> > > +	airoha_pwm_flash_rmw((pc), (offset), (mask), 0)
> > > +
> > > +static int airoha_pwm_get_waveform(struct airoha_pwm *pc, u64 duty_ns,
> > > +				   u64 period_ns)
> > Given that "waveform" will gain some specific semantic soon, but this
> > usage is different, I'd like to see this function renamed.
> 
> I suggest pwm_generator. Is that acceptable ?

This function determines the register values to write for a given
duty_ns + period_ns combination, right? airoha_pwm_calc_bucket_config()?
 
> > If you limit the number of requested pwm devices to 8, the code gets a
> > tad simpler (because you can map a fixed bucket to each pwm device and
> > don't need to search during .apply()) and each consumer has maximal
> > freedom to configure its device.
> 
> The main use for this solution is for led-dimming which uses the same timing
> among groups of leds. Most (of our) products have more then 8 leds in total,
> with a limit of only 8 pwm devices it would not be possible to use the
> mainline driver with our hardware. I suggest that the current logic is kept
> but properly documented in the limitations section.

Fine for me.

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller
  2024-09-03 14:00     ` Christian Marangi
@ 2024-09-03 16:42       ` Lee Jones
  2024-09-04 11:06         ` Christian Marangi
  0 siblings, 1 reply; 24+ messages in thread
From: Lee Jones @ 2024-09-03 16:42 UTC (permalink / raw)
  To: Christian Marangi
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Uwe Kleine-König, linux-mediatek,
	linux-gpio, devicetree, linux-arm-kernel, upstream,
	benjamin.larsson, linux-pwm

On Tue, 03 Sep 2024, Christian Marangi wrote:

> On Tue, Sep 03, 2024 at 04:33:53PM +0100, Lee Jones wrote:
> > On Sat, 31 Aug 2024, Lorenzo Bianconi wrote:
> > 
> > > From: Christian Marangi <ansuelsmth@gmail.com>
> > > 
> > > Add support for Airoha EN7581 GPIO System Controller which provide a
> > > register map for controlling the GPIO, pinctrl and PWM of the SoC.
> > > 
> > > Schema define cells for both gpio/interrupt controller and PWM.
> > > Moreover it provides a dedicated pinctrl node for pins and config
> > > definitions.
> > > 
> > > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > > Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > > ---
> > >  .../bindings/mfd/airoha,en7581-gpio-sysctl.yaml    | 433 +++++++++++++++++++++
> > >  1 file changed, 433 insertions(+)
> > > 
> > > diff --git a/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> > > new file mode 100644
> > > index 000000000000..a9080c7f50f9
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> > > @@ -0,0 +1,433 @@
> > > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/mfd/airoha,en7581-gpio-sysctl.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Airoha EN7581 GPIO System Controller
> > > +
> > > +maintainers:
> > > +  - Christian Marangi <ansuelsmth@gmail.com>
> > > +  - Lorenzo Bianconi <lorenzo@kernel.org>
> > > +
> > > +description:
> > > +  Airoha EN7581 SoC GPIO system controller which provided a register map
> > > +  for controlling the GPIO, pins and PWM of the SoC.
> > 
> > This whole thing is just about pins.
> > 
> > The MFD portion of this submission doesn't do anything.
> >
> 
> Hi Lee,
> 
> thanks for the review. I think you missed the other series as it was
> requested to use MFD implementation due to shared register map.
> 
> > Please rework this to omit the MFD driver.
> 
> I'm a bit confused by this you mean in the schema? Putting PWM property
> in a pinctrl schema looks wrong to me :(
> 
> > 
> > After just a glance, it looks like simple-mfd _might_ work.
> 
> Simple-mfd works if register map are well defined and you can have
> something like
> - parent define the whole register
> - child can user reg property to register offset and subsection of the
>   parent register
> 
> Here it's all mixed and scrambled and also it was requested to have a
> very simple node that include both pwm and pinctrl property (cause that
> is how the HW register block is designed and schema must reflect HW)
> 
> Hope you can understand these reasons.

Thinking very quickly before I have to rush off.

Have you considered syscon?

-- 
Lee Jones [李琼斯]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller
  2024-09-03 16:42       ` Lee Jones
@ 2024-09-04 11:06         ` Christian Marangi
  0 siblings, 0 replies; 24+ messages in thread
From: Christian Marangi @ 2024-09-04 11:06 UTC (permalink / raw)
  To: Lee Jones
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Uwe Kleine-König, linux-mediatek,
	linux-gpio, devicetree, linux-arm-kernel, upstream,
	benjamin.larsson, linux-pwm

On Tue, Sep 03, 2024 at 05:42:18PM +0100, Lee Jones wrote:
> On Tue, 03 Sep 2024, Christian Marangi wrote:
> 
> > On Tue, Sep 03, 2024 at 04:33:53PM +0100, Lee Jones wrote:
> > > On Sat, 31 Aug 2024, Lorenzo Bianconi wrote:
> > > 
> > > > From: Christian Marangi <ansuelsmth@gmail.com>
> > > > 
> > > > Add support for Airoha EN7581 GPIO System Controller which provide a
> > > > register map for controlling the GPIO, pinctrl and PWM of the SoC.
> > > > 
> > > > Schema define cells for both gpio/interrupt controller and PWM.
> > > > Moreover it provides a dedicated pinctrl node for pins and config
> > > > definitions.
> > > > 
> > > > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > > > Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > > > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > > > ---
> > > >  .../bindings/mfd/airoha,en7581-gpio-sysctl.yaml    | 433 +++++++++++++++++++++
> > > >  1 file changed, 433 insertions(+)
> > > > 
> > > > diff --git a/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> > > > new file mode 100644
> > > > index 000000000000..a9080c7f50f9
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml
> > > > @@ -0,0 +1,433 @@
> > > > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/mfd/airoha,en7581-gpio-sysctl.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: Airoha EN7581 GPIO System Controller
> > > > +
> > > > +maintainers:
> > > > +  - Christian Marangi <ansuelsmth@gmail.com>
> > > > +  - Lorenzo Bianconi <lorenzo@kernel.org>
> > > > +
> > > > +description:
> > > > +  Airoha EN7581 SoC GPIO system controller which provided a register map
> > > > +  for controlling the GPIO, pins and PWM of the SoC.
> > > 
> > > This whole thing is just about pins.
> > > 
> > > The MFD portion of this submission doesn't do anything.
> > >
> > 
> > Hi Lee,
> > 
> > thanks for the review. I think you missed the other series as it was
> > requested to use MFD implementation due to shared register map.
> > 
> > > Please rework this to omit the MFD driver.
> > 
> > I'm a bit confused by this you mean in the schema? Putting PWM property
> > in a pinctrl schema looks wrong to me :(
> > 
> > > 
> > > After just a glance, it looks like simple-mfd _might_ work.
> > 
> > Simple-mfd works if register map are well defined and you can have
> > something like
> > - parent define the whole register
> > - child can user reg property to register offset and subsection of the
> >   parent register
> > 
> > Here it's all mixed and scrambled and also it was requested to have a
> > very simple node that include both pwm and pinctrl property (cause that
> > is how the HW register block is designed and schema must reflect HW)
> > 
> > Hope you can understand these reasons.
> 
> Thinking very quickly before I have to rush off.
> 
> Have you considered syscon?
>

Yes it was considered and proposed but it's not suitable for what it was
suggested from DT guy. A syscon implementation would require adding
child nodes with compatible (to probe driver that way) and that got
rejected.

The main reason is that it's wrong to create .yaml that describe how
the driver structure works (mfd + child nodes with compatible) instead
of describing how the actual HW works (single register map that expose
both GPIO/PINCTRL and PWM)

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-09-03 15:47       ` Uwe Kleine-König
@ 2024-09-04 23:09         ` Benjamin Larsson
  2024-09-05  9:30           ` Uwe Kleine-König
  0 siblings, 1 reply; 24+ messages in thread
From: Benjamin Larsson @ 2024-09-04 23:09 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-mediatek, linux-gpio,
	devicetree, linux-arm-kernel, upstream, ansuelsmth, linux-pwm

Hi.

On 03/09/2024 17:47, Uwe Kleine-König wrote:
> Hello Benjamin,
>
> On Tue, Sep 03, 2024 at 01:58:30PM +0200, Benjamin Larsson wrote:
>> On 2024-09-03 12:46, Uwe Kleine-König wrote:
>>> Would you please add a "Limitations" paragraph here covering the
>>> following questions:
>>>
>>>    - How does the hardware behave on changes of configuration (does it
>>>      complete the currently running period? Are there any glitches?)
>>>    - How does the hardware behave on disabling?
>>>
>>> Please stick to the format used in several other drivers such that
>>>
>>> 	sed -rn '/Limitations:/,/\*\/?$/p' drivers/pwm/*.c
>>>
>>> emits the informations.
>> The answer to your questions are currently unknown. Is this information
>> needed for a merge of the driver ?
> It would be very welcome and typically isn't that hard to work out if
> you have an LED connected to the output or a similar means to observe
> the output. An oscilloscope makes it still easier.
>
> For example to check if the current period is completed configure the
> PWM with period = 1s and duty_cycle = 0 disabling the LED. (I leave it
> as an exercise for the reader what to do if duty_cycle = 0 enables the
> LED :-) Then do:
>
> 	pwm_apply_might_sleep(mypwm, &(struct pwm_state){
> 		.period = NSEC_PER_SEC,
> 		.duty_cycle = NSEC_PER_SEC,
> 		.enabled = true,
> 	});
> 	pwm_apply_might_sleep(mypwm, &(struct pwm_state){
> 		.period = NSEC_PER_SEC,
> 		.duty_cycle = 0,
> 		.enabled = true,
> 	});
>
> Iff that enables the LED for a second, the period is completed. The
> question about glitches is a bit harder to answer, but with a tool like
> memtool should be possible to answer. Alternatively add delays and
> printk output to .apply() in the critical places.
>
>

I connected a logic analyzer to a pin and configured the pwm for it.

I then configured the pwm with these parameters (setup for 2Hz).

echo 1000000000 > /sys/class/pwm/pwmchip0/pwm12/period
echo 0 > /sys/class/pwm/pwmchip0/pwm12/duty_cycle

If I then ran the following (in a script) no pulse was detected:

echo 500000000 > /sys/class/pwm/pwmchip0/pwm12/duty_cycle
echo 0 > /sys/class/pwm/pwmchip0/pwm12/duty_cycle

If I added a sleep 1 in between I always got 1 500ms pulse.

I then did the same but with direct register access with the same 
result. Setting the duty cycle to 0 disables the pwm function on the 
pin, it seems to take a while before it properly activates but before it 
disables it the cycle completes.


I also tested with enabling the pwn signal and then setting a 0 duty 
cycle. The last observed pulse was always 500ms long.


I am not sure what of your questions this answers and is there some 
other tests I should perform ?

For the record while toggling the registers I noticed that it was 
actually possible to generate 1 second long pulses. The documentation is 
not clear on this part.

MvH

Benjamin Larsson


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-09-04 23:09         ` Benjamin Larsson
@ 2024-09-05  9:30           ` Uwe Kleine-König
  2024-09-05 12:18             ` Benjamin Larsson
  0 siblings, 1 reply; 24+ messages in thread
From: Uwe Kleine-König @ 2024-09-05  9:30 UTC (permalink / raw)
  To: Benjamin Larsson
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-mediatek, linux-gpio,
	devicetree, linux-arm-kernel, upstream, ansuelsmth, linux-pwm

[-- Attachment #1: Type: text/plain, Size: 3748 bytes --]

Hello,

On Thu, Sep 05, 2024 at 01:09:48AM +0200, Benjamin Larsson wrote:
> On 03/09/2024 17:47, Uwe Kleine-König wrote:
> > Hello Benjamin,
> > 
> > On Tue, Sep 03, 2024 at 01:58:30PM +0200, Benjamin Larsson wrote:
> > > On 2024-09-03 12:46, Uwe Kleine-König wrote:
> > > > Would you please add a "Limitations" paragraph here covering the
> > > > following questions:
> > > > 
> > > >    - How does the hardware behave on changes of configuration (does it
> > > >      complete the currently running period? Are there any glitches?)
> > > >    - How does the hardware behave on disabling?
> > > > 
> > > > Please stick to the format used in several other drivers such that
> > > > 
> > > > 	sed -rn '/Limitations:/,/\*\/?$/p' drivers/pwm/*.c
> > > > 
> > > > emits the informations.
> > > The answer to your questions are currently unknown. Is this information
> > > needed for a merge of the driver ?
> > It would be very welcome and typically isn't that hard to work out if
> > you have an LED connected to the output or a similar means to observe
> > the output. An oscilloscope makes it still easier.
> > 
> > For example to check if the current period is completed configure the
> > PWM with period = 1s and duty_cycle = 0 disabling the LED. (I leave it
> > as an exercise for the reader what to do if duty_cycle = 0 enables the
> > LED :-) Then do:
> > 
> > 	pwm_apply_might_sleep(mypwm, &(struct pwm_state){
> > 		.period = NSEC_PER_SEC,
> > 		.duty_cycle = NSEC_PER_SEC,
> > 		.enabled = true,
> > 	});
> > 	pwm_apply_might_sleep(mypwm, &(struct pwm_state){
> > 		.period = NSEC_PER_SEC,
> > 		.duty_cycle = 0,
> > 		.enabled = true,
> > 	});
> > 
> > Iff that enables the LED for a second, the period is completed. The
> > question about glitches is a bit harder to answer, but with a tool like
> > memtool should be possible to answer. Alternatively add delays and
> > printk output to .apply() in the critical places.
> > 
> > 
> 
> I connected a logic analyzer to a pin and configured the pwm for it.
> 
> I then configured the pwm with these parameters (setup for 2Hz).
> 
> echo 1000000000 > /sys/class/pwm/pwmchip0/pwm12/period
> echo 0 > /sys/class/pwm/pwmchip0/pwm12/duty_cycle
> 
> If I then ran the following (in a script) no pulse was detected:
> 
> echo 500000000 > /sys/class/pwm/pwmchip0/pwm12/duty_cycle
> echo 0 > /sys/class/pwm/pwmchip0/pwm12/duty_cycle
> 
> If I added a sleep 1 in between I always got 1 500ms pulse.
> 
> I then did the same but with direct register access with the same result.
> Setting the duty cycle to 0 disables the pwm function on the pin, it seems
> to take a while before it properly activates but before it disables it the
> cycle completes.
> 
> 
> I also tested with enabling the pwn signal and then setting a 0 duty cycle.
> The last observed pulse was always 500ms long.
> 
> 
> I am not sure what of your questions this answers and is there some other
> tests I should perform ?

IIUC that means to add:

	On configuration the currently running period is completed.

to the Limitations paragraph.

> For the record while toggling the registers I noticed that it was actually
> possible to generate 1 second long pulses. The documentation is not clear on
> this part.

1 second long pulses with a period size of 1 second, so a constant high
signal?

Another thing that would be interesting is, if it can happen that you
get a mixed signal. That is, if you update from 

	.period = A
	.duty_cycle = B

to

	.period = C
	.duty_cycle = D

that you get one period with length C and duty_cycle B when the period
completes after configuring period but before duty_cycle.

Best regards
UWe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-09-05  9:30           ` Uwe Kleine-König
@ 2024-09-05 12:18             ` Benjamin Larsson
  2024-09-05 15:39               ` Uwe Kleine-König
  0 siblings, 1 reply; 24+ messages in thread
From: Benjamin Larsson @ 2024-09-05 12:18 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-mediatek, linux-gpio,
	devicetree, linux-arm-kernel, upstream, ansuelsmth, linux-pwm

On 2024-09-05 11:30, Uwe Kleine-König wrote:
> 1 second long pulses with a period size of 1 second, so a constant high
> signal?

Hi, I think I was unclear. The SoC documentation is not that detailed. 
But I think I understand how it works now.

One register contains the minimum duration (d_min). And then there is 
one 8 bit register for the signal low period (lp) and then another 8bit 
register for the high period (hp). Per my understanding a change of 
polarity is then just a swap of lp and hp.

The period is d_min * (lp + hp) and duty_cycle (on time) is then 
d_min*hp (per my understanding of the linux api). This means that there 
can be different settings that result in the same pwm signal (if you 
double d_min and halving lp and hp the signal should be the same).

This means that when requesting a period and duty cycle you need to 
search through the configuration space to find the optimal value.

>
> Another thing that would be interesting is, if it can happen that you
> get a mixed signal. That is, if you update from
>
> 	.period = A
> 	.duty_cycle = B
>
> to
>
> 	.period = C
> 	.duty_cycle = D
>
> that you get one period with length C and duty_cycle B when the period
> completes after configuring period but before duty_cycle.
>
> Best regards
> UWe

I will perform this test also.

MvH

Benjamin Larsson


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-09-05 12:18             ` Benjamin Larsson
@ 2024-09-05 15:39               ` Uwe Kleine-König
  2024-09-05 18:35                 ` Benjamin Larsson
  0 siblings, 1 reply; 24+ messages in thread
From: Uwe Kleine-König @ 2024-09-05 15:39 UTC (permalink / raw)
  To: Benjamin Larsson
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-mediatek, linux-gpio,
	devicetree, linux-arm-kernel, upstream, ansuelsmth, linux-pwm

[-- Attachment #1: Type: text/plain, Size: 2629 bytes --]

Hello Benjamin,

On Thu, Sep 05, 2024 at 02:18:41PM +0200, Benjamin Larsson wrote:
> On 2024-09-05 11:30, Uwe Kleine-König wrote:
> > 1 second long pulses with a period size of 1 second, so a constant high
> > signal?
> 
> Hi, I think I was unclear. The SoC documentation is not that detailed. But I
> think I understand how it works now.
> 
> One register contains the minimum duration (d_min). And then there is one 8
> bit register for the signal low period (lp) and then another 8bit register
> for the high period (hp). Per my understanding a change of polarity is then
> just a swap of lp and hp.

that doesn't sound right.

A "normal" waveform with period = 10 ns and duty_cycle = 2 ns looks as
follows:

   _         _         _
  / \_______/ \_______/ \_______/ 
  ^         ^         ^         ^

assuming a monospace font that's 1 char per ns, the ^ marking the period
start.

Ignoring scaling, your hardware needs to have hp = 2 and lp = 8. If you
switch that (assuming you mean switching in the same way as I do) to hp
= 8 and lp = 2, you get:

   _______   _______   _______
  /       \_/       \_/       \_/
  ^         ^         ^         ^

which is still a "normal" polarity output as a period starts with the
active part.

I admit that's a bit artificial, because the waveform for

	period = 10 ns
	duty_cycle = 2 ns
	polarity = inversed

looks as follows:

     _______   _______   _______
  \_/       \_/       \_/       \_/
  ^         ^         ^         ^

which isn't any different from the 2nd waveform above if you ignore the
period start markers (which are not observable apart from the moments
where you reconfigure the output).

However it matters if you have a chip with >1 output that are not
independent.

> The period is d_min * (lp + hp) and duty_cycle (on time) is then d_min*hp
> (per my understanding of the linux api). This means that there can be
> different settings that result in the same pwm signal (if you double d_min
> and halving lp and hp the signal should be the same).

Sounds correct.

> This means that when requesting a period and duty cycle you need to search
> through the configuration space to find the optimal value.

Or restrict yourself consistently to something simpler than a exhaustive
search through the complete configuration space.

> MvH

(BTW, I had to research the meaning of MvH. In case someone else doesn't
know it: It's the usual abbreviation for "Med vänliga hälsningar" in
Sweden or "Med vennlig hilsen" in Norway; both meaning "With friendly
greetings".)

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-09-05 15:39               ` Uwe Kleine-König
@ 2024-09-05 18:35                 ` Benjamin Larsson
  2024-09-06  8:01                   ` Uwe Kleine-König
  0 siblings, 1 reply; 24+ messages in thread
From: Benjamin Larsson @ 2024-09-05 18:35 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-mediatek, linux-gpio,
	devicetree, linux-arm-kernel, upstream, ansuelsmth, linux-pwm

Hi.

On 05/09/2024 17:39, Uwe Kleine-König wrote:
> Hello Benjamin,
>
> On Thu, Sep 05, 2024 at 02:18:41PM +0200, Benjamin Larsson wrote:
>> On 2024-09-05 11:30, Uwe Kleine-König wrote:
>>> 1 second long pulses with a period size of 1 second, so a constant high
>>> signal?
>> Hi, I think I was unclear. The SoC documentation is not that detailed. But I
>> think I understand how it works now.
>>
>> One register contains the minimum duration (d_min). And then there is one 8
>> bit register for the signal low period (lp) and then another 8bit register
>> for the high period (hp). Per my understanding a change of polarity is then
>> just a swap of lp and hp.
> that doesn't sound right.
>
> A "normal" waveform with period = 10 ns and duty_cycle = 2 ns looks as
> follows:
>
>     _         _         _
>    / \_______/ \_______/ \_______/
>    ^         ^         ^         ^
>
> assuming a monospace font that's 1 char per ns, the ^ marking the period
> start.
>
> Ignoring scaling, your hardware needs to have hp = 2 and lp = 8. If you
> switch that (assuming you mean switching in the same way as I do) to hp
> = 8 and lp = 2, you get:
>
>     _______   _______   _______
>    /       \_/       \_/       \_/
>    ^         ^         ^         ^
>
> which is still a "normal" polarity output as a period starts with the
> active part.
>
> I admit that's a bit artificial, because the waveform for
>
> 	period = 10 ns
> 	duty_cycle = 2 ns
> 	polarity = inversed
>
> looks as follows:
>
>       _______   _______   _______
>    \_/       \_/       \_/       \_/
>    ^         ^         ^         ^
>
> which isn't any different from the 2nd waveform above if you ignore the
> period start markers (which are not observable apart from the moments
> where you reconfigure the output).
>
> However it matters if you have a chip with >1 output that are not
> independent.


Ok that was a clear explanation, anyway the pwm hardware is then not 
capable of a polarity change. It is possible to change the polarity via 
other means but there is no way for the pwm block (and driver) to handle 
this.


>> This means that when requesting a period and duty cycle you need to search
>> through the configuration space to find the optimal value.
> Or restrict yourself consistently to something simpler than a exhaustive
> search through the complete configuration space.

Is there a recommendation on what is more important? Period duration or 
duty cycle percentage?


>> MvH
> (BTW, I had to research the meaning of MvH. In case someone else doesn't
> know it: It's the usual abbreviation for "Med vänliga hälsningar" in
> Sweden or "Med vennlig hilsen" in Norway; both meaning "With friendly
> greetings".)
>
> Best regards
> Uwe

MvH

Benjamin Larsson


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 5/5] pwm: airoha: Add support for EN7581 SoC
  2024-09-05 18:35                 ` Benjamin Larsson
@ 2024-09-06  8:01                   ` Uwe Kleine-König
  0 siblings, 0 replies; 24+ messages in thread
From: Uwe Kleine-König @ 2024-09-06  8:01 UTC (permalink / raw)
  To: Benjamin Larsson
  Cc: Lorenzo Bianconi, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-mediatek, linux-gpio,
	devicetree, linux-arm-kernel, upstream, ansuelsmth, linux-pwm

[-- Attachment #1: Type: text/plain, Size: 3392 bytes --]

Hello Benjamin,

On Thu, Sep 05, 2024 at 08:35:17PM +0200, Benjamin Larsson wrote:
> On 05/09/2024 17:39, Uwe Kleine-König wrote:
> > On Thu, Sep 05, 2024 at 02:18:41PM +0200, Benjamin Larsson wrote:
> > > On 2024-09-05 11:30, Uwe Kleine-König wrote:
> > > > 1 second long pulses with a period size of 1 second, so a constant high
> > > > signal?
> > > Hi, I think I was unclear. The SoC documentation is not that detailed. But I
> > > think I understand how it works now.
> > > 
> > > One register contains the minimum duration (d_min). And then there is one 8
> > > bit register for the signal low period (lp) and then another 8bit register
> > > for the high period (hp). Per my understanding a change of polarity is then
> > > just a swap of lp and hp.
> > that doesn't sound right.
> > 
> > A "normal" waveform with period = 10 ns and duty_cycle = 2 ns looks as
> > follows:
> > 
> >     _         _         _
> >    / \_______/ \_______/ \_______/
> >    ^         ^         ^         ^
> > 
> > assuming a monospace font that's 1 char per ns, the ^ marking the period
> > start.
> > 
> > Ignoring scaling, your hardware needs to have hp = 2 and lp = 8. If you
> > switch that (assuming you mean switching in the same way as I do) to hp
> > = 8 and lp = 2, you get:
> > 
> >     _______   _______   _______
> >    /       \_/       \_/       \_/
> >    ^         ^         ^         ^
> > 
> > which is still a "normal" polarity output as a period starts with the
> > active part.
> > 
> > I admit that's a bit artificial, because the waveform for
> > 
> > 	period = 10 ns
> > 	duty_cycle = 2 ns
> > 	polarity = inversed
> > 
> > looks as follows:
> > 
> >       _______   _______   _______
> >    \_/       \_/       \_/       \_/
> >    ^         ^         ^         ^
> > 
> > which isn't any different from the 2nd waveform above if you ignore the
> > period start markers (which are not observable apart from the moments
> > where you reconfigure the output).
> > 
> > However it matters if you have a chip with >1 output that are not
> > independent.
> 
> 
> Ok that was a clear explanation,

\o/

> anyway the pwm hardware is then not capable
> of a polarity change. It is possible to change the polarity via other means
> but there is no way for the pwm block (and driver) to handle this.

That's ok. Just do something like

	if (state->polarity != PWM_POLARITY_NORMAL)
		return -EINVAL;

It's quite usual that drivers only support a single polarity.

> > > This means that when requesting a period and duty cycle you need to search
> > > through the configuration space to find the optimal value.
> > Or restrict yourself consistently to something simpler than a exhaustive
> > search through the complete configuration space.
> 
> Is there a recommendation on what is more important? Period duration or duty
> cycle percentage?

That really depends on your usage domain. Just pick an algorithm that is
sound, ideally easy to review and serves your purpose. If you pick
something that is too simple for the next consumer, we can add the
needed complexity still later.

So in my book even something like restricting the period to a single
fixed value and just modify the duty cycle is fine. In that case add a
comment that there is room for improvement and I'm happy.

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2024-09-06  8:01 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-31 14:27 [PATCH v3 0/5] Add mfd, pinctrl and pwm support to EN7581 SoC Lorenzo Bianconi
2024-08-31 14:27 ` [PATCH v3 1/5] dt-bindings: arm: airoha: Add the chip-scu node for " Lorenzo Bianconi
2024-09-03 15:05   ` Rob Herring (Arm)
2024-08-31 14:27 ` [PATCH v3 2/5] dt-bindings: mfd: Add support for Airoha EN7581 GPIO System Controller Lorenzo Bianconi
2024-09-03 15:09   ` Rob Herring
2024-09-03 15:33   ` Lee Jones
2024-09-03 14:00     ` Christian Marangi
2024-09-03 16:42       ` Lee Jones
2024-09-04 11:06         ` Christian Marangi
2024-08-31 14:27 ` [PATCH v3 3/5] mfd: airoha: Add support for Airoha EN7581 MFD Lorenzo Bianconi
2024-08-31 14:27 ` [PATCH v3 4/5] pinctrl: airoha: Add support for EN7581 SoC Lorenzo Bianconi
2024-08-31 17:35   ` kernel test robot
2024-09-01 12:46   ` kernel test robot
2024-09-01 13:28   ` kernel test robot
2024-08-31 14:27 ` [PATCH v3 5/5] pwm: " Lorenzo Bianconi
2024-09-03 10:46   ` Uwe Kleine-König
2024-09-03 11:58     ` Benjamin Larsson
2024-09-03 15:47       ` Uwe Kleine-König
2024-09-04 23:09         ` Benjamin Larsson
2024-09-05  9:30           ` Uwe Kleine-König
2024-09-05 12:18             ` Benjamin Larsson
2024-09-05 15:39               ` Uwe Kleine-König
2024-09-05 18:35                 ` Benjamin Larsson
2024-09-06  8:01                   ` Uwe Kleine-König

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).