* [PATCH v7 1/6] dt-bindings: leds: Binding for RTL8231 scan matrix
2025-11-17 21:51 [PATCH v7 0/6] RTL8231 GPIO expander support Sander Vanheule
@ 2025-11-17 21:51 ` Sander Vanheule
2025-11-18 14:56 ` Rob Herring
2025-11-18 14:58 ` Rob Herring
2025-11-17 21:51 ` [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231 Sander Vanheule
` (4 subsequent siblings)
5 siblings, 2 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-17 21:51 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Linus Walleij, Michael Walle, Bartosz Golaszewski
Cc: linux-leds, devicetree, linux-kernel, linux-gpio, Sander Vanheule
Add a binding description for the Realtek RTL8231's LED support, which
consists of up to 88 LEDs arranged in a number of scanning matrices.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
Changes since v6:
- Relax description formatting
- Enforce address format for led node names
- Use absolute paths for schema references
---
.../bindings/leds/realtek,rtl8231-leds.yaml | 167 ++++++++++++++++++
1 file changed, 167 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
diff --git a/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml b/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
new file mode 100644
index 000000000000..222cd08914da
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
@@ -0,0 +1,167 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RTL8231 LED scan matrix.
+
+maintainers:
+ - Sander Vanheule <sander@svanheule.net>
+
+description: |
+ The RTL8231 has support for driving a number of LED matrices, by scanning
+ over the LEDs pins, alternatingly lighting different columns and/or rows.
+
+ This functionality is available on an RTL8231, when it is configured for use
+ as an MDIO device, or SMI device.
+
+ In single color scan mode, 88 LEDs are supported. These are grouped into
+ three output matrices:
+ - Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO
+ pins 0-11.
+ L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]
+ | | | | | |
+ P0/P6 --<--------<--------<--------<--------<--------< (3)
+ | | | | | |
+ P1/P7 --<--------<--------<--------<--------<--------< (4)
+ | | | | | |
+ P2/P8 --<--------<--------<--------<--------<--------< (5)
+ | | | | | |
+ P3/P9 --<--------<--------<--------<--------<--------< (6)
+ | | | | | |
+ P4/P10 --<--------<--------<--------<--------<--------< (7)
+ | | | | | |
+ P5/P11 --<--------<--------<--------<--------<--------< (8)
+ (0) (1) (2) (9) (10) (11)
+ - Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO
+ pins 12-23.
+ L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]
+ | | | | | |
+ P12/P18 --<--------<--------<--------<--------<--------< (15)
+ | | | | | |
+ P13/P19 --<--------<--------<--------<--------<--------< (16)
+ | | | | | |
+ P14/P20 --<--------<--------<--------<--------<--------< (17)
+ | | | | | |
+ P15/P21 --<--------<--------<--------<--------<--------< (18)
+ | | | | | |
+ P16/P22 --<--------<--------<--------<--------<--------< (19)
+ | | | | | |
+ P17/P23 --<--------<--------<--------<--------<--------< (20)
+ (12) (13) (14) (21) (22) (23)
+ - Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection is
+ provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28.
+ P24 P25 ... P30 P31
+ | | | |
+ LED POL --X-------X---/\/---X-------X (28)
+ (24) (25) ... (31) (32)
+
+ In bi-color scan mode, 72 LEDs are supported. These are grouped into four
+ output matrices:
+ - Group A of 12 pairs of anti-parallel LEDs. LED selection is provided
+ by GPIO pins 0-11, polarity selection by GPIO 12.
+ - Group B of 6 pairs of anti-parallel LEDs. LED selection is provided
+ by GPIO pins 23-28, polarity selection by GPIO 21.
+ - Group C of 6 pairs of anti-parallel LEDs. LED selection is provided
+ by GPIO pins 29-34, polarity selection by GPIO 22.
+ - Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20,
+ columns by GPIO pins 13-14 and 21-22 (shared with groups B and C).
+ L2[n] L2[n+6] L2[n+12] L2[n+18]
+ | | | |
+ +0 --<--------<---------<---------< (15)
+ | | | |
+ +1 --<--------<---------<---------< (16)
+ | | | |
+ +2 --<--------<---------<---------< (17)
+ | | | |
+ +3 --<--------<---------<---------< (18)
+ | | | |
+ +4 --<--------<---------<---------< (19)
+ | | | |
+ +6 --<--------<---------<---------< (20)
+ (13) (14) (21) (22)
+
+ This node must always be a child of a 'realtek,rtl8231' node.
+
+properties:
+ $nodename:
+ const: led-controller
+
+ compatible:
+ const: realtek,rtl8231-leds
+
+ "#address-cells":
+ const: 2
+
+ "#size-cells":
+ const: 0
+
+ realtek,led-scan-mode:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ Specify the scanning mode the chip should run in. See general description
+ for how the scanning matrices are wired up.
+ enum: [single-color, bi-color]
+
+patternProperties:
+ "^led@([1-2]?[0-9]|3[0-1]),[0-2]":
+ description:
+ LEDs are addressed by their port index and led index. Ports 0-23 always
+ support three LEDs. Additionally, but only when used in single color scan
+ mode, ports 24-31 support two LEDs.
+ type: object
+
+ properties:
+ reg:
+ items:
+ - items:
+ - description: port index
+ maximum: 31
+ - description: led index
+ maximum: 2
+
+ allOf:
+ - $ref: /schemas/leds/common.yaml#
+
+ required:
+ - reg
+
+required:
+ - compatible
+ - "#address-cells"
+ - "#size-cells"
+ - realtek,led-scan-mode
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/leds/common.h>
+ led-controller {
+ compatible = "realtek,rtl8231-leds";
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ realtek,led-scan-mode = "single-color";
+
+ led@0,0 {
+ reg = <0 0>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ function-enumerator = <0>;
+ };
+
+ led@0,1 {
+ reg = <0 1>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_LAN;
+ function-enumerator = <0>;
+ };
+
+ led@0,2 {
+ reg = <0 2>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_STATUS;
+ };
+ };
--
2.51.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH v7 1/6] dt-bindings: leds: Binding for RTL8231 scan matrix
2025-11-17 21:51 ` [PATCH v7 1/6] dt-bindings: leds: Binding for RTL8231 scan matrix Sander Vanheule
@ 2025-11-18 14:56 ` Rob Herring
2025-11-18 14:58 ` Rob Herring
1 sibling, 0 replies; 19+ messages in thread
From: Rob Herring @ 2025-11-18 14:56 UTC (permalink / raw)
To: Sander Vanheule
Cc: Lee Jones, Pavel Machek, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Michael Walle, Bartosz Golaszewski, linux-leds,
devicetree, linux-kernel, linux-gpio
On Mon, Nov 17, 2025 at 10:51:31PM +0100, Sander Vanheule wrote:
> Add a binding description for the Realtek RTL8231's LED support, which
> consists of up to 88 LEDs arranged in a number of scanning matrices.
>
> Signed-off-by: Sander Vanheule <sander@svanheule.net>
> ---
> Changes since v6:
> - Relax description formatting
> - Enforce address format for led node names
> - Use absolute paths for schema references
> ---
> .../bindings/leds/realtek,rtl8231-leds.yaml | 167 ++++++++++++++++++
> 1 file changed, 167 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
>
> diff --git a/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml b/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
> new file mode 100644
> index 000000000000..222cd08914da
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
> @@ -0,0 +1,167 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Realtek RTL8231 LED scan matrix.
> +
> +maintainers:
> + - Sander Vanheule <sander@svanheule.net>
> +
> +description: |
> + The RTL8231 has support for driving a number of LED matrices, by scanning
> + over the LEDs pins, alternatingly lighting different columns and/or rows.
> +
> + This functionality is available on an RTL8231, when it is configured for use
> + as an MDIO device, or SMI device.
> +
> + In single color scan mode, 88 LEDs are supported. These are grouped into
> + three output matrices:
> + - Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO
> + pins 0-11.
> + L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]
> + | | | | | |
> + P0/P6 --<--------<--------<--------<--------<--------< (3)
> + | | | | | |
> + P1/P7 --<--------<--------<--------<--------<--------< (4)
> + | | | | | |
> + P2/P8 --<--------<--------<--------<--------<--------< (5)
> + | | | | | |
> + P3/P9 --<--------<--------<--------<--------<--------< (6)
> + | | | | | |
> + P4/P10 --<--------<--------<--------<--------<--------< (7)
> + | | | | | |
> + P5/P11 --<--------<--------<--------<--------<--------< (8)
> + (0) (1) (2) (9) (10) (11)
> + - Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO
> + pins 12-23.
> + L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]
> + | | | | | |
> + P12/P18 --<--------<--------<--------<--------<--------< (15)
> + | | | | | |
> + P13/P19 --<--------<--------<--------<--------<--------< (16)
> + | | | | | |
> + P14/P20 --<--------<--------<--------<--------<--------< (17)
> + | | | | | |
> + P15/P21 --<--------<--------<--------<--------<--------< (18)
> + | | | | | |
> + P16/P22 --<--------<--------<--------<--------<--------< (19)
> + | | | | | |
> + P17/P23 --<--------<--------<--------<--------<--------< (20)
> + (12) (13) (14) (21) (22) (23)
> + - Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection is
> + provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28.
> + P24 P25 ... P30 P31
> + | | | |
> + LED POL --X-------X---/\/---X-------X (28)
> + (24) (25) ... (31) (32)
> +
> + In bi-color scan mode, 72 LEDs are supported. These are grouped into four
> + output matrices:
> + - Group A of 12 pairs of anti-parallel LEDs. LED selection is provided
> + by GPIO pins 0-11, polarity selection by GPIO 12.
> + - Group B of 6 pairs of anti-parallel LEDs. LED selection is provided
> + by GPIO pins 23-28, polarity selection by GPIO 21.
> + - Group C of 6 pairs of anti-parallel LEDs. LED selection is provided
> + by GPIO pins 29-34, polarity selection by GPIO 22.
> + - Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20,
> + columns by GPIO pins 13-14 and 21-22 (shared with groups B and C).
> + L2[n] L2[n+6] L2[n+12] L2[n+18]
> + | | | |
> + +0 --<--------<---------<---------< (15)
> + | | | |
> + +1 --<--------<---------<---------< (16)
> + | | | |
> + +2 --<--------<---------<---------< (17)
> + | | | |
> + +3 --<--------<---------<---------< (18)
> + | | | |
> + +4 --<--------<---------<---------< (19)
> + | | | |
> + +6 --<--------<---------<---------< (20)
> + (13) (14) (21) (22)
> +
> + This node must always be a child of a 'realtek,rtl8231' node.
> +
> +properties:
> + $nodename:
> + const: led-controller
> +
> + compatible:
> + const: realtek,rtl8231-leds
> +
> + "#address-cells":
> + const: 2
> +
> + "#size-cells":
> + const: 0
> +
> + realtek,led-scan-mode:
> + $ref: /schemas/types.yaml#/definitions/string
> + description:
> + Specify the scanning mode the chip should run in. See general description
> + for how the scanning matrices are wired up.
> + enum: [single-color, bi-color]
> +
> +patternProperties:
> + "^led@([1-2]?[0-9]|3[0-1]),[0-2]":
> + description:
> + LEDs are addressed by their port index and led index. Ports 0-23 always
> + support three LEDs. Additionally, but only when used in single color scan
> + mode, ports 24-31 support two LEDs.
> + type: object
Drop the allOf and move the $ref here. You also need:
unevaluatedProperties: false
The tools should have caught this, I'll have to check...
> +
> + properties:
> + reg:
> + items:
> + - items:
> + - description: port index
> + maximum: 31
> + - description: led index
> + maximum: 2
> +
> + allOf:
> + - $ref: /schemas/leds/common.yaml#
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH v7 1/6] dt-bindings: leds: Binding for RTL8231 scan matrix
2025-11-17 21:51 ` [PATCH v7 1/6] dt-bindings: leds: Binding for RTL8231 scan matrix Sander Vanheule
2025-11-18 14:56 ` Rob Herring
@ 2025-11-18 14:58 ` Rob Herring
2025-11-18 20:37 ` Sander Vanheule
1 sibling, 1 reply; 19+ messages in thread
From: Rob Herring @ 2025-11-18 14:58 UTC (permalink / raw)
To: Sander Vanheule
Cc: Lee Jones, Pavel Machek, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Michael Walle, Bartosz Golaszewski, linux-leds,
devicetree, linux-kernel, linux-gpio
On Mon, Nov 17, 2025 at 10:51:31PM +0100, Sander Vanheule wrote:
> Add a binding description for the Realtek RTL8231's LED support, which
> consists of up to 88 LEDs arranged in a number of scanning matrices.
>
> Signed-off-by: Sander Vanheule <sander@svanheule.net>
> ---
> Changes since v6:
> - Relax description formatting
> - Enforce address format for led node names
> - Use absolute paths for schema references
> ---
> .../bindings/leds/realtek,rtl8231-leds.yaml | 167 ++++++++++++++++++
> 1 file changed, 167 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
>
> diff --git a/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml b/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
> new file mode 100644
> index 000000000000..222cd08914da
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
> @@ -0,0 +1,167 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Realtek RTL8231 LED scan matrix.
> +
> +maintainers:
> + - Sander Vanheule <sander@svanheule.net>
> +
> +description: |
> + The RTL8231 has support for driving a number of LED matrices, by scanning
> + over the LEDs pins, alternatingly lighting different columns and/or rows.
> +
> + This functionality is available on an RTL8231, when it is configured for use
> + as an MDIO device, or SMI device.
> +
> + In single color scan mode, 88 LEDs are supported. These are grouped into
> + three output matrices:
> + - Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO
> + pins 0-11.
> + L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]
> + | | | | | |
> + P0/P6 --<--------<--------<--------<--------<--------< (3)
> + | | | | | |
> + P1/P7 --<--------<--------<--------<--------<--------< (4)
> + | | | | | |
> + P2/P8 --<--------<--------<--------<--------<--------< (5)
> + | | | | | |
> + P3/P9 --<--------<--------<--------<--------<--------< (6)
> + | | | | | |
> + P4/P10 --<--------<--------<--------<--------<--------< (7)
> + | | | | | |
> + P5/P11 --<--------<--------<--------<--------<--------< (8)
> + (0) (1) (2) (9) (10) (11)
> + - Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO
> + pins 12-23.
> + L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]
> + | | | | | |
> + P12/P18 --<--------<--------<--------<--------<--------< (15)
> + | | | | | |
> + P13/P19 --<--------<--------<--------<--------<--------< (16)
> + | | | | | |
> + P14/P20 --<--------<--------<--------<--------<--------< (17)
> + | | | | | |
> + P15/P21 --<--------<--------<--------<--------<--------< (18)
> + | | | | | |
> + P16/P22 --<--------<--------<--------<--------<--------< (19)
> + | | | | | |
> + P17/P23 --<--------<--------<--------<--------<--------< (20)
> + (12) (13) (14) (21) (22) (23)
> + - Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection is
> + provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28.
> + P24 P25 ... P30 P31
> + | | | |
> + LED POL --X-------X---/\/---X-------X (28)
> + (24) (25) ... (31) (32)
> +
> + In bi-color scan mode, 72 LEDs are supported. These are grouped into four
> + output matrices:
> + - Group A of 12 pairs of anti-parallel LEDs. LED selection is provided
> + by GPIO pins 0-11, polarity selection by GPIO 12.
> + - Group B of 6 pairs of anti-parallel LEDs. LED selection is provided
> + by GPIO pins 23-28, polarity selection by GPIO 21.
> + - Group C of 6 pairs of anti-parallel LEDs. LED selection is provided
> + by GPIO pins 29-34, polarity selection by GPIO 22.
> + - Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20,
> + columns by GPIO pins 13-14 and 21-22 (shared with groups B and C).
> + L2[n] L2[n+6] L2[n+12] L2[n+18]
> + | | | |
> + +0 --<--------<---------<---------< (15)
> + | | | |
> + +1 --<--------<---------<---------< (16)
> + | | | |
> + +2 --<--------<---------<---------< (17)
> + | | | |
> + +3 --<--------<---------<---------< (18)
> + | | | |
> + +4 --<--------<---------<---------< (19)
> + | | | |
> + +6 --<--------<---------<---------< (20)
> + (13) (14) (21) (22)
> +
> + This node must always be a child of a 'realtek,rtl8231' node.
> +
> +properties:
> + $nodename:
> + const: led-controller
> +
> + compatible:
> + const: realtek,rtl8231-leds
> +
> + "#address-cells":
> + const: 2
> +
> + "#size-cells":
> + const: 0
> +
> + realtek,led-scan-mode:
> + $ref: /schemas/types.yaml#/definitions/string
> + description:
> + Specify the scanning mode the chip should run in. See general description
> + for how the scanning matrices are wired up.
> + enum: [single-color, bi-color]
> +
> +patternProperties:
> + "^led@([1-2]?[0-9]|3[0-1]),[0-2]":
> + description:
> + LEDs are addressed by their port index and led index. Ports 0-23 always
> + support three LEDs. Additionally, but only when used in single color scan
> + mode, ports 24-31 support two LEDs.
> + type: object
> +
> + properties:
> + reg:
> + items:
> + - items:
> + - description: port index
> + maximum: 31
> + - description: led index
> + maximum: 2
> +
> + allOf:
> + - $ref: /schemas/leds/common.yaml#
> +
> + required:
> + - reg
> +
> +required:
> + - compatible
> + - "#address-cells"
> + - "#size-cells"
> + - realtek,led-scan-mode
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/leds/common.h>
> + led-controller {
Also, drop the example here. It just duplicates what is in the mfd
schema and we want 1 complete example, not piecemeal examples.
> + compatible = "realtek,rtl8231-leds";
> + #address-cells = <2>;
> + #size-cells = <0>;
> +
> + realtek,led-scan-mode = "single-color";
> +
> + led@0,0 {
> + reg = <0 0>;
> + color = <LED_COLOR_ID_GREEN>;
> + function = LED_FUNCTION_LAN;
> + function-enumerator = <0>;
> + };
> +
> + led@0,1 {
> + reg = <0 1>;
> + color = <LED_COLOR_ID_AMBER>;
> + function = LED_FUNCTION_LAN;
> + function-enumerator = <0>;
> + };
> +
> + led@0,2 {
> + reg = <0 2>;
> + color = <LED_COLOR_ID_GREEN>;
> + function = LED_FUNCTION_STATUS;
> + };
> + };
> --
> 2.51.1
>
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH v7 1/6] dt-bindings: leds: Binding for RTL8231 scan matrix
2025-11-18 14:58 ` Rob Herring
@ 2025-11-18 20:37 ` Sander Vanheule
0 siblings, 0 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-18 20:37 UTC (permalink / raw)
To: Rob Herring
Cc: Lee Jones, Pavel Machek, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Michael Walle, Bartosz Golaszewski, linux-leds,
devicetree, linux-kernel, linux-gpio
Hi Rob,
On Tue, 2025-11-18 at 08:58 -0600, Rob Herring wrote:
> On Mon, Nov 17, 2025 at 10:51:31PM +0100, Sander Vanheule wrote:
> > + allOf:
> > + - $ref: /schemas/leds/common.yaml#
I've moved the $ref property as you mentioned in your first message and added the
unevaluatedProperties: false
> > +examples:
> > + - |
> > + #include <dt-bindings/leds/common.h>
> > + led-controller {
>
> Also, drop the example here. It just duplicates what is in the mfd
> schema and we want 1 complete example, not piecemeal examples.
Done!
Thanks,
Sander
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231
2025-11-17 21:51 [PATCH v7 0/6] RTL8231 GPIO expander support Sander Vanheule
2025-11-17 21:51 ` [PATCH v7 1/6] dt-bindings: leds: Binding for RTL8231 scan matrix Sander Vanheule
@ 2025-11-17 21:51 ` Sander Vanheule
2025-11-18 14:59 ` Rob Herring (Arm)
2025-11-18 21:28 ` Rob Herring
2025-11-17 21:51 ` [PATCH v7 3/6] mfd: Add RTL8231 core device Sander Vanheule
` (3 subsequent siblings)
5 siblings, 2 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-17 21:51 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Linus Walleij, Michael Walle, Bartosz Golaszewski
Cc: linux-leds, devicetree, linux-kernel, linux-gpio, Sander Vanheule
Add a binding description for the Realtek RTL8231, a GPIO and LED
expander chip commonly used in ethernet switches based on a Realtek
switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
as a plain 36-bit shift register.
This binding only describes the feature set provided by the MDIO/SMI
configuration, and covers the GPIO, PWM, and pin control properties. The
LED properties are defined in a separate binding.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
Changes since v6:
- Relax description formatting
- Use absolute paths for schema references
- Add pinctrl properties to led-controller node in example
---
.../bindings/mfd/realtek,rtl8231.yaml | 193 ++++++++++++++++++
1 file changed, 193 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
diff --git a/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml b/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
new file mode 100644
index 000000000000..5669dd58654e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
@@ -0,0 +1,193 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/realtek,rtl8231.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RTL8231 GPIO and LED expander.
+
+maintainers:
+ - Sander Vanheule <sander@svanheule.net>
+
+description: |
+ The RTL8231 is a GPIO and LED expander chip, providing up to 37 GPIOs, up to
+ 88 LEDs, and up to one PWM output. This device is frequently used alongside
+ Realtek switch SoCs, to provide additional I/O capabilities.
+
+ To manage the RTL8231's features, its strapping pins can be used to configure
+ it in one of three modes: shift register, MDIO device, or SMI device. The
+ shift register mode does not need special support. In MDIO or SMI mode, most
+ pins can be configured as a GPIO output or LED matrix scan line/column. One
+ pin can be used as PWM output.
+
+ The GPIO, PWM, and pin control are part of the main node. LED support is
+ configured as a sub-node.
+
+properties:
+ compatible:
+ const: realtek,rtl8231
+
+ reg:
+ description: MDIO or SMI device address.
+ maxItems: 1
+
+ # GPIO support
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+ description:
+ The first cell is the pin number and the second cell is used to specify
+ the GPIO active state.
+
+ gpio-ranges:
+ description:
+ Must reference itself, and provide a zero-based mapping for 37 pins.
+ maxItems: 1
+
+ # Pin muxing and configuration
+ drive-strength:
+ description:
+ Common drive strength used for all GPIO output pins, must be 4mA or 8mA.
+ On reset, this value will default to 8mA.
+ enum: [4, 8]
+
+ # LED scanning matrix
+ led-controller:
+ $ref: /schemas/leds/realtek,rtl8231-leds.yaml#
+
+ # PWM output
+ "#pwm-cells":
+ description:
+ Twos cells with PWM index (must be 0) and PWM frequency in Hz. To use
+ the PWM output, gpio35 must be muxed to its "pwm" function. Valid
+ frequency values for consumers are 1200, 1600, 2000, 2400, 2800, 3200,
+ 4000, and 4800.
+ const: 2
+
+patternProperties:
+ "-pins$":
+ type: object
+ $ref: /schemas/pinctrl/pinmux-node.yaml#
+
+ properties:
+ pins:
+ items:
+ 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,
+ gpio32, gpio33, gpio34, gpio35, gpio36]
+ minItems: 1
+ maxItems: 37
+
+ function:
+ description:
+ Select which function to use. "gpio" is supported for all pins, "led" is supported
+ for pins 0-34, "pwm" is supported for pin 35.
+ enum: [gpio, led, pwm]
+
+ required:
+ - pins
+ - function
+
+required:
+ - compatible
+ - reg
+ - gpio-controller
+ - "#gpio-cells"
+ - gpio-ranges
+
+additionalProperties: false
+
+examples:
+ - |
+ // Minimal example
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ expander0: expander@0 {
+ compatible = "realtek,rtl8231";
+ reg = <0>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&expander0 0 0 37>;
+ };
+ };
+ - |
+ // All bells and whistles included
+ #include <dt-bindings/leds/common.h>
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ expander1: expander@1 {
+ compatible = "realtek,rtl8231";
+ reg = <1>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&expander1 0 0 37>;
+
+ #pwm-cells = <2>;
+
+ drive-strength = <4>;
+
+ button-pins {
+ pins = "gpio36";
+ function = "gpio";
+ input-debounce = <100000>;
+ };
+
+ pwm-pins {
+ pins = "gpio35";
+ function = "pwm";
+ };
+
+ led_matrix: led-pins {
+ pins = "gpio0", "gpio1", "gpio3", "gpio4";
+ function = "led";
+ };
+
+ led-controller {
+ compatible = "realtek,rtl8231-leds";
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_matrix>;
+
+ realtek,led-scan-mode = "single-color";
+
+ led@0,0 {
+ reg = <0 0>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ function-enumerator = <0>;
+ };
+
+ led@0,1 {
+ reg = <0 1>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_LAN;
+ function-enumerator = <0>;
+ };
+
+ led@1,0 {
+ reg = <1 0>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ function-enumerator = <1>;
+ };
+
+ led@1,1 {
+ reg = <1 1>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_LAN;
+ function-enumerator = <1>;
+ };
+ };
+ };
+ };
--
2.51.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231
2025-11-17 21:51 ` [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231 Sander Vanheule
@ 2025-11-18 14:59 ` Rob Herring (Arm)
2025-11-18 21:28 ` Rob Herring
1 sibling, 0 replies; 19+ messages in thread
From: Rob Herring (Arm) @ 2025-11-18 14:59 UTC (permalink / raw)
To: Sander Vanheule
Cc: Lee Jones, devicetree, Pavel Machek, Conor Dooley, linux-kernel,
Bartosz Golaszewski, Michael Walle, Linus Walleij, linux-gpio,
linux-leds, Krzysztof Kozlowski
On Mon, 17 Nov 2025 22:51:32 +0100, Sander Vanheule wrote:
> Add a binding description for the Realtek RTL8231, a GPIO and LED
> expander chip commonly used in ethernet switches based on a Realtek
> switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
> as a plain 36-bit shift register.
>
> This binding only describes the feature set provided by the MDIO/SMI
> configuration, and covers the GPIO, PWM, and pin control properties. The
> LED properties are defined in a separate binding.
>
> Signed-off-by: Sander Vanheule <sander@svanheule.net>
> ---
> Changes since v6:
> - Relax description formatting
> - Use absolute paths for schema references
> - Add pinctrl properties to led-controller node in example
> ---
> .../bindings/mfd/realtek,rtl8231.yaml | 193 ++++++++++++++++++
> 1 file changed, 193 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231
2025-11-17 21:51 ` [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231 Sander Vanheule
2025-11-18 14:59 ` Rob Herring (Arm)
@ 2025-11-18 21:28 ` Rob Herring
2025-11-18 21:57 ` Sander Vanheule
1 sibling, 1 reply; 19+ messages in thread
From: Rob Herring @ 2025-11-18 21:28 UTC (permalink / raw)
To: Sander Vanheule
Cc: Lee Jones, Pavel Machek, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Michael Walle, Bartosz Golaszewski, linux-leds,
devicetree, linux-kernel, linux-gpio
On Mon, Nov 17, 2025 at 3:52 PM Sander Vanheule <sander@svanheule.net> wrote:
>
> Add a binding description for the Realtek RTL8231, a GPIO and LED
> expander chip commonly used in ethernet switches based on a Realtek
> switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
> as a plain 36-bit shift register.
>
> This binding only describes the feature set provided by the MDIO/SMI
> configuration, and covers the GPIO, PWM, and pin control properties. The
> LED properties are defined in a separate binding.
>
> Signed-off-by: Sander Vanheule <sander@svanheule.net>
> ---
> Changes since v6:
> - Relax description formatting
> - Use absolute paths for schema references
> - Add pinctrl properties to led-controller node in example
> ---
> .../bindings/mfd/realtek,rtl8231.yaml | 193 ++++++++++++++++++
> 1 file changed, 193 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
>
> diff --git a/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml b/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
> new file mode 100644
> index 000000000000..5669dd58654e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
> @@ -0,0 +1,193 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/realtek,rtl8231.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Realtek RTL8231 GPIO and LED expander.
> +
> +maintainers:
> + - Sander Vanheule <sander@svanheule.net>
> +
> +description: |
> + The RTL8231 is a GPIO and LED expander chip, providing up to 37 GPIOs, up to
> + 88 LEDs, and up to one PWM output. This device is frequently used alongside
> + Realtek switch SoCs, to provide additional I/O capabilities.
> +
> + To manage the RTL8231's features, its strapping pins can be used to configure
> + it in one of three modes: shift register, MDIO device, or SMI device. The
> + shift register mode does not need special support. In MDIO or SMI mode, most
> + pins can be configured as a GPIO output or LED matrix scan line/column. One
> + pin can be used as PWM output.
> +
> + The GPIO, PWM, and pin control are part of the main node. LED support is
> + configured as a sub-node.
> +
> +properties:
> + compatible:
> + const: realtek,rtl8231
> +
> + reg:
> + description: MDIO or SMI device address.
> + maxItems: 1
> +
> + # GPIO support
> + gpio-controller: true
> +
> + "#gpio-cells":
> + const: 2
> + description:
> + The first cell is the pin number and the second cell is used to specify
> + the GPIO active state.
> +
> + gpio-ranges:
> + description:
> + Must reference itself, and provide a zero-based mapping for 37 pins.
> + maxItems: 1
> +
> + # Pin muxing and configuration
> + drive-strength:
> + description:
> + Common drive strength used for all GPIO output pins, must be 4mA or 8mA.
> + On reset, this value will default to 8mA.
> + enum: [4, 8]
> +
> + # LED scanning matrix
> + led-controller:
> + $ref: /schemas/leds/realtek,rtl8231-leds.yaml#
> +
> + # PWM output
> + "#pwm-cells":
> + description:
> + Twos cells with PWM index (must be 0) and PWM frequency in Hz. To use
> + the PWM output, gpio35 must be muxed to its "pwm" function. Valid
> + frequency values for consumers are 1200, 1600, 2000, 2400, 2800, 3200,
> + 4000, and 4800.
> + const: 2
> +
> +patternProperties:
> + "-pins$":
> + type: object
> + $ref: /schemas/pinctrl/pinmux-node.yaml#
additionalProperties: false
> +
> + properties:
> + pins:
> + items:
> + 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,
> + gpio32, gpio33, gpio34, gpio35, gpio36]
> + minItems: 1
> + maxItems: 37
> +
> + function:
> + description:
> + Select which function to use. "gpio" is supported for all pins, "led" is supported
> + for pins 0-34, "pwm" is supported for pin 35.
> + enum: [gpio, led, pwm]
> +
> + required:
> + - pins
> + - function
> +
> +required:
> + - compatible
> + - reg
> + - gpio-controller
> + - "#gpio-cells"
> + - gpio-ranges
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + // Minimal example
> + mdio {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + expander0: expander@0 {
> + compatible = "realtek,rtl8231";
> + reg = <0>;
> +
> + gpio-controller;
> + #gpio-cells = <2>;
> + gpio-ranges = <&expander0 0 0 37>;
> + };
> + };
> + - |
> + // All bells and whistles included
> + #include <dt-bindings/leds/common.h>
> + mdio {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + expander1: expander@1 {
> + compatible = "realtek,rtl8231";
> + reg = <1>;
> +
> + gpio-controller;
> + #gpio-cells = <2>;
> + gpio-ranges = <&expander1 0 0 37>;
> +
> + #pwm-cells = <2>;
> +
> + drive-strength = <4>;
> +
> + button-pins {
> + pins = "gpio36";
> + function = "gpio";
> + input-debounce = <100000>;
> + };
> +
> + pwm-pins {
> + pins = "gpio35";
> + function = "pwm";
> + };
> +
> + led_matrix: led-pins {
> + pins = "gpio0", "gpio1", "gpio3", "gpio4";
> + function = "led";
> + };
> +
> + led-controller {
> + compatible = "realtek,rtl8231-leds";
> + #address-cells = <2>;
> + #size-cells = <0>;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&led_matrix>;
> +
> + realtek,led-scan-mode = "single-color";
> +
> + led@0,0 {
> + reg = <0 0>;
> + color = <LED_COLOR_ID_GREEN>;
> + function = LED_FUNCTION_LAN;
> + function-enumerator = <0>;
> + };
> +
> + led@0,1 {
> + reg = <0 1>;
> + color = <LED_COLOR_ID_AMBER>;
> + function = LED_FUNCTION_LAN;
> + function-enumerator = <0>;
> + };
> +
> + led@1,0 {
> + reg = <1 0>;
> + color = <LED_COLOR_ID_GREEN>;
> + function = LED_FUNCTION_LAN;
> + function-enumerator = <1>;
> + };
> +
> + led@1,1 {
> + reg = <1 1>;
> + color = <LED_COLOR_ID_AMBER>;
> + function = LED_FUNCTION_LAN;
> + function-enumerator = <1>;
> + };
> + };
> + };
> + };
> --
> 2.51.1
>
>
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231
2025-11-18 21:28 ` Rob Herring
@ 2025-11-18 21:57 ` Sander Vanheule
2025-11-19 1:37 ` Rob Herring
0 siblings, 1 reply; 19+ messages in thread
From: Sander Vanheule @ 2025-11-18 21:57 UTC (permalink / raw)
To: Rob Herring
Cc: Lee Jones, Pavel Machek, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Michael Walle, Bartosz Golaszewski, linux-leds,
devicetree, linux-kernel, linux-gpio
Hi Rob,
On Tue, 2025-11-18 at 15:28 -0600, Rob Herring wrote:
> On Mon, Nov 17, 2025 at 3:52 PM Sander Vanheule <sander@svanheule.net> wrote:
> > +patternProperties:
> > + "-pins$":
> > + type: object
> > + $ref: /schemas/pinctrl/pinmux-node.yaml#
>
> additionalProperties: false
In this case dt_binding_check doesn't recognize input-debounce. The following seems to
work for the provided example:
- $ref: /schemas/pinctrl/pinmux-node.yaml#
+ allOf:
+ - $ref: /schemas/pinctrl/pincfg-node.yaml#
+ - $ref: /schemas/pinctrl/pinmux-node.yaml#
+
+ additionalProperties: false
with this included in the led node properties:
+ input-debounce: true
If I understand correctly, "unevaluatedProperties: false" (like for the leds binding)
would allow everything from the referenced pincfg-node and pinmux-node schemas, which is
more than is actually supported by this device.
>
Best,
Sander
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231
2025-11-18 21:57 ` Sander Vanheule
@ 2025-11-19 1:37 ` Rob Herring
0 siblings, 0 replies; 19+ messages in thread
From: Rob Herring @ 2025-11-19 1:37 UTC (permalink / raw)
To: Sander Vanheule
Cc: Lee Jones, Pavel Machek, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Michael Walle, Bartosz Golaszewski, linux-leds,
devicetree, linux-kernel, linux-gpio
On Tue, Nov 18, 2025 at 3:57 PM Sander Vanheule <sander@svanheule.net> wrote:
>
> Hi Rob,
>
> On Tue, 2025-11-18 at 15:28 -0600, Rob Herring wrote:
> > On Mon, Nov 17, 2025 at 3:52 PM Sander Vanheule <sander@svanheule.net> wrote:
> > > +patternProperties:
> > > + "-pins$":
> > > + type: object
> > > + $ref: /schemas/pinctrl/pinmux-node.yaml#
> >
> > additionalProperties: false
>
> In this case dt_binding_check doesn't recognize input-debounce. The following seems to
> work for the provided example:
>
> - $ref: /schemas/pinctrl/pinmux-node.yaml#
> + allOf:
> + - $ref: /schemas/pinctrl/pincfg-node.yaml#
> + - $ref: /schemas/pinctrl/pinmux-node.yaml#
> +
> + additionalProperties: false
>
>
> with this included in the led node properties:
> + input-debounce: true
>
> If I understand correctly, "unevaluatedProperties: false" (like for the leds binding)
> would allow everything from the referenced pincfg-node and pinmux-node schemas, which is
> more than is actually supported by this device.
Yes, that works too. The first way lets you be explicit about which
referenced properties are used, but either way is fine. If it is only
1 property, then I'd probably go with the first way.
Rob
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v7 3/6] mfd: Add RTL8231 core device
2025-11-17 21:51 [PATCH v7 0/6] RTL8231 GPIO expander support Sander Vanheule
2025-11-17 21:51 ` [PATCH v7 1/6] dt-bindings: leds: Binding for RTL8231 scan matrix Sander Vanheule
2025-11-17 21:51 ` [PATCH v7 2/6] dt-bindings: mfd: Binding for RTL8231 Sander Vanheule
@ 2025-11-17 21:51 ` Sander Vanheule
2025-11-17 21:51 ` [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support Sander Vanheule
` (2 subsequent siblings)
5 siblings, 0 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-17 21:51 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Linus Walleij, Michael Walle, Bartosz Golaszewski
Cc: linux-leds, devicetree, linux-kernel, linux-gpio, Sander Vanheule
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After a soft reset, all pins are muxed to GPIO inputs before the pin
drivers are enabled. This is done to prevent accidental system resets,
when a pin is connected to the main SoC's reset line.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
Changes since v6:
- Sort header includes
- Drop comment on cache type (6.19 will support REGCACHE_FLAT_S)
- Limit scope of LED_START field definition to init
- Variable renames:
- map -> regmap
- val -> status (register value) and ready_code (field value)
- val -> cfg
- Invert logic for !started, reducing code indentation
- Place __maybe_unused after function return type
- Use regmap_field_write() for LED_START field (volatile register)
- Use regcache_drop_region() to invalidate cache, replacing
regcache_mark_dirty() which invalidates the device state
---
drivers/mfd/Kconfig | 9 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/rtl8231.c | 193 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/rtl8231.h | 71 +++++++++++++
4 files changed, 274 insertions(+)
create mode 100644 drivers/mfd/rtl8231.c
create mode 100644 include/linux/mfd/rtl8231.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6cec1858947b..e13e2df63fee 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1301,6 +1301,15 @@ config MFD_RDC321X
southbridge which provides access to GPIOs and Watchdog using the
southbridge PCI device configuration space.
+config MFD_RTL8231
+ tristate "Realtek RTL8231 GPIO and LED expander"
+ select MFD_CORE
+ select REGMAP_MDIO
+ help
+ Support for the Realtek RTL8231 GPIO and LED expander.
+ Provides up to 37 GPIOs, 88 LEDs, and one PWM output.
+ When built as a module, this module will be named rtl8231.
+
config MFD_RT4831
tristate "Richtek RT4831 four channel WLED and Display Bias Voltage"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 865e9f12faff..ba973382a20f 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -252,6 +252,7 @@ obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o
obj-$(CONFIG_MFD_HI6421_SPMI) += hi6421-spmi-pmic.o
obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
obj-$(CONFIG_MFD_DLN2) += dln2.o
+obj-$(CONFIG_MFD_RTL8231) += rtl8231.o
obj-$(CONFIG_MFD_RT4831) += rt4831.o
obj-$(CONFIG_MFD_RT5033) += rt5033.o
obj-$(CONFIG_MFD_RT5120) += rt5120.o
diff --git a/drivers/mfd/rtl8231.c b/drivers/mfd/rtl8231.c
new file mode 100644
index 000000000000..8c74a3497045
--- /dev/null
+++ b/drivers/mfd/rtl8231.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mdio.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/rtl8231.h>
+
+static bool rtl8231_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ /*
+ * Registers with self-clearing bits, strapping pin values.
+ * Don't mark the data registers as volatile, since we need
+ * caching for the output values.
+ */
+ case RTL8231_REG_FUNC0:
+ case RTL8231_REG_FUNC1:
+ case RTL8231_REG_PIN_HI_CFG:
+ case RTL8231_REG_LED_END:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct mfd_cell rtl8231_cells[] = {
+ {
+ .name = "rtl8231-pinctrl",
+ },
+ {
+ .name = "rtl8231-leds",
+ .of_compatible = "realtek,rtl8231-leds",
+ },
+};
+
+static int rtl8231_soft_reset(struct regmap *regmap)
+{
+ const unsigned int all_pins_mask = GENMASK(RTL8231_BITS_VAL - 1, 0);
+ unsigned int cfg;
+ int err;
+
+ /* SOFT_RESET bit self-clears when done */
+ regmap_write_bits(regmap, RTL8231_REG_PIN_HI_CFG,
+ RTL8231_PIN_HI_CFG_SOFT_RESET, RTL8231_PIN_HI_CFG_SOFT_RESET);
+
+ err = regmap_read_poll_timeout(regmap, RTL8231_REG_PIN_HI_CFG, cfg,
+ !(cfg & RTL8231_PIN_HI_CFG_SOFT_RESET), 50, 1000);
+ if (err)
+ return err;
+
+ regcache_drop_region(regmap, 0, RTL8231_REG_COUNT - 1);
+
+ /*
+ * Chip reset results in a pin configuration that is a mix of LED and GPIO outputs.
+ * Select GPI functionality for all pins before enabling pin outputs.
+ */
+ regmap_write(regmap, RTL8231_REG_PIN_MODE0, all_pins_mask);
+ regmap_write(regmap, RTL8231_REG_GPIO_DIR0, all_pins_mask);
+ regmap_write(regmap, RTL8231_REG_PIN_MODE1, all_pins_mask);
+ regmap_write(regmap, RTL8231_REG_GPIO_DIR1, all_pins_mask);
+ regmap_write(regmap, RTL8231_REG_PIN_HI_CFG,
+ RTL8231_PIN_HI_CFG_MODE_MASK | RTL8231_PIN_HI_CFG_DIR_MASK);
+
+ return 0;
+}
+
+static int rtl8231_init(struct device *dev, struct regmap *regmap)
+{
+ struct regmap_field *led_start;
+ unsigned int ready_code;
+ unsigned int started;
+ unsigned int status;
+ int err;
+
+ err = regmap_read(regmap, RTL8231_REG_FUNC1, &status);
+ if (err) {
+ dev_err(dev, "failed to read READY_CODE\n");
+ return err;
+ }
+
+ ready_code = FIELD_GET(RTL8231_FUNC1_READY_CODE_MASK, status);
+ if (ready_code != RTL8231_FUNC1_READY_CODE_VALUE) {
+ dev_err(dev, "RTL8231 not present or ready 0x%x != 0x%x\n",
+ ready_code, RTL8231_FUNC1_READY_CODE_VALUE);
+ return -ENODEV;
+ }
+
+ led_start = dev_get_drvdata(dev);
+ err = regmap_field_read(led_start, &started);
+ if (err)
+ return err;
+
+ if (started)
+ return 0;
+
+ err = rtl8231_soft_reset(regmap);
+ if (err)
+ return err;
+
+ /* LED_START enables power to output pins, and starts the LED engine */
+ return regmap_field_write(led_start, 1);
+}
+
+static const struct regmap_config rtl8231_mdio_regmap_config = {
+ .val_bits = RTL8231_BITS_VAL,
+ .reg_bits = RTL8231_BITS_REG,
+ .volatile_reg = rtl8231_volatile_reg,
+ .max_register = RTL8231_REG_COUNT - 1,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int rtl8231_mdio_probe(struct mdio_device *mdiodev)
+{
+ const struct reg_field field_led_start = REG_FIELD(RTL8231_REG_FUNC0, 1, 1);
+ struct device *dev = &mdiodev->dev;
+ struct regmap_field *led_start;
+ struct regmap *regmap;
+ int err;
+
+ regmap = devm_regmap_init_mdio(mdiodev, &rtl8231_mdio_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to init regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ led_start = devm_regmap_field_alloc(dev, regmap, field_led_start);
+ if (IS_ERR(led_start))
+ return PTR_ERR(led_start);
+
+ dev_set_drvdata(dev, led_start);
+
+ mdiodev->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(mdiodev->reset_gpio))
+ return PTR_ERR(mdiodev->reset_gpio);
+
+ device_property_read_u32(dev, "reset-assert-delay", &mdiodev->reset_assert_delay);
+ device_property_read_u32(dev, "reset-deassert-delay", &mdiodev->reset_deassert_delay);
+
+ err = rtl8231_init(dev, regmap);
+ if (err)
+ return err;
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, rtl8231_cells,
+ ARRAY_SIZE(rtl8231_cells), NULL, 0, NULL);
+}
+
+static int __maybe_unused rtl8231_suspend(struct device *dev)
+{
+ struct regmap_field *led_start = dev_get_drvdata(dev);
+
+ return regmap_field_write(led_start, 0);
+}
+
+static int __maybe_unused rtl8231_resume(struct device *dev)
+{
+ struct regmap_field *led_start = dev_get_drvdata(dev);
+
+ return regmap_field_write(led_start, 1);
+}
+
+static SIMPLE_DEV_PM_OPS(rtl8231_pm_ops, rtl8231_suspend, rtl8231_resume);
+
+static const struct of_device_id rtl8231_of_match[] = {
+ { .compatible = "realtek,rtl8231" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtl8231_of_match);
+
+static struct mdio_driver rtl8231_mdio_driver = {
+ .mdiodrv.driver = {
+ .name = "rtl8231-expander",
+ .of_match_table = rtl8231_of_match,
+ .pm = pm_ptr(&rtl8231_pm_ops),
+ },
+ .probe = rtl8231_mdio_probe,
+};
+mdio_module_driver(rtl8231_mdio_driver);
+
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek RTL8231 GPIO and LED expander");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/rtl8231.h b/include/linux/mfd/rtl8231.h
new file mode 100644
index 000000000000..003eda3797a3
--- /dev/null
+++ b/include/linux/mfd/rtl8231.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Register definitions the RTL8231 GPIO and LED expander chip
+ */
+
+#ifndef __LINUX_MFD_RTL8231_H
+#define __LINUX_MFD_RTL8231_H
+
+#include <linux/bits.h>
+
+/*
+ * Registers addresses are 5 bit, values are 16 bit
+ * Also define a duplicated range of virtual addresses, to enable
+ * different read/write behaviour on the GPIO data registers
+ */
+#define RTL8231_BITS_VAL 16
+#define RTL8231_BITS_REG 5
+
+/* Chip control */
+#define RTL8231_REG_FUNC0 0x00
+#define RTL8231_FUNC0_SCAN_MODE BIT(0)
+#define RTL8231_FUNC0_SCAN_SINGLE 0
+#define RTL8231_FUNC0_SCAN_BICOLOR BIT(0)
+
+#define RTL8231_REG_FUNC1 0x01
+#define RTL8231_FUNC1_READY_CODE_VALUE 0x37
+#define RTL8231_FUNC1_READY_CODE_MASK GENMASK(9, 4)
+#define RTL8231_FUNC1_DEBOUNCE_MASK GENMASK(15, 10)
+
+/* Pin control */
+#define RTL8231_REG_PIN_MODE0 0x02
+#define RTL8231_REG_PIN_MODE1 0x03
+
+#define RTL8231_PIN_MODE_LED 0
+#define RTL8231_PIN_MODE_GPIO 1
+
+/* Pin high config: pin and GPIO control for pins 32-26 */
+#define RTL8231_REG_PIN_HI_CFG 0x04
+#define RTL8231_PIN_HI_CFG_MODE_MASK GENMASK(4, 0)
+#define RTL8231_PIN_HI_CFG_DIR_MASK GENMASK(9, 5)
+#define RTL8231_PIN_HI_CFG_INV_MASK GENMASK(14, 10)
+#define RTL8231_PIN_HI_CFG_SOFT_RESET BIT(15)
+
+/* GPIO control registers */
+#define RTL8231_REG_GPIO_DIR0 0x05
+#define RTL8231_REG_GPIO_DIR1 0x06
+#define RTL8231_REG_GPIO_INVERT0 0x07
+#define RTL8231_REG_GPIO_INVERT1 0x08
+
+#define RTL8231_GPIO_DIR_IN 1
+#define RTL8231_GPIO_DIR_OUT 0
+
+/*
+ * GPIO data registers
+ * Only the output data can be written to these registers, and only the input
+ * data can be read.
+ */
+#define RTL8231_REG_GPIO_DATA0 0x1c
+#define RTL8231_REG_GPIO_DATA1 0x1d
+#define RTL8231_REG_GPIO_DATA2 0x1e
+#define RTL8231_PIN_HI_DATA_MASK GENMASK(4, 0)
+
+/* LED control base registers */
+#define RTL8231_REG_LED0_BASE 0x09
+#define RTL8231_REG_LED1_BASE 0x10
+#define RTL8231_REG_LED2_BASE 0x17
+#define RTL8231_REG_LED_END 0x1b
+
+#define RTL8231_REG_COUNT 0x1f
+
+#endif /* __LINUX_MFD_RTL8231_H */
--
2.51.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
2025-11-17 21:51 [PATCH v7 0/6] RTL8231 GPIO expander support Sander Vanheule
` (2 preceding siblings ...)
2025-11-17 21:51 ` [PATCH v7 3/6] mfd: Add RTL8231 core device Sander Vanheule
@ 2025-11-17 21:51 ` Sander Vanheule
2025-11-18 21:19 ` kernel test robot
` (2 more replies)
2025-11-17 21:51 ` [PATCH v7 5/6] leds: Add support for RTL8231 LED scan matrix Sander Vanheule
2025-11-17 21:51 ` [PATCH v7 6/6] MAINTAINERS: Add RTL8231 MFD driver Sander Vanheule
5 siblings, 3 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-17 21:51 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Linus Walleij, Michael Walle, Bartosz Golaszewski
Cc: linux-leds, devicetree, linux-kernel, linux-gpio, Sander Vanheule
This driver implements the GPIO and pin muxing features provided by the
RTL8231. The device should be instantiated as an MFD child, where the
parent device has already configured the regmap used for register
access.
Debouncing is only available for the six highest GPIOs, and must be
emulated when other pins are used for (button) inputs. Although
described in the bindings, drive strength selection is currently not
implemented.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
Changes since v6:
- Use uintptr_t storage for enum rtl8231_pin_function
- Simplify safe direction configuration
- Add GPIOLIB dependency
- Add Linus's review tag
---
drivers/pinctrl/Kconfig | 12 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rtl8231.c | 533 ++++++++++++++++++++++++++++++
3 files changed, 546 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 4f8507ebbdac..107c59f8aa7d 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -520,6 +520,18 @@ config PINCTRL_ROCKCHIP
help
This support pinctrl and GPIO driver for Rockchip SoCs.
+config PINCTRL_RTL8231
+ tristate "Realtek RTL8231 GPIO expander's pin controller"
+ depends on MFD_RTL8231
+ default MFD_RTL8231
+ select GENERIC_PINCONF
+ select GENERIC_PINMUX_FUNCTIONS
+ select GPIOLIB
+ select GPIO_REGMAP
+ help
+ Support for RTL8231 expander's GPIOs and pin controller.
+ When built as a module, the module will be called pinctrl-rtl8231.
+
config PINCTRL_SCMI
tristate "Pinctrl driver using SCMI protocol interface"
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index e0cfb9b7c99b..ded51723d452 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_RP1) += pinctrl-rp1.o
+obj-$(CONFIG_PINCTRL_RTL8231) += pinctrl-rtl8231.o
obj-$(CONFIG_PINCTRL_SCMI) += pinctrl-scmi.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
diff --git a/drivers/pinctrl/pinctrl-rtl8231.c b/drivers/pinctrl/pinctrl-rtl8231.c
new file mode 100644
index 000000000000..a06cbdbb2018
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rtl8231.c
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/module.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "core.h"
+#include "pinmux.h"
+#include <linux/mfd/rtl8231.h>
+
+#define RTL8231_NUM_GPIOS 37
+#define RTL8231_DEBOUNCE_USEC 100000
+#define RTL8231_DEBOUNCE_MIN_OFFSET 31
+
+struct rtl8231_pin_ctrl {
+ struct regmap *map;
+};
+
+/*
+ * Pin controller functionality
+ */
+enum rtl8231_pin_function : uintptr_t {
+ RTL8231_PIN_FUNCTION_GPIO = BIT(0),
+ RTL8231_PIN_FUNCTION_LED = BIT(1),
+ RTL8231_PIN_FUNCTION_PWM = BIT(2),
+};
+
+struct rtl8231_function_info {
+ enum rtl8231_pin_function flag;
+ const char *name;
+};
+
+#define RTL8231_FUNCTION(_name, _flag) \
+((struct rtl8231_function_info) { \
+ .flag = (_flag), \
+ .name = (_name), \
+ })
+
+static const struct rtl8231_function_info rtl8231_pin_functions[] = {
+ RTL8231_FUNCTION("gpio", RTL8231_PIN_FUNCTION_GPIO),
+ RTL8231_FUNCTION("led", RTL8231_PIN_FUNCTION_LED),
+ RTL8231_FUNCTION("pwm", RTL8231_PIN_FUNCTION_PWM),
+};
+
+struct rtl8231_pin_desc {
+ enum rtl8231_pin_function functions:8;
+ u8 reg;
+ u8 offset;
+ u8 gpio_function_value;
+};
+
+#define RTL8231_PIN_DESC(_num, _func, _reg, _fld, _val) \
+ [(_num)] = ((struct rtl8231_pin_desc) { \
+ .functions = RTL8231_PIN_FUNCTION_GPIO | (_func), \
+ .reg = (_reg), \
+ .offset = (_fld), \
+ .gpio_function_value = (_val), \
+ })
+#define RTL8231_GPIO_PIN_DESC(_num, _reg, _fld) \
+ RTL8231_PIN_DESC(_num, 0, _reg, _fld, RTL8231_PIN_MODE_GPIO)
+#define RTL8231_LED_PIN_DESC(_num, _reg, _fld) \
+ RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_LED, _reg, _fld, RTL8231_PIN_MODE_GPIO)
+#define RTL8231_PWM_PIN_DESC(_num, _reg, _fld) \
+ RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_PWM, _reg, _fld, 0)
+
+/*
+ * All pins have a GPIO/LED mux bit, but the bits for pins 35/36 are read-only. Use this bit
+ * for the GPIO-only pin instead of a placeholder, so the rest of the logic can stay generic.
+ */
+static const struct rtl8231_pin_desc rtl8231_pin_data[RTL8231_NUM_GPIOS] = {
+ RTL8231_LED_PIN_DESC(0, RTL8231_REG_PIN_MODE0, 0),
+ RTL8231_LED_PIN_DESC(1, RTL8231_REG_PIN_MODE0, 1),
+ RTL8231_LED_PIN_DESC(2, RTL8231_REG_PIN_MODE0, 2),
+ RTL8231_LED_PIN_DESC(3, RTL8231_REG_PIN_MODE0, 3),
+ RTL8231_LED_PIN_DESC(4, RTL8231_REG_PIN_MODE0, 4),
+ RTL8231_LED_PIN_DESC(5, RTL8231_REG_PIN_MODE0, 5),
+ RTL8231_LED_PIN_DESC(6, RTL8231_REG_PIN_MODE0, 6),
+ RTL8231_LED_PIN_DESC(7, RTL8231_REG_PIN_MODE0, 7),
+ RTL8231_LED_PIN_DESC(8, RTL8231_REG_PIN_MODE0, 8),
+ RTL8231_LED_PIN_DESC(9, RTL8231_REG_PIN_MODE0, 9),
+ RTL8231_LED_PIN_DESC(10, RTL8231_REG_PIN_MODE0, 10),
+ RTL8231_LED_PIN_DESC(11, RTL8231_REG_PIN_MODE0, 11),
+ RTL8231_LED_PIN_DESC(12, RTL8231_REG_PIN_MODE0, 12),
+ RTL8231_LED_PIN_DESC(13, RTL8231_REG_PIN_MODE0, 13),
+ RTL8231_LED_PIN_DESC(14, RTL8231_REG_PIN_MODE0, 14),
+ RTL8231_LED_PIN_DESC(15, RTL8231_REG_PIN_MODE0, 15),
+ RTL8231_LED_PIN_DESC(16, RTL8231_REG_PIN_MODE1, 0),
+ RTL8231_LED_PIN_DESC(17, RTL8231_REG_PIN_MODE1, 1),
+ RTL8231_LED_PIN_DESC(18, RTL8231_REG_PIN_MODE1, 2),
+ RTL8231_LED_PIN_DESC(19, RTL8231_REG_PIN_MODE1, 3),
+ RTL8231_LED_PIN_DESC(20, RTL8231_REG_PIN_MODE1, 4),
+ RTL8231_LED_PIN_DESC(21, RTL8231_REG_PIN_MODE1, 5),
+ RTL8231_LED_PIN_DESC(22, RTL8231_REG_PIN_MODE1, 6),
+ RTL8231_LED_PIN_DESC(23, RTL8231_REG_PIN_MODE1, 7),
+ RTL8231_LED_PIN_DESC(24, RTL8231_REG_PIN_MODE1, 8),
+ RTL8231_LED_PIN_DESC(25, RTL8231_REG_PIN_MODE1, 9),
+ RTL8231_LED_PIN_DESC(26, RTL8231_REG_PIN_MODE1, 10),
+ RTL8231_LED_PIN_DESC(27, RTL8231_REG_PIN_MODE1, 11),
+ RTL8231_LED_PIN_DESC(28, RTL8231_REG_PIN_MODE1, 12),
+ RTL8231_LED_PIN_DESC(29, RTL8231_REG_PIN_MODE1, 13),
+ RTL8231_LED_PIN_DESC(30, RTL8231_REG_PIN_MODE1, 14),
+ RTL8231_LED_PIN_DESC(31, RTL8231_REG_PIN_MODE1, 15),
+ RTL8231_LED_PIN_DESC(32, RTL8231_REG_PIN_HI_CFG, 0),
+ RTL8231_LED_PIN_DESC(33, RTL8231_REG_PIN_HI_CFG, 1),
+ RTL8231_LED_PIN_DESC(34, RTL8231_REG_PIN_HI_CFG, 2),
+ RTL8231_PWM_PIN_DESC(35, RTL8231_REG_FUNC1, 3),
+ RTL8231_GPIO_PIN_DESC(36, RTL8231_REG_PIN_HI_CFG, 4),
+};
+static const unsigned int PWM_PIN = 35;
+
+#define RTL8231_PIN(_num) \
+ ((struct pinctrl_pin_desc) { \
+ .number = (_num), \
+ .name = "gpio" #_num, \
+ .drv_data = (void *) &rtl8231_pin_data[(_num)] \
+ })
+
+static const struct pinctrl_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] = {
+ RTL8231_PIN(0),
+ RTL8231_PIN(1),
+ RTL8231_PIN(2),
+ RTL8231_PIN(3),
+ RTL8231_PIN(4),
+ RTL8231_PIN(5),
+ RTL8231_PIN(6),
+ RTL8231_PIN(7),
+ RTL8231_PIN(8),
+ RTL8231_PIN(9),
+ RTL8231_PIN(10),
+ RTL8231_PIN(11),
+ RTL8231_PIN(12),
+ RTL8231_PIN(13),
+ RTL8231_PIN(14),
+ RTL8231_PIN(15),
+ RTL8231_PIN(16),
+ RTL8231_PIN(17),
+ RTL8231_PIN(18),
+ RTL8231_PIN(19),
+ RTL8231_PIN(20),
+ RTL8231_PIN(21),
+ RTL8231_PIN(22),
+ RTL8231_PIN(23),
+ RTL8231_PIN(24),
+ RTL8231_PIN(25),
+ RTL8231_PIN(26),
+ RTL8231_PIN(27),
+ RTL8231_PIN(28),
+ RTL8231_PIN(29),
+ RTL8231_PIN(30),
+ RTL8231_PIN(31),
+ RTL8231_PIN(32),
+ RTL8231_PIN(33),
+ RTL8231_PIN(34),
+ RTL8231_PIN(35),
+ RTL8231_PIN(36),
+};
+
+static int rtl8231_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(rtl8231_pins);
+}
+
+static const char *rtl8231_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector)
+{
+ return rtl8231_pins[selector].name;
+}
+
+static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
+ const unsigned int **pins, unsigned int *num_pins)
+{
+ if (selector >= ARRAY_SIZE(rtl8231_pins))
+ return -EINVAL;
+
+ *pins = &rtl8231_pins[selector].number;
+ *num_pins = 1;
+
+ return 0;
+}
+
+static const struct pinctrl_ops rtl8231_pinctrl_ops = {
+ .get_groups_count = rtl8231_get_groups_count,
+ .get_group_name = rtl8231_get_group_name,
+ .get_group_pins = rtl8231_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static int rtl8231_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector,
+ unsigned int group_selector)
+{
+ const struct function_desc *func = pinmux_generic_get_function(pctldev, func_selector);
+ const struct rtl8231_pin_desc *desc = rtl8231_pins[group_selector].drv_data;
+ const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
+ enum rtl8231_pin_function func_flag = (uintptr_t) func->data;
+ unsigned int function_mask;
+ unsigned int gpio_function;
+
+ if (!(desc->functions & func_flag))
+ return -EINVAL;
+
+ function_mask = BIT(desc->offset);
+ gpio_function = desc->gpio_function_value << desc->offset;
+
+ if (func_flag == RTL8231_PIN_FUNCTION_GPIO)
+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function);
+ else
+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_function);
+}
+
+static int rtl8231_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range, unsigned int offset)
+{
+ const struct rtl8231_pin_desc *desc = rtl8231_pins[offset].drv_data;
+ const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int function_mask;
+ unsigned int gpio_function;
+
+ function_mask = BIT(desc->offset);
+ gpio_function = desc->gpio_function_value << desc->offset;
+
+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function);
+}
+
+static const struct pinmux_ops rtl8231_pinmux_ops = {
+ .get_functions_count = pinmux_generic_get_function_count,
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .function_is_gpio = pinmux_generic_function_is_gpio,
+ .set_mux = rtl8231_set_mux,
+ .gpio_request_enable = rtl8231_gpio_request_enable,
+ .strict = true,
+};
+
+static int rtl8231_pin_config_get(struct pinctrl_dev *pctldev, unsigned int offset,
+ unsigned long *config)
+{
+ const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int param = pinconf_to_config_param(*config);
+ unsigned int arg;
+ int err;
+ int v;
+
+ switch (param) {
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ if (offset < RTL8231_DEBOUNCE_MIN_OFFSET)
+ return -EINVAL;
+
+ err = regmap_read(ctrl->map, RTL8231_REG_FUNC1, &v);
+ if (err)
+ return err;
+
+ v = FIELD_GET(RTL8231_FUNC1_DEBOUNCE_MASK, v);
+ if (v & BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET))
+ arg = RTL8231_DEBOUNCE_USEC;
+ else
+ arg = 0;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+
+ return 0;
+}
+
+static int rtl8231_pin_config_set(struct pinctrl_dev *pctldev, unsigned int offset,
+ unsigned long *configs, unsigned int num_configs)
+{
+ const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int param, arg;
+ unsigned int pin_mask;
+ int err;
+ int i;
+
+ for (i = 0; i < num_configs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ if (offset < RTL8231_DEBOUNCE_MIN_OFFSET)
+ return -EINVAL;
+
+ pin_mask = FIELD_PREP(RTL8231_FUNC1_DEBOUNCE_MASK,
+ BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET));
+
+ switch (arg) {
+ case 0:
+ err = regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1,
+ pin_mask, 0);
+ break;
+ case RTL8231_DEBOUNCE_USEC:
+ err = regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1,
+ pin_mask, pin_mask);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ }
+
+ return err;
+}
+
+static const struct pinconf_ops rtl8231_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_get = rtl8231_pin_config_get,
+ .pin_config_set = rtl8231_pin_config_set,
+};
+
+static int rtl8231_pinctrl_init_functions(struct pinctrl_dev *pctl,
+ const struct pinctrl_desc *pctl_desc)
+{
+ enum rtl8231_pin_function flag;
+ struct pinfunction func;
+ const char **groups;
+ unsigned int f_idx;
+ const char *name;
+ unsigned int pin;
+ int num_groups;
+ int err;
+
+ for (f_idx = 0; f_idx < ARRAY_SIZE(rtl8231_pin_functions); f_idx++) {
+ name = rtl8231_pin_functions[f_idx].name;
+ flag = rtl8231_pin_functions[f_idx].flag;
+
+ for (pin = 0, num_groups = 0; pin < pctl_desc->npins; pin++)
+ if (rtl8231_pin_data[pin].functions & flag)
+ num_groups++;
+
+ groups = devm_kcalloc(pctl->dev, num_groups, sizeof(*groups), GFP_KERNEL);
+ if (!groups)
+ return -ENOMEM;
+
+ for (pin = 0, num_groups = 0; pin < pctl_desc->npins; pin++)
+ if (rtl8231_pin_data[pin].functions & flag)
+ groups[num_groups++] = rtl8231_pins[pin].name;
+
+ func = PINCTRL_PINFUNCTION(name, groups, num_groups);
+ if (flag == RTL8231_PIN_FUNCTION_GPIO)
+ func.flags |= PINFUNCTION_FLAG_GPIO;
+
+ err = pinmux_generic_add_pinfunction(pctl, &func, (void *) flag);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+struct pin_field_info {
+ const struct reg_field gpio_dir;
+ const struct reg_field mode;
+};
+
+static const struct pin_field_info pin_fields[] = {
+ {
+ .gpio_dir = REG_FIELD(RTL8231_REG_GPIO_DIR0, 0, 15),
+ .mode = REG_FIELD(RTL8231_REG_PIN_MODE0, 0, 15),
+ },
+ {
+ .gpio_dir = REG_FIELD(RTL8231_REG_GPIO_DIR1, 0, 15),
+ .mode = REG_FIELD(RTL8231_REG_PIN_MODE1, 0, 15),
+ },
+ {
+ .gpio_dir = REG_FIELD(RTL8231_REG_PIN_HI_CFG, 5, 9),
+ .mode = REG_FIELD(RTL8231_REG_PIN_HI_CFG, 0, 4),
+ },
+};
+
+static int rtl8231_configure_safe(struct device *dev, struct regmap *map)
+{
+ struct regmap_field *field_mode;
+ struct regmap_field *field_dir;
+ unsigned int is_input;
+ unsigned int is_gpio;
+ int err;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(pin_fields); i++) {
+ field_dir = devm_regmap_field_alloc(dev, map, pin_fields[i].gpio_dir);
+ if (IS_ERR(field_dir))
+ return PTR_ERR(field_dir);
+
+ field_mode = devm_regmap_field_alloc(dev, map, pin_fields[i].mode);
+ if (IS_ERR(field_mode))
+ return PTR_ERR(field_mode);
+
+ err = regmap_field_read(field_dir, &is_input);
+ if (err)
+ return err;
+
+ err = regmap_field_read(field_mode, &is_gpio);
+ if (err)
+ return err;
+
+ /* Enable field for PWM (on GPIO35) is in another register */
+ if (pin_fields[i].mode.reg == RTL8231_REG_PIN_HI_CFG) {
+ err = regmap_test_bits(map, rtl8231_pin_data[PWM_PIN].reg,
+ BIT(rtl8231_pin_data[PWM_PIN].offset));
+ if (err < 0)
+ return err;
+
+ if (err)
+ is_gpio &= ~BIT(PWM_PIN % RTL8231_BITS_VAL);
+ }
+
+ /*
+ * Set every pin that is not muxed as a GPIO to gpio-in. That
+ * way the pin will be high impedance when it is muxed to GPIO,
+ * preventing unwanted glitches.
+ * The pin muxes are left as-is, so there are no signal changes.
+ */
+ regmap_field_write(field_dir, is_input | ~is_gpio);
+
+ devm_regmap_field_free(dev, field_dir);
+ devm_regmap_field_free(dev, field_mode);
+ }
+
+ return 0;
+}
+
+static const struct pinctrl_desc rtl8231_pctl_desc = {
+ .name = "rtl8231-pinctrl",
+ .owner = THIS_MODULE,
+ .confops = &rtl8231_pinconf_ops,
+ .pctlops = &rtl8231_pinctrl_ops,
+ .pmxops = &rtl8231_pinmux_ops,
+ .npins = ARRAY_SIZE(rtl8231_pins),
+ .pins = rtl8231_pins,
+};
+
+static int rtl8231_pinctrl_init(struct device *dev, struct rtl8231_pin_ctrl *ctrl)
+{
+ struct pinctrl_dev *pctldev;
+ int err;
+
+ err = devm_pinctrl_register_and_init(dev->parent, &rtl8231_pctl_desc, ctrl, &pctldev);
+ if (err) {
+ dev_err(dev, "failed to register pin controller\n");
+ return err;
+ }
+
+ err = rtl8231_pinctrl_init_functions(pctldev, &rtl8231_pctl_desc);
+ if (err)
+ return err;
+
+ err = pinctrl_enable(pctldev);
+ if (err)
+ dev_err(dev, "failed to enable pin controller\n");
+
+ return err;
+}
+
+/*
+ * GPIO controller functionality
+ */
+static int rtl8231_gpio_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
+ unsigned int offset, unsigned int *reg, unsigned int *mask)
+{
+ unsigned int pin_mask = BIT(offset % RTL8231_BITS_VAL);
+
+ if (base == RTL8231_REG_GPIO_DATA0 || offset < 32) {
+ *reg = base + offset / RTL8231_BITS_VAL;
+ *mask = pin_mask;
+ } else if (base == RTL8231_REG_GPIO_DIR0) {
+ *reg = RTL8231_REG_PIN_HI_CFG;
+ *mask = FIELD_PREP(RTL8231_PIN_HI_CFG_DIR_MASK, pin_mask);
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtl8231_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rtl8231_pin_ctrl *ctrl;
+ struct gpio_regmap_config gpio_cfg = {};
+ int err;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ ctrl->map = dev_get_regmap(dev->parent, NULL);
+ if (!ctrl->map)
+ return -ENODEV;
+
+ err = rtl8231_configure_safe(dev, ctrl->map);
+ if (err)
+ return err;
+
+ err = rtl8231_pinctrl_init(dev, ctrl);
+ if (err)
+ return err;
+
+ gpio_cfg.regmap = ctrl->map;
+ gpio_cfg.parent = dev->parent;
+ gpio_cfg.ngpio = RTL8231_NUM_GPIOS;
+ gpio_cfg.ngpio_per_reg = RTL8231_BITS_VAL;
+
+ gpio_cfg.reg_dat_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0);
+ gpio_cfg.reg_set_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0);
+ gpio_cfg.reg_dir_in_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DIR0);
+
+ gpio_cfg.reg_mask_xlate = rtl8231_gpio_reg_mask_xlate;
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_cfg));
+}
+
+static struct platform_driver rtl8231_pinctrl_driver = {
+ .driver = {
+ .name = "rtl8231-pinctrl",
+ },
+ .probe = rtl8231_pinctrl_probe,
+};
+module_platform_driver(rtl8231_pinctrl_driver);
+
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek RTL8231 pin control and GPIO support");
+MODULE_LICENSE("GPL");
--
2.51.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
2025-11-17 21:51 ` [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support Sander Vanheule
@ 2025-11-18 21:19 ` kernel test robot
2025-11-18 22:24 ` Sander Vanheule
2025-11-19 4:19 ` kernel test robot
2025-11-19 19:41 ` kernel test robot
2 siblings, 1 reply; 19+ messages in thread
From: kernel test robot @ 2025-11-18 21:19 UTC (permalink / raw)
To: Sander Vanheule, Lee Jones, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Michael Walle,
Bartosz Golaszewski
Cc: oe-kbuild-all, linux-leds, devicetree, linux-kernel, linux-gpio,
Sander Vanheule
Hi Sander,
kernel test robot noticed the following build errors:
[auto build test ERROR on lee-mfd/for-mfd-next]
[also build test ERROR on lee-mfd/for-mfd-fixes lee-leds/for-leds-next linusw-pinctrl/devel linusw-pinctrl/for-next linus/master v6.18-rc6 next-20251118]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sander-Vanheule/dt-bindings-leds-Binding-for-RTL8231-scan-matrix/20251118-055707
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
patch link: https://lore.kernel.org/r/20251117215138.4353-5-sander%40svanheule.net
patch subject: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20251119/202511190436.9oQLbI8A-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251119/202511190436.9oQLbI8A-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/202511190436.9oQLbI8A-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
>> drivers/pinctrl/pinctrl-rtl8231.c:28:27: error: expected identifier or '(' before ':' token
28 | enum rtl8231_pin_function : uintptr_t {
| ^
>> drivers/pinctrl/pinctrl-rtl8231.c:35:35: error: field 'flag' has incomplete type
35 | enum rtl8231_pin_function flag;
| ^~~~
>> drivers/pinctrl/pinctrl-rtl8231.c:46:34: error: 'RTL8231_PIN_FUNCTION_GPIO' undeclared here (not in a function); did you mean 'RTL8231_PIN_MODE_GPIO'?
46 | RTL8231_FUNCTION("gpio", RTL8231_PIN_FUNCTION_GPIO),
| ^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/pinctrl-rtl8231.c:41:26: note: in definition of macro 'RTL8231_FUNCTION'
41 | .flag = (_flag), \
| ^~~~~
>> drivers/pinctrl/pinctrl-rtl8231.c:47:33: error: 'RTL8231_PIN_FUNCTION_LED' undeclared here (not in a function); did you mean 'RTL8231_FUNCTION'?
47 | RTL8231_FUNCTION("led", RTL8231_PIN_FUNCTION_LED),
| ^~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/pinctrl-rtl8231.c:41:26: note: in definition of macro 'RTL8231_FUNCTION'
41 | .flag = (_flag), \
| ^~~~~
>> drivers/pinctrl/pinctrl-rtl8231.c:48:33: error: 'RTL8231_PIN_FUNCTION_PWM' undeclared here (not in a function); did you mean 'RTL8231_FUNCTION'?
48 | RTL8231_FUNCTION("pwm", RTL8231_PIN_FUNCTION_PWM),
| ^~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/pinctrl-rtl8231.c:41:26: note: in definition of macro 'RTL8231_FUNCTION'
41 | .flag = (_flag), \
| ^~~~~
>> drivers/pinctrl/pinctrl-rtl8231.c:52:35: warning: 'functions' is narrower than values of its type
52 | enum rtl8231_pin_function functions:8;
| ^~~~~~~~~
>> drivers/pinctrl/pinctrl-rtl8231.c:52:35: error: field 'functions' has incomplete type
drivers/pinctrl/pinctrl-rtl8231.c: In function 'rtl8231_set_mux':
>> drivers/pinctrl/pinctrl-rtl8231.c:200:14: error: variable 'func_flag' has initializer but incomplete type
200 | enum rtl8231_pin_function func_flag = (uintptr_t) func->data;
| ^~~~~~~~~~~~~~~~~~~~
>> drivers/pinctrl/pinctrl-rtl8231.c:200:35: error: storage size of 'func_flag' isn't known
200 | enum rtl8231_pin_function func_flag = (uintptr_t) func->data;
| ^~~~~~~~~
>> drivers/pinctrl/pinctrl-rtl8231.c:200:35: warning: unused variable 'func_flag' [-Wunused-variable]
drivers/pinctrl/pinctrl-rtl8231.c: In function 'rtl8231_pinctrl_init_functions':
>> drivers/pinctrl/pinctrl-rtl8231.c:325:35: error: storage size of 'flag' isn't known
325 | enum rtl8231_pin_function flag;
| ^~~~
>> drivers/pinctrl/pinctrl-rtl8231.c:325:35: warning: unused variable 'flag' [-Wunused-variable]
drivers/pinctrl/pinctrl-rtl8231.c: In function 'rtl8231_set_mux':
>> drivers/pinctrl/pinctrl-rtl8231.c:214:1: warning: control reaches end of non-void function [-Wreturn-type]
214 | }
| ^
vim +28 drivers/pinctrl/pinctrl-rtl8231.c
24
25 /*
26 * Pin controller functionality
27 */
> 28 enum rtl8231_pin_function : uintptr_t {
29 RTL8231_PIN_FUNCTION_GPIO = BIT(0),
30 RTL8231_PIN_FUNCTION_LED = BIT(1),
31 RTL8231_PIN_FUNCTION_PWM = BIT(2),
32 };
33
34 struct rtl8231_function_info {
> 35 enum rtl8231_pin_function flag;
36 const char *name;
37 };
38
39 #define RTL8231_FUNCTION(_name, _flag) \
40 ((struct rtl8231_function_info) { \
41 .flag = (_flag), \
42 .name = (_name), \
43 })
44
45 static const struct rtl8231_function_info rtl8231_pin_functions[] = {
> 46 RTL8231_FUNCTION("gpio", RTL8231_PIN_FUNCTION_GPIO),
> 47 RTL8231_FUNCTION("led", RTL8231_PIN_FUNCTION_LED),
> 48 RTL8231_FUNCTION("pwm", RTL8231_PIN_FUNCTION_PWM),
49 };
50
51 struct rtl8231_pin_desc {
> 52 enum rtl8231_pin_function functions:8;
53 u8 reg;
54 u8 offset;
55 u8 gpio_function_value;
56 };
57
58 #define RTL8231_PIN_DESC(_num, _func, _reg, _fld, _val) \
59 [(_num)] = ((struct rtl8231_pin_desc) { \
60 .functions = RTL8231_PIN_FUNCTION_GPIO | (_func), \
61 .reg = (_reg), \
62 .offset = (_fld), \
63 .gpio_function_value = (_val), \
64 })
65 #define RTL8231_GPIO_PIN_DESC(_num, _reg, _fld) \
66 RTL8231_PIN_DESC(_num, 0, _reg, _fld, RTL8231_PIN_MODE_GPIO)
67 #define RTL8231_LED_PIN_DESC(_num, _reg, _fld) \
68 RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_LED, _reg, _fld, RTL8231_PIN_MODE_GPIO)
69 #define RTL8231_PWM_PIN_DESC(_num, _reg, _fld) \
70 RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_PWM, _reg, _fld, 0)
71
72 /*
73 * All pins have a GPIO/LED mux bit, but the bits for pins 35/36 are read-only. Use this bit
74 * for the GPIO-only pin instead of a placeholder, so the rest of the logic can stay generic.
75 */
76 static const struct rtl8231_pin_desc rtl8231_pin_data[RTL8231_NUM_GPIOS] = {
77 RTL8231_LED_PIN_DESC(0, RTL8231_REG_PIN_MODE0, 0),
78 RTL8231_LED_PIN_DESC(1, RTL8231_REG_PIN_MODE0, 1),
79 RTL8231_LED_PIN_DESC(2, RTL8231_REG_PIN_MODE0, 2),
80 RTL8231_LED_PIN_DESC(3, RTL8231_REG_PIN_MODE0, 3),
81 RTL8231_LED_PIN_DESC(4, RTL8231_REG_PIN_MODE0, 4),
82 RTL8231_LED_PIN_DESC(5, RTL8231_REG_PIN_MODE0, 5),
83 RTL8231_LED_PIN_DESC(6, RTL8231_REG_PIN_MODE0, 6),
84 RTL8231_LED_PIN_DESC(7, RTL8231_REG_PIN_MODE0, 7),
85 RTL8231_LED_PIN_DESC(8, RTL8231_REG_PIN_MODE0, 8),
86 RTL8231_LED_PIN_DESC(9, RTL8231_REG_PIN_MODE0, 9),
87 RTL8231_LED_PIN_DESC(10, RTL8231_REG_PIN_MODE0, 10),
88 RTL8231_LED_PIN_DESC(11, RTL8231_REG_PIN_MODE0, 11),
89 RTL8231_LED_PIN_DESC(12, RTL8231_REG_PIN_MODE0, 12),
90 RTL8231_LED_PIN_DESC(13, RTL8231_REG_PIN_MODE0, 13),
91 RTL8231_LED_PIN_DESC(14, RTL8231_REG_PIN_MODE0, 14),
92 RTL8231_LED_PIN_DESC(15, RTL8231_REG_PIN_MODE0, 15),
93 RTL8231_LED_PIN_DESC(16, RTL8231_REG_PIN_MODE1, 0),
94 RTL8231_LED_PIN_DESC(17, RTL8231_REG_PIN_MODE1, 1),
95 RTL8231_LED_PIN_DESC(18, RTL8231_REG_PIN_MODE1, 2),
96 RTL8231_LED_PIN_DESC(19, RTL8231_REG_PIN_MODE1, 3),
97 RTL8231_LED_PIN_DESC(20, RTL8231_REG_PIN_MODE1, 4),
98 RTL8231_LED_PIN_DESC(21, RTL8231_REG_PIN_MODE1, 5),
99 RTL8231_LED_PIN_DESC(22, RTL8231_REG_PIN_MODE1, 6),
100 RTL8231_LED_PIN_DESC(23, RTL8231_REG_PIN_MODE1, 7),
101 RTL8231_LED_PIN_DESC(24, RTL8231_REG_PIN_MODE1, 8),
102 RTL8231_LED_PIN_DESC(25, RTL8231_REG_PIN_MODE1, 9),
103 RTL8231_LED_PIN_DESC(26, RTL8231_REG_PIN_MODE1, 10),
104 RTL8231_LED_PIN_DESC(27, RTL8231_REG_PIN_MODE1, 11),
105 RTL8231_LED_PIN_DESC(28, RTL8231_REG_PIN_MODE1, 12),
106 RTL8231_LED_PIN_DESC(29, RTL8231_REG_PIN_MODE1, 13),
107 RTL8231_LED_PIN_DESC(30, RTL8231_REG_PIN_MODE1, 14),
108 RTL8231_LED_PIN_DESC(31, RTL8231_REG_PIN_MODE1, 15),
109 RTL8231_LED_PIN_DESC(32, RTL8231_REG_PIN_HI_CFG, 0),
110 RTL8231_LED_PIN_DESC(33, RTL8231_REG_PIN_HI_CFG, 1),
111 RTL8231_LED_PIN_DESC(34, RTL8231_REG_PIN_HI_CFG, 2),
112 RTL8231_PWM_PIN_DESC(35, RTL8231_REG_FUNC1, 3),
113 RTL8231_GPIO_PIN_DESC(36, RTL8231_REG_PIN_HI_CFG, 4),
114 };
115 static const unsigned int PWM_PIN = 35;
116
117 #define RTL8231_PIN(_num) \
118 ((struct pinctrl_pin_desc) { \
119 .number = (_num), \
120 .name = "gpio" #_num, \
121 .drv_data = (void *) &rtl8231_pin_data[(_num)] \
122 })
123
124 static const struct pinctrl_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] = {
125 RTL8231_PIN(0),
126 RTL8231_PIN(1),
127 RTL8231_PIN(2),
128 RTL8231_PIN(3),
129 RTL8231_PIN(4),
130 RTL8231_PIN(5),
131 RTL8231_PIN(6),
132 RTL8231_PIN(7),
133 RTL8231_PIN(8),
134 RTL8231_PIN(9),
135 RTL8231_PIN(10),
136 RTL8231_PIN(11),
137 RTL8231_PIN(12),
138 RTL8231_PIN(13),
139 RTL8231_PIN(14),
140 RTL8231_PIN(15),
141 RTL8231_PIN(16),
142 RTL8231_PIN(17),
143 RTL8231_PIN(18),
144 RTL8231_PIN(19),
145 RTL8231_PIN(20),
146 RTL8231_PIN(21),
147 RTL8231_PIN(22),
148 RTL8231_PIN(23),
149 RTL8231_PIN(24),
150 RTL8231_PIN(25),
151 RTL8231_PIN(26),
152 RTL8231_PIN(27),
153 RTL8231_PIN(28),
154 RTL8231_PIN(29),
155 RTL8231_PIN(30),
156 RTL8231_PIN(31),
157 RTL8231_PIN(32),
158 RTL8231_PIN(33),
159 RTL8231_PIN(34),
160 RTL8231_PIN(35),
161 RTL8231_PIN(36),
162 };
163
164 static int rtl8231_get_groups_count(struct pinctrl_dev *pctldev)
165 {
166 return ARRAY_SIZE(rtl8231_pins);
167 }
168
169 static const char *rtl8231_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector)
170 {
171 return rtl8231_pins[selector].name;
172 }
173
174 static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
175 const unsigned int **pins, unsigned int *num_pins)
176 {
177 if (selector >= ARRAY_SIZE(rtl8231_pins))
178 return -EINVAL;
179
180 *pins = &rtl8231_pins[selector].number;
181 *num_pins = 1;
182
183 return 0;
184 }
185
186 static const struct pinctrl_ops rtl8231_pinctrl_ops = {
187 .get_groups_count = rtl8231_get_groups_count,
188 .get_group_name = rtl8231_get_group_name,
189 .get_group_pins = rtl8231_get_group_pins,
190 .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
191 .dt_free_map = pinconf_generic_dt_free_map,
192 };
193
194 static int rtl8231_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector,
195 unsigned int group_selector)
196 {
197 const struct function_desc *func = pinmux_generic_get_function(pctldev, func_selector);
198 const struct rtl8231_pin_desc *desc = rtl8231_pins[group_selector].drv_data;
199 const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
> 200 enum rtl8231_pin_function func_flag = (uintptr_t) func->data;
201 unsigned int function_mask;
202 unsigned int gpio_function;
203
204 if (!(desc->functions & func_flag))
205 return -EINVAL;
206
207 function_mask = BIT(desc->offset);
208 gpio_function = desc->gpio_function_value << desc->offset;
209
210 if (func_flag == RTL8231_PIN_FUNCTION_GPIO)
211 return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function);
212 else
213 return regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_function);
> 214 }
215
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
2025-11-18 21:19 ` kernel test robot
@ 2025-11-18 22:24 ` Sander Vanheule
0 siblings, 0 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-18 22:24 UTC (permalink / raw)
To: kernel test robot, Lee Jones, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Michael Walle,
Bartosz Golaszewski
Cc: oe-kbuild-all, linux-leds, devicetree, linux-kernel, linux-gpio
On Wed, 2025-11-19 at 05:19 +0800, kernel test robot wrote:
> Hi Sander,
>
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on lee-mfd/for-mfd-next]
> [also build test ERROR on lee-mfd/for-mfd-fixes lee-leds/for-leds-next linusw-
> pinctrl/devel linusw-pinctrl/for-next linus/master v6.18-rc6 next-20251118]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url:
> https://github.com/intel-lab-lkp/linux/commits/Sander-Vanheule/dt-bindings-leds-Binding-for-RTL8231-scan-matrix/20251118-055707
> base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
> patch link: https://lore.kernel.org/r/20251117215138.4353-5-sander%40svanheule.net
> patch subject: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
> config: nios2-allmodconfig
> (https://download.01.org/0day-ci/archive/20251119/202511190436.9oQLbI8A-lkp@intel.com/co
> nfig)
> compiler: nios2-linux-gcc (GCC) 11.5.0
> reproduce (this is a W=1 build):
> (https://download.01.org/0day-ci/archive/20251119/202511190436.9oQLbI8A-lkp@intel.com/re
> produce)
>
> 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/202511190436.9oQLbI8A-lkp@intel.com/
>
> All error/warnings (new ones prefixed by >>):
>
> > > drivers/pinctrl/pinctrl-rtl8231.c:28:27: error: expected identifier or '(' before
> > > ':' token
> 28 | enum rtl8231_pin_function : uintptr_t {
> | ^
This isn't supported yet pre-C23 and before GCC 13. I will look to ensure the a properly
sized integer is stored into the pinfunction's data pointer.
Best,
Sander
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
2025-11-17 21:51 ` [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support Sander Vanheule
2025-11-18 21:19 ` kernel test robot
@ 2025-11-19 4:19 ` kernel test robot
2025-11-19 18:57 ` Sander Vanheule
2025-11-19 19:41 ` kernel test robot
2 siblings, 1 reply; 19+ messages in thread
From: kernel test robot @ 2025-11-19 4:19 UTC (permalink / raw)
To: Sander Vanheule, Lee Jones, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Michael Walle,
Bartosz Golaszewski
Cc: oe-kbuild-all, linux-leds, devicetree, linux-kernel, linux-gpio,
Sander Vanheule
Hi Sander,
kernel test robot noticed the following build warnings:
[auto build test WARNING on lee-mfd/for-mfd-next]
[also build test WARNING on lee-mfd/for-mfd-fixes lee-leds/for-leds-next linusw-pinctrl/devel linusw-pinctrl/for-next linus/master v6.18-rc6 next-20251118]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sander-Vanheule/dt-bindings-leds-Binding-for-RTL8231-scan-matrix/20251118-055707
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
patch link: https://lore.kernel.org/r/20251117215138.4353-5-sander%40svanheule.net
patch subject: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
config: i386-randconfig-063-20251119 (https://download.01.org/0day-ci/archive/20251119/202511191158.bVKUDrKa-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251119/202511191158.bVKUDrKa-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/202511191158.bVKUDrKa-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/pinctrl/pinctrl-rtl8231.c:28:27: sparse: sparse: missing identifier in declaration
drivers/pinctrl/pinctrl-rtl8231.c:28:27: sparse: sparse: Expected ; at the end of type declaration
drivers/pinctrl/pinctrl-rtl8231.c:28:27: sparse: sparse: got :
>> drivers/pinctrl/pinctrl-rtl8231.c:52:44: sparse: sparse: invalid bitfield specifier for type incomplete type enum rtl8231_pin_function.
drivers/pinctrl/pinctrl-rtl8231.c:46:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:47:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:48:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_PWM'
drivers/pinctrl/pinctrl-rtl8231.c:77:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:77:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:78:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:78:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:79:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:79:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:80:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:80:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:81:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:81:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:82:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:82:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:83:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:83:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:84:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:84:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:85:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:85:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:86:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:86:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:87:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:87:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:88:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:88:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:89:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:89:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:90:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:90:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:91:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:91:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:92:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:92:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:93:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:93:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:94:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:94:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:95:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:95:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:96:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:96:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:97:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:97:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:98:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:98:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:99:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:99:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:100:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:100:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:101:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:101:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:102:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:102:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:103:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:103:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:104:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:104:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:105:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:105:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:106:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:106:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:107:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:107:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:108:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:108:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:109:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:109:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:110:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:110:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:111:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:111:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_LED'
drivers/pinctrl/pinctrl-rtl8231.c:112:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:112:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_PWM'
drivers/pinctrl/pinctrl-rtl8231.c:113:9: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
>> drivers/pinctrl/pinctrl-rtl8231.c:204:31: sparse: sparse: incompatible types for operation (&):
drivers/pinctrl/pinctrl-rtl8231.c:204:31: sparse: incomplete type enum rtl8231_pin_function const functions
drivers/pinctrl/pinctrl-rtl8231.c:204:31: sparse: incomplete type enum rtl8231_pin_function func_flag
drivers/pinctrl/pinctrl-rtl8231.c:210:26: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
drivers/pinctrl/pinctrl-rtl8231.c:339:61: sparse: sparse: incompatible types for operation (&):
drivers/pinctrl/pinctrl-rtl8231.c:339:61: sparse: incomplete type enum rtl8231_pin_function const functions
drivers/pinctrl/pinctrl-rtl8231.c:339:61: sparse: incomplete type enum rtl8231_pin_function flag
drivers/pinctrl/pinctrl-rtl8231.c:347:61: sparse: sparse: incompatible types for operation (&):
drivers/pinctrl/pinctrl-rtl8231.c:347:61: sparse: incomplete type enum rtl8231_pin_function const functions
drivers/pinctrl/pinctrl-rtl8231.c:347:61: sparse: incomplete type enum rtl8231_pin_function flag
drivers/pinctrl/pinctrl-rtl8231.c:351:29: sparse: sparse: undefined identifier 'RTL8231_PIN_FUNCTION_GPIO'
vim +28 drivers/pinctrl/pinctrl-rtl8231.c
24
25 /*
26 * Pin controller functionality
27 */
> 28 enum rtl8231_pin_function : uintptr_t {
29 RTL8231_PIN_FUNCTION_GPIO = BIT(0),
30 RTL8231_PIN_FUNCTION_LED = BIT(1),
31 RTL8231_PIN_FUNCTION_PWM = BIT(2),
32 };
33
34 struct rtl8231_function_info {
35 enum rtl8231_pin_function flag;
36 const char *name;
37 };
38
39 #define RTL8231_FUNCTION(_name, _flag) \
40 ((struct rtl8231_function_info) { \
41 .flag = (_flag), \
42 .name = (_name), \
43 })
44
45 static const struct rtl8231_function_info rtl8231_pin_functions[] = {
46 RTL8231_FUNCTION("gpio", RTL8231_PIN_FUNCTION_GPIO),
47 RTL8231_FUNCTION("led", RTL8231_PIN_FUNCTION_LED),
48 RTL8231_FUNCTION("pwm", RTL8231_PIN_FUNCTION_PWM),
49 };
50
51 struct rtl8231_pin_desc {
> 52 enum rtl8231_pin_function functions:8;
53 u8 reg;
54 u8 offset;
55 u8 gpio_function_value;
56 };
57
58 #define RTL8231_PIN_DESC(_num, _func, _reg, _fld, _val) \
59 [(_num)] = ((struct rtl8231_pin_desc) { \
60 .functions = RTL8231_PIN_FUNCTION_GPIO | (_func), \
61 .reg = (_reg), \
62 .offset = (_fld), \
63 .gpio_function_value = (_val), \
64 })
65 #define RTL8231_GPIO_PIN_DESC(_num, _reg, _fld) \
66 RTL8231_PIN_DESC(_num, 0, _reg, _fld, RTL8231_PIN_MODE_GPIO)
67 #define RTL8231_LED_PIN_DESC(_num, _reg, _fld) \
68 RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_LED, _reg, _fld, RTL8231_PIN_MODE_GPIO)
69 #define RTL8231_PWM_PIN_DESC(_num, _reg, _fld) \
70 RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_PWM, _reg, _fld, 0)
71
72 /*
73 * All pins have a GPIO/LED mux bit, but the bits for pins 35/36 are read-only. Use this bit
74 * for the GPIO-only pin instead of a placeholder, so the rest of the logic can stay generic.
75 */
76 static const struct rtl8231_pin_desc rtl8231_pin_data[RTL8231_NUM_GPIOS] = {
77 RTL8231_LED_PIN_DESC(0, RTL8231_REG_PIN_MODE0, 0),
78 RTL8231_LED_PIN_DESC(1, RTL8231_REG_PIN_MODE0, 1),
79 RTL8231_LED_PIN_DESC(2, RTL8231_REG_PIN_MODE0, 2),
80 RTL8231_LED_PIN_DESC(3, RTL8231_REG_PIN_MODE0, 3),
81 RTL8231_LED_PIN_DESC(4, RTL8231_REG_PIN_MODE0, 4),
82 RTL8231_LED_PIN_DESC(5, RTL8231_REG_PIN_MODE0, 5),
83 RTL8231_LED_PIN_DESC(6, RTL8231_REG_PIN_MODE0, 6),
84 RTL8231_LED_PIN_DESC(7, RTL8231_REG_PIN_MODE0, 7),
85 RTL8231_LED_PIN_DESC(8, RTL8231_REG_PIN_MODE0, 8),
86 RTL8231_LED_PIN_DESC(9, RTL8231_REG_PIN_MODE0, 9),
87 RTL8231_LED_PIN_DESC(10, RTL8231_REG_PIN_MODE0, 10),
88 RTL8231_LED_PIN_DESC(11, RTL8231_REG_PIN_MODE0, 11),
89 RTL8231_LED_PIN_DESC(12, RTL8231_REG_PIN_MODE0, 12),
90 RTL8231_LED_PIN_DESC(13, RTL8231_REG_PIN_MODE0, 13),
91 RTL8231_LED_PIN_DESC(14, RTL8231_REG_PIN_MODE0, 14),
92 RTL8231_LED_PIN_DESC(15, RTL8231_REG_PIN_MODE0, 15),
93 RTL8231_LED_PIN_DESC(16, RTL8231_REG_PIN_MODE1, 0),
94 RTL8231_LED_PIN_DESC(17, RTL8231_REG_PIN_MODE1, 1),
95 RTL8231_LED_PIN_DESC(18, RTL8231_REG_PIN_MODE1, 2),
96 RTL8231_LED_PIN_DESC(19, RTL8231_REG_PIN_MODE1, 3),
97 RTL8231_LED_PIN_DESC(20, RTL8231_REG_PIN_MODE1, 4),
98 RTL8231_LED_PIN_DESC(21, RTL8231_REG_PIN_MODE1, 5),
99 RTL8231_LED_PIN_DESC(22, RTL8231_REG_PIN_MODE1, 6),
100 RTL8231_LED_PIN_DESC(23, RTL8231_REG_PIN_MODE1, 7),
101 RTL8231_LED_PIN_DESC(24, RTL8231_REG_PIN_MODE1, 8),
102 RTL8231_LED_PIN_DESC(25, RTL8231_REG_PIN_MODE1, 9),
103 RTL8231_LED_PIN_DESC(26, RTL8231_REG_PIN_MODE1, 10),
104 RTL8231_LED_PIN_DESC(27, RTL8231_REG_PIN_MODE1, 11),
105 RTL8231_LED_PIN_DESC(28, RTL8231_REG_PIN_MODE1, 12),
106 RTL8231_LED_PIN_DESC(29, RTL8231_REG_PIN_MODE1, 13),
107 RTL8231_LED_PIN_DESC(30, RTL8231_REG_PIN_MODE1, 14),
108 RTL8231_LED_PIN_DESC(31, RTL8231_REG_PIN_MODE1, 15),
109 RTL8231_LED_PIN_DESC(32, RTL8231_REG_PIN_HI_CFG, 0),
110 RTL8231_LED_PIN_DESC(33, RTL8231_REG_PIN_HI_CFG, 1),
111 RTL8231_LED_PIN_DESC(34, RTL8231_REG_PIN_HI_CFG, 2),
112 RTL8231_PWM_PIN_DESC(35, RTL8231_REG_FUNC1, 3),
113 RTL8231_GPIO_PIN_DESC(36, RTL8231_REG_PIN_HI_CFG, 4),
114 };
115 static const unsigned int PWM_PIN = 35;
116
117 #define RTL8231_PIN(_num) \
118 ((struct pinctrl_pin_desc) { \
119 .number = (_num), \
120 .name = "gpio" #_num, \
121 .drv_data = (void *) &rtl8231_pin_data[(_num)] \
122 })
123
124 static const struct pinctrl_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] = {
125 RTL8231_PIN(0),
126 RTL8231_PIN(1),
127 RTL8231_PIN(2),
128 RTL8231_PIN(3),
129 RTL8231_PIN(4),
130 RTL8231_PIN(5),
131 RTL8231_PIN(6),
132 RTL8231_PIN(7),
133 RTL8231_PIN(8),
134 RTL8231_PIN(9),
135 RTL8231_PIN(10),
136 RTL8231_PIN(11),
137 RTL8231_PIN(12),
138 RTL8231_PIN(13),
139 RTL8231_PIN(14),
140 RTL8231_PIN(15),
141 RTL8231_PIN(16),
142 RTL8231_PIN(17),
143 RTL8231_PIN(18),
144 RTL8231_PIN(19),
145 RTL8231_PIN(20),
146 RTL8231_PIN(21),
147 RTL8231_PIN(22),
148 RTL8231_PIN(23),
149 RTL8231_PIN(24),
150 RTL8231_PIN(25),
151 RTL8231_PIN(26),
152 RTL8231_PIN(27),
153 RTL8231_PIN(28),
154 RTL8231_PIN(29),
155 RTL8231_PIN(30),
156 RTL8231_PIN(31),
157 RTL8231_PIN(32),
158 RTL8231_PIN(33),
159 RTL8231_PIN(34),
160 RTL8231_PIN(35),
161 RTL8231_PIN(36),
162 };
163
164 static int rtl8231_get_groups_count(struct pinctrl_dev *pctldev)
165 {
166 return ARRAY_SIZE(rtl8231_pins);
167 }
168
169 static const char *rtl8231_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector)
170 {
171 return rtl8231_pins[selector].name;
172 }
173
174 static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
175 const unsigned int **pins, unsigned int *num_pins)
176 {
177 if (selector >= ARRAY_SIZE(rtl8231_pins))
178 return -EINVAL;
179
180 *pins = &rtl8231_pins[selector].number;
181 *num_pins = 1;
182
183 return 0;
184 }
185
186 static const struct pinctrl_ops rtl8231_pinctrl_ops = {
187 .get_groups_count = rtl8231_get_groups_count,
188 .get_group_name = rtl8231_get_group_name,
189 .get_group_pins = rtl8231_get_group_pins,
190 .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
191 .dt_free_map = pinconf_generic_dt_free_map,
192 };
193
194 static int rtl8231_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector,
195 unsigned int group_selector)
196 {
197 const struct function_desc *func = pinmux_generic_get_function(pctldev, func_selector);
198 const struct rtl8231_pin_desc *desc = rtl8231_pins[group_selector].drv_data;
199 const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
200 enum rtl8231_pin_function func_flag = (uintptr_t) func->data;
201 unsigned int function_mask;
202 unsigned int gpio_function;
203
> 204 if (!(desc->functions & func_flag))
205 return -EINVAL;
206
207 function_mask = BIT(desc->offset);
208 gpio_function = desc->gpio_function_value << desc->offset;
209
210 if (func_flag == RTL8231_PIN_FUNCTION_GPIO)
211 return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function);
212 else
213 return regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_function);
214 }
215
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
2025-11-19 4:19 ` kernel test robot
@ 2025-11-19 18:57 ` Sander Vanheule
0 siblings, 0 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-19 18:57 UTC (permalink / raw)
To: kernel test robot, Lee Jones, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Michael Walle,
Bartosz Golaszewski
Cc: oe-kbuild-all, linux-leds, devicetree, linux-kernel, linux-gpio
On Wed, 2025-11-19 at 12:19 +0800, kernel test robot wrote:
> Hi Sander,
>
> kernel test robot noticed the following build warnings:
>
> [auto build test WARNING on lee-mfd/for-mfd-next]
> [also build test WARNING on lee-mfd/for-mfd-fixes lee-leds/for-leds-next linusw-
> pinctrl/devel linusw-pinctrl/for-next linus/master v6.18-rc6 next-20251118]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url:
> https://github.com/intel-lab-lkp/linux/commits/Sander-Vanheule/dt-bindings-leds-Binding-for-RTL8231-scan-matrix/20251118-055707
> base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
> patch link: https://lore.kernel.org/r/20251117215138.4353-5-sander%40svanheule.net
> patch subject: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
> config: i386-randconfig-063-20251119
> (https://download.01.org/0day-ci/archive/20251119/202511191158.bVKUDrKa-lkp@intel.com/co
> nfig)
> compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
> reproduce (this is a W=1 build):
> (https://download.01.org/0day-ci/archive/20251119/202511191158.bVKUDrKa-lkp@intel.com/re
> produce)
>
> 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/202511191158.bVKUDrKa-lkp@intel.com/
>
> sparse warnings: (new ones prefixed by >>)
> > > drivers/pinctrl/pinctrl-rtl8231.c:28:27: sparse: sparse: missing identifier in
> > > declaration
> drivers/pinctrl/pinctrl-rtl8231.c:28:27: sparse: sparse: Expected ; at the end of
> type declaration
> drivers/pinctrl/pinctrl-rtl8231.c:28:27: sparse: sparse: got :
> > > drivers/pinctrl/pinctrl-rtl8231.c:52:44: sparse: sparse: invalid bitfield specifier
> > > for type incomplete type enum rtl8231_pin_function.
sparse doesn't seem to understand the enum type specifier either. Fixed by instead casting
rtl8231_pin_function where a uintptr_t is required.
Best,
Sander
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
2025-11-17 21:51 ` [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support Sander Vanheule
2025-11-18 21:19 ` kernel test robot
2025-11-19 4:19 ` kernel test robot
@ 2025-11-19 19:41 ` kernel test robot
2 siblings, 0 replies; 19+ messages in thread
From: kernel test robot @ 2025-11-19 19:41 UTC (permalink / raw)
To: Sander Vanheule, Lee Jones, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Michael Walle,
Bartosz Golaszewski
Cc: oe-kbuild-all, linux-leds, devicetree, linux-kernel, linux-gpio,
Sander Vanheule
Hi Sander,
kernel test robot noticed the following build errors:
[auto build test ERROR on lee-mfd/for-mfd-next]
[also build test ERROR on lee-mfd/for-mfd-fixes lee-leds/for-leds-next linusw-pinctrl/devel linusw-pinctrl/for-next linus/master v6.18-rc6 next-20251119]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sander-Vanheule/dt-bindings-leds-Binding-for-RTL8231-scan-matrix/20251118-055707
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
patch link: https://lore.kernel.org/r/20251117215138.4353-5-sander%40svanheule.net
patch subject: [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support
config: x86_64-randconfig-122-20251119 (https://download.01.org/0day-ci/archive/20251120/202511200318.xcfHOO5R-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251120/202511200318.xcfHOO5R-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/202511200318.xcfHOO5R-lkp@intel.com/
All errors (new ones prefixed by >>):
ld: drivers/pinctrl/pinctrl-rtl8231.o: in function `pinconf_generic_dt_node_to_map_all':
include/linux/pinctrl/pinconf-generic.h:235:(.text+0x6e4): undefined reference to `pinconf_generic_dt_node_to_map'
>> ld: drivers/pinctrl/pinctrl-rtl8231.o:(.rodata+0x248): undefined reference to `pinconf_generic_dt_free_map'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v7 5/6] leds: Add support for RTL8231 LED scan matrix
2025-11-17 21:51 [PATCH v7 0/6] RTL8231 GPIO expander support Sander Vanheule
` (3 preceding siblings ...)
2025-11-17 21:51 ` [PATCH v7 4/6] pinctrl: Add RTL8231 pin control and GPIO support Sander Vanheule
@ 2025-11-17 21:51 ` Sander Vanheule
2025-11-17 21:51 ` [PATCH v7 6/6] MAINTAINERS: Add RTL8231 MFD driver Sander Vanheule
5 siblings, 0 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-17 21:51 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Linus Walleij, Michael Walle, Bartosz Golaszewski
Cc: linux-leds, devicetree, linux-kernel, linux-gpio, Sander Vanheule
Both single and bi-color scanning modes are supported. The driver will
verify that the addresses are valid for the current mode, before
registering the LEDs. LEDs can be turned on, off, or toggled at one of
six predefined rates from 40ms to 1280ms.
Implements a platform device for use as a child device with RTL8231 MFD,
and uses the parent regmap to access the required registers.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/leds/Kconfig | 10 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-rtl8231.c | 285 ++++++++++++++++++++++++++++++++++++
3 files changed, 296 insertions(+)
create mode 100644 drivers/leds/leds-rtl8231.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 06e6291be11b..bb4429bcd7a2 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -666,6 +666,16 @@ config LEDS_REGULATOR
help
This option enables support for regulator driven LEDs.
+config LEDS_RTL8231
+ tristate "RTL8231 LED matrix support"
+ depends on LEDS_CLASS
+ depends on MFD_RTL8231
+ default MFD_RTL8231
+ help
+ This option enables support for using the LED scanning matrix output
+ of the RTL8231 GPIO and LED expander chip.
+ When built as a module, this module will be named leds-rtl8231.
+
config LEDS_BD2606MVV
tristate "LED driver for BD2606MVV"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 9a0333ec1a86..27c32204aebc 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
obj-$(CONFIG_LEDS_QNAP_MCU) += leds-qnap-mcu.o
obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
+obj-$(CONFIG_LEDS_RTL8231) += leds-rtl8231.o
obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
obj-$(CONFIG_LEDS_ST1202) += leds-st1202.o
obj-$(CONFIG_LEDS_SUN50I_A100) += leds-sun50i-a100.o
diff --git a/drivers/leds/leds-rtl8231.c b/drivers/leds/leds-rtl8231.c
new file mode 100644
index 000000000000..8ff20cc7ea98
--- /dev/null
+++ b/drivers/leds/leds-rtl8231.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/rtl8231.h>
+
+/**
+ * struct led_toggle_rate - description of an LED blinking mode
+ * @interval_ms: LED toggle rate in milliseconds
+ * @mode: Register field value used to activate this mode
+ *
+ * For LED hardware accelerated blinking, with equal on and off delay.
+ * Both delays are given by @interval, so the interval at which the LED blinks
+ * (i.e. turn on and off once) is double this value.
+ */
+struct led_toggle_rate {
+ u16 interval_ms;
+ u8 mode;
+};
+
+/**
+ * struct led_modes - description of all LED modes
+ * @toggle_rates: Array of led_toggle_rate values, sorted by ascending interval
+ * @num_toggle_rates: Number of elements in @led_toggle_rate
+ * @off: Register field value to turn LED off
+ * @on: Register field value to turn LED on
+ */
+struct led_modes {
+ const struct led_toggle_rate *toggle_rates;
+ unsigned int num_toggle_rates;
+ u8 off;
+ u8 on;
+};
+
+struct rtl8231_led {
+ struct led_classdev led;
+ const struct led_modes *modes;
+ struct regmap_field *reg_field;
+};
+#define to_rtl8231_led(_cdev) container_of(_cdev, struct rtl8231_led, led)
+
+#define RTL8231_NUM_LEDS 3
+#define RTL8231_LED_PER_REG 5
+#define RTL8231_BITS_PER_LED 3
+
+static const unsigned int rtl8231_led_port_counts_single[RTL8231_NUM_LEDS] = {32, 32, 24};
+static const unsigned int rtl8231_led_port_counts_bicolor[RTL8231_NUM_LEDS] = {24, 24, 24};
+
+static const unsigned int rtl8231_led_base[RTL8231_NUM_LEDS] = {
+ RTL8231_REG_LED0_BASE,
+ RTL8231_REG_LED1_BASE,
+ RTL8231_REG_LED2_BASE,
+};
+
+#define RTL8231_DEFAULT_TOGGLE_INTERVAL_MS 500
+
+static const struct led_toggle_rate rtl8231_toggle_rates[] = {
+ { 40, 1},
+ { 80, 2},
+ { 160, 3},
+ { 320, 4},
+ { 640, 5},
+ {1280, 6},
+};
+
+static const struct led_modes rtl8231_led_modes = {
+ .off = 0,
+ .on = 7,
+ .num_toggle_rates = ARRAY_SIZE(rtl8231_toggle_rates),
+ .toggle_rates = rtl8231_toggle_rates,
+};
+
+static void rtl8231_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev);
+
+ if (brightness)
+ regmap_field_write(pled->reg_field, pled->modes->on);
+ else
+ regmap_field_write(pled->reg_field, pled->modes->off);
+}
+
+static enum led_brightness rtl8231_led_brightness_get(struct led_classdev *led_cdev)
+{
+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev);
+ u32 current_mode = pled->modes->off;
+
+ regmap_field_read(pled->reg_field, ¤t_mode);
+
+ if (current_mode == pled->modes->off)
+ return LED_OFF;
+ else
+ return LED_ON;
+}
+
+static unsigned int rtl8231_led_current_interval(struct rtl8231_led *pled)
+{
+ unsigned int mode;
+ unsigned int i;
+
+ if (regmap_field_read(pled->reg_field, &mode))
+ return 0;
+
+ for (i = 0; i < pled->modes->num_toggle_rates; i++)
+ if (mode == pled->modes->toggle_rates[i].mode)
+ return pled->modes->toggle_rates[i].interval_ms;
+
+ return 0;
+}
+
+static int rtl8231_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev);
+ const struct led_toggle_rate *rates = pled->modes->toggle_rates;
+ unsigned int num_rates = pled->modes->num_toggle_rates;
+ unsigned int interval_ms;
+ unsigned int i;
+ int err;
+
+ if (*delay_on == 0 && *delay_off == 0) {
+ interval_ms = RTL8231_DEFAULT_TOGGLE_INTERVAL_MS;
+ } else {
+ /*
+ * If the current mode is blinking, choose the delay that (likely) changed.
+ * Otherwise, choose the interval that would have the same total delay.
+ */
+ interval_ms = rtl8231_led_current_interval(pled);
+ if (interval_ms > 0 && interval_ms == *delay_off)
+ interval_ms = *delay_on;
+ else if (interval_ms > 0 && interval_ms == *delay_on)
+ interval_ms = *delay_off;
+ else
+ interval_ms = (*delay_on + *delay_off) / 2;
+ }
+
+ /* Find clamped toggle interval */
+ for (i = 0; i < (num_rates - 1); i++)
+ if (interval_ms > rates[i].interval_ms)
+ break;
+
+ interval_ms = rates[i].interval_ms;
+
+ err = regmap_field_write(pled->reg_field, rates[i].mode);
+ if (err)
+ return err;
+
+ *delay_on = interval_ms;
+ *delay_off = interval_ms;
+
+ return 0;
+}
+
+static int rtl8231_led_read_address(struct fwnode_handle *fwnode, unsigned int *addr_port,
+ unsigned int *addr_led)
+{
+ u32 addr[2];
+ int err;
+
+ err = fwnode_property_count_u32(fwnode, "reg");
+ if (err < 0)
+ return err;
+ if (err != ARRAY_SIZE(addr))
+ return -EINVAL;
+
+ err = fwnode_property_read_u32_array(fwnode, "reg", addr, ARRAY_SIZE(addr));
+ if (err)
+ return err;
+
+ *addr_port = addr[0];
+ *addr_led = addr[1];
+
+ return 0;
+}
+
+static struct regmap_field *rtl8231_led_get_field(struct device *dev, struct regmap *map,
+ unsigned int port_index, unsigned int led_index)
+{
+ unsigned int offset = port_index / RTL8231_LED_PER_REG;
+ unsigned int shift = (port_index % RTL8231_LED_PER_REG) * RTL8231_BITS_PER_LED;
+ const struct reg_field field = REG_FIELD(rtl8231_led_base[led_index] + offset, shift,
+ shift + RTL8231_BITS_PER_LED - 1);
+
+ return devm_regmap_field_alloc(dev, map, field);
+}
+
+static int rtl8231_led_probe_single(struct device *dev, struct regmap *map,
+ const unsigned int *port_counts, struct fwnode_handle *fwnode)
+{
+ struct led_init_data init_data = {};
+ struct rtl8231_led *pled;
+ unsigned int port_index;
+ unsigned int led_index;
+ int err;
+
+ pled = devm_kzalloc(dev, sizeof(*pled), GFP_KERNEL);
+ if (!pled)
+ return -ENOMEM;
+
+ err = rtl8231_led_read_address(fwnode, &port_index, &led_index);
+ if (err) {
+ dev_err(dev, "LED address invalid");
+ return err;
+ }
+
+ if (led_index >= RTL8231_NUM_LEDS || port_index >= port_counts[led_index]) {
+ dev_err(dev, "LED address (%d.%d) invalid", port_index, led_index);
+ return -EINVAL;
+ }
+
+ pled->reg_field = rtl8231_led_get_field(dev, map, port_index, led_index);
+ if (IS_ERR(pled->reg_field))
+ return PTR_ERR(pled->reg_field);
+
+ pled->modes = &rtl8231_led_modes;
+
+ pled->led.max_brightness = 1;
+ pled->led.brightness_get = rtl8231_led_brightness_get;
+ pled->led.brightness_set = rtl8231_led_brightness_set;
+ pled->led.blink_set = rtl8231_led_blink_set;
+
+ init_data.fwnode = fwnode;
+
+ return devm_led_classdev_register_ext(dev, &pled->led, &init_data);
+}
+
+static int rtl8231_led_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const unsigned int *port_counts;
+ struct fwnode_handle *child;
+ struct regmap *map;
+ int err;
+
+ map = dev_get_regmap(dev->parent, NULL);
+ if (!map)
+ return -ENODEV;
+
+ if (device_property_match_string(dev, "realtek,led-scan-mode", "single-color") >= 0) {
+ port_counts = rtl8231_led_port_counts_single;
+ regmap_update_bits(map, RTL8231_REG_FUNC0,
+ RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_SINGLE);
+ } else if (device_property_match_string(dev, "realtek,led-scan-mode", "bi-color") >= 0) {
+ port_counts = rtl8231_led_port_counts_bicolor;
+ regmap_update_bits(map, RTL8231_REG_FUNC0,
+ RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_BICOLOR);
+ } else {
+ dev_err(dev, "scan mode missing or invalid");
+ return -EINVAL;
+ }
+
+ fwnode_for_each_available_child_node(dev->fwnode, child) {
+ err = rtl8231_led_probe_single(dev, map, port_counts, child);
+ if (err)
+ dev_warn(dev, "failed to register LED %pfwP", child);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id of_rtl8231_led_match[] = {
+ { .compatible = "realtek,rtl8231-leds" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_rtl8231_led_match);
+
+static struct platform_driver rtl8231_led_driver = {
+ .driver = {
+ .name = "rtl8231-leds",
+ .of_match_table = of_rtl8231_led_match,
+ },
+ .probe = rtl8231_led_probe,
+};
+module_platform_driver(rtl8231_led_driver);
+
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek RTL8231 LED support");
+MODULE_LICENSE("GPL");
--
2.51.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v7 6/6] MAINTAINERS: Add RTL8231 MFD driver
2025-11-17 21:51 [PATCH v7 0/6] RTL8231 GPIO expander support Sander Vanheule
` (4 preceding siblings ...)
2025-11-17 21:51 ` [PATCH v7 5/6] leds: Add support for RTL8231 LED scan matrix Sander Vanheule
@ 2025-11-17 21:51 ` Sander Vanheule
5 siblings, 0 replies; 19+ messages in thread
From: Sander Vanheule @ 2025-11-17 21:51 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Linus Walleij, Michael Walle, Bartosz Golaszewski
Cc: linux-leds, devicetree, linux-kernel, linux-gpio, Sander Vanheule
Add the files associated with the RTL8231 support, and list Sander
Vanheule (myself) as maintainer.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3da2c26a796b..d0b4e93ec58e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21656,6 +21656,16 @@ S: Maintained
F: Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
F: drivers/watchdog/realtek_otto_wdt.c
+REALTEK RTL8231 MFD DRIVER
+M: Sander Vanheule <sander@svanheule.net>
+S: Maintained
+F: Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
+F: Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
+F: drivers/leds/leds-rtl8231.c
+F: drivers/mfd/rtl8231.c
+F: drivers/pinctrl/pinctrl-rtl8231.c
+F: include/linux/mfd/rtl8231.h
+
REALTEK RTL83xx SMI DSA ROUTER CHIPS
M: Linus Walleij <linus.walleij@linaro.org>
M: Alvin Šipraga <alsi@bang-olufsen.dk>
--
2.51.1
^ permalink raw reply related [flat|nested] 19+ messages in thread