Skip to contentSkip to conversation

RTL8231 GPIO expander support

PATCH In review Sander Vanheule 3 participants 14 messages Jun 12, 2021 – Jul 8, 2021

Subsystems (8)
lore
Share & tools
lore
#0 lore raw

v5 cover RTL8231 GPIO expander support

The RTL8231 GPIO and LED expander can be configured for use as an MDIO or SMI bus device. Currently only the MDIO mode is supported, although SMI mode support should be fairly straightforward, once an SMI bus driver is available.

Provided features by the RTL8231:
- Up to 37 GPIOs
- Configurable drive strength: 8mA or 4mA (currently unsupported)
- Input debouncing on GPIOs 31-36
- Up to 88 LEDs in multiple scan matrix groups
- On, off, or one of six toggling intervals
- "single-color mode": 2×36 single color LEDs + 8 bi-color LEDs
- "bi-color mode": (12 + 2×6) bi-color LEDs + 24 single color LEDs
- Up to one PWM output (currently unsupported)
- Fixed duty cycle, 8 selectable frequencies (1.2kHz - 4.8kHz)

The GPIO controller uses gpio-regmap. To support the aliased data input and output registers, the regmap interface is extended to supported atomic, uncached register reads. This is then used with a new quirk for gpio-regmap.

Register access is provided through a new MDIO regmap provider. The required MDIO regmap support was merged in Mark Brown's regmap repository, and can be found under the regmap-mdio tag:
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git/tag/?h=regmap-mdio

---
Another revision of this patch series, now without (virtual) register paging. After a few other (failed) attemps, I added a call to the regmap interface to perform atomic, uncached register reads. Combined with the appropriate caching of the register values, this can provide a split view of the data registers for gpio-regmap. See patches 1/7 and 2/7.

These additions allowed the MFD core driver to be a bit less complex. The GPIO support didn't see significant changes, so I've kept the review tags. The bindings and LED driver are unchanged.

With this patch series (hopefully) nearing its final form, I was wondering if this could be merged via the MFD tree, when all the necessary reviews and/or acks are present. Would that be OK for everyone?

Changes since v4:
- List myself as maintainer for this chip
- Add uncached register reads to regmap; replaces virtual registers

Changes since v3:
- Drop gpio-regmap direction-before-value quirk
- Use secondary virtual register range to enable proper read-modify-write behaviour on GPIO output values
- Add pin debounce support
- Switch to generic pinmux functions

Changes since v2:
- MDIO regmap support was merged, so patch is dropped here
- Implement feedback for DT bindings
- Use correct module names in Kconfigs
- Fix k*alloc return value checks
- Introduce GPIO regmap quirks to set output direction first
- pinctrl: Use static pin descriptions for pin controller
- pinctrl: Fix gpio consumer resource leak
- mfd: Replace CONFIG_PM-ifdef'ery
- leds: Rename interval to interval_ms

Changes since v1:
- Reintroduce MDIO regmap, with fixed Kconfig dependencies
- Add configurable dir/value order for gpio-regmap direction_out call
- Drop allocations for regmap fields that are used only on init
- Move some definitions to MFD header
- Add PM ops to replace driver remove for MFD
- Change pinctrl driver to (modified) gpio-regmap
- Change leds driver to use fwnode

Changes since RFC:
- Dropped MDIO regmap interface. I was unable to resolve the Kconfig dependency issue, so have reverted to using regmap_config.reg_read/write.
- Added pinctrl support
- Added LED support
- Changed root device to MFD, with pinctrl and leds child devices. Root device is now an mdio_device driver.

Sander Vanheule (8):
regmap: Support atomic forced uncached reads
gpio: regmap: Add quirk for aliased data registers
dt-bindings: leds: Binding for RTL8231 scan matrix
dt-bindings: mfd: Binding for RTL8231
mfd: Add RTL8231 core device
pinctrl: Add RTL8231 pin control and GPIO support
leds: Add support for RTL8231 LED scan matrix
MAINTAINERS: Add RTL8231 MFD driver

 .../bindings/leds/realtek,rtl8231-leds.yaml   | 166 +++++++
 .../bindings/mfd/realtek,rtl8231.yaml         | 190 ++++++++
 MAINTAINERS                                   |  10 +
 drivers/base/regmap/regmap.c                  |  33 ++
 drivers/gpio/gpio-regmap.c                    |   7 +-
 drivers/leds/Kconfig                          |  10 +
 drivers/leds/Makefile                         |   1 +
 drivers/leds/leds-rtl8231.c                   | 291 ++++++++++++
 drivers/mfd/Kconfig                           |   9 +
 drivers/mfd/Makefile                          |   1 +
 drivers/mfd/rtl8231.c                         | 186 ++++++++
 drivers/pinctrl/Kconfig                       |  11 +
 drivers/pinctrl/Makefile                      |   1 +
 drivers/pinctrl/pinctrl-rtl8231.c             | 438 ++++++++++++++++++
 include/linux/gpio/regmap.h                   |  13 +
 include/linux/mfd/rtl8231.h                   |  71 +++
 include/linux/regmap.h                        |   8 +
 17 files changed, 1445 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
 create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
 create mode 100644 drivers/leds/leds-rtl8231.c
 create mode 100644 drivers/mfd/rtl8231.c
 create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
 create mode 100644 include/linux/mfd/rtl8231.h
Signature · 1 line
2.31.1
#1 lore raw

PATCH v5 1/8 regmap: Support atomic forced uncached reads

When a user wants to read a single uncached register, cache bypassing can be enabled. However, this is not atomic unless an external lock is used for the regmap. When using regcache_cache_bypass, the original bypass state also cannot be restored.

Add support to atomically read a single uncached value, bypassing any regmap cache.

---
 drivers/base/regmap/regmap.c | 33 +++++++++++++++++++++++++++++++++
 include/linux/regmap.h       |  8 ++++++++
 2 files changed, 41 insertions(+)
drivers/base/regmap/regmap.c +33 -0
index fe3e38dd5324..a828f05535b7 100644
@@ -2745,6 +2745,39 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
 }
 EXPORT_SYMBOL_GPL(regmap_read);
 
+/**
+ * regmap_read_bypassed() - Read a value from a single register, bypassing the cache
+ *
+ * @map: Register map to read from
+ * @reg: Register to be read from
+ * @val: Pointer to store read value
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val)
+{
+	bool bypass;
+	int ret;
+
+	if (!IS_ALIGNED(reg, map->reg_stride))
+		return -EINVAL;
+
+	map->lock(map->lock_arg);
+
+	bypass = map->cache_bypass;
+	map->cache_bypass = true;
+
+	ret = _regmap_read(map, reg, val);
+
+	map->cache_bypass = bypass;
+
+	map->unlock(map->lock_arg);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_read_bypassed);
+
 /**
  * regmap_raw_read() - Read raw data from the device
  *
include/linux/regmap.h +8 -0
index f5f08dd0a116..a54dc00326ba 100644
@@ -1133,6 +1133,7 @@ int regmap_multi_reg_write_bypassed(struct regmap *map,
 int regmap_raw_write_async(struct regmap *map, unsigned int reg,
 			   const void *val, size_t val_len);
 int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
+int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val);
 int regmap_raw_read(struct regmap *map, unsigned int reg,
 		    void *val, size_t val_len);
 int regmap_noinc_read(struct regmap *map, unsigned int reg,
@@ -1607,6 +1608,13 @@ static inline int regmap_read(struct regmap *map, unsigned int reg,
 	return -EINVAL;
 }
 
+static inline int regmap_read_bypassed(struct regmap *map, unsigned int reg,
+				       unsigned int *val)
+{
+	WARN_ONCE(1, "regmap API is disabled");
+	return -EINVAL;
+}
+
 static inline int regmap_raw_read(struct regmap *map, unsigned int reg,
 				  void *val, size_t val_len)
 {
Signature · 1 line
2.31.1
#2 lore raw

Re: [PATCH v5 1/8] regmap: Support atomic forced uncached reads

Quoted 10 lines · Sander Vanheule

When a user wants to read a single uncached register, cache bypassing can be enabled. However, this is not atomic unless an external lock is used for the regmap. When using regcache_cache_bypass, the original bypass state also cannot be restored.

Add support to atomically read a single uncached value, bypassing any regmap cache.

+int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val)

If this is acceptable in general, I will rather name the function like regmap_nocache_read() to be aligned with the other API naming pattern (see below).

Quoted 7 lines

int regmap_raw_write_async(struct regmap *map, unsigned int reg,

                           const void *val, size_t val_len);
 int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
+int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val);
 int regmap_raw_read(struct regmap *map, unsigned int reg,
                    void *val, size_t val_len);

int regmap_noinc_read(struct regmap *map, unsigned int reg,

Signature · 2 lines
With Best Regards,
Andy Shevchenko
maintainer
#3 lore raw

Re: [PATCH v5 1/8] regmap: Support atomic forced uncached reads

In reply to Sander Vanheule
Quoted 7 lines · Sander Vanheule

When a user wants to read a single uncached register, cache bypassing can be enabled. However, this is not atomic unless an external lock is used for the regmap. When using regcache_cache_bypass, the original bypass state also cannot be restored.

Add support to atomically read a single uncached value, bypassing any regmap cache.

The expectation here is that if there is a need to do this for some reason the user can arrange to do this for itself - if something is happening that makes a normally non-volatile register volatile then it probably needs higher level coordination. What's the use case?

Quoted 1 line

+int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val)

Bypassed what? I think Andy's naming suggestion was much better.

Please also keep to 80 columns if you can, I know the requirements got relaxed a bit but no need to do it excessively.

#4 lore raw

PATCH v5 2/8 gpio: regmap: Add quirk for aliased data registers

In reply to Sander Vanheule

Some chips have the read-only input and write-only output data registers aliased to the same offset. As a result it is not possible to perform read-modify-writes on the output values, when a line is still configured as input.

Add a quirk for aliased data registers, and document how the regmap should be set up for correct operation.

---
 drivers/gpio/gpio-regmap.c  |  7 ++++++-
 include/linux/gpio/regmap.h | 13 +++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)
drivers/gpio/gpio-regmap.c +6 -1
index 134cedf151a7..3a3d0d9a945c 100644
@@ -15,6 +15,7 @@ struct gpio_regmap {
 	struct device *parent;
 	struct regmap *regmap;
 	struct gpio_chip gpio_chip;
+	unsigned int quirks;
 
 	int reg_stride;
 	int ngpio_per_reg;
@@ -68,7 +69,10 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
 	if (ret)
 		return ret;
 
-	ret = regmap_read(gpio->regmap, reg, &val);
+	if (gpio->quirks & GPIO_REGMAP_QUIRK_ALIASED_DATA)
+		ret = regmap_read_bypassed(gpio->regmap, reg, &val);
+	else
+		ret = regmap_read(gpio->regmap, reg, &val);
 	if (ret)
 		return ret;
 
@@ -227,6 +231,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
 
 	gpio->parent = config->parent;
 	gpio->regmap = config->regmap;
+	gpio->quirks = config->quirks;
 	gpio->ngpio_per_reg = config->ngpio_per_reg;
 	gpio->reg_stride = config->reg_stride;
 	gpio->reg_mask_xlate = config->reg_mask_xlate;
include/linux/gpio/regmap.h +13 -0
index 334dd928042b..b0751a10fa4a 100644
@@ -12,6 +12,17 @@ struct regmap;
 #define GPIO_REGMAP_ADDR_ZERO ((unsigned int)(-1))
 #define GPIO_REGMAP_ADDR(addr) ((addr) ? : GPIO_REGMAP_ADDR_ZERO)
 
+enum gpio_regmap_quirk {
+	/*
+	 * For hardware where the (read-only) input and (write-only) output
+	 * registers are aliased to the same offset. In this case the register
+	 * must not be marked as volatile and a regcache must be used, to cache
+	 * the write-only output values. Register reads for the input values
+	 * will be performed by bypassing the cache.
+	 */
+	GPIO_REGMAP_QUIRK_ALIASED_DATA = BIT(0),
+};
+
 /**
  * struct gpio_regmap_config - Description of a generic regmap gpio_chip.
  * @parent:		The parent device
@@ -31,6 +42,7 @@ struct regmap;
  * @reg_stride:		(Optional) May be set if the registers (of the
  *			same type, dat, set, etc) are not consecutive.
  * @ngpio_per_reg:	Number of GPIOs per register
+ * @quirks:		Flags indicating GPIO chip hardware issues
  * @irq_domain:		(Optional) IRQ domain if the controller is
  *			interrupt-capable
  * @reg_mask_xlate:     (Optional) Translates base address and GPIO
@@ -73,6 +85,7 @@ struct gpio_regmap_config {
 	unsigned int reg_dir_out_base;
 	int reg_stride;
 	int ngpio_per_reg;
+	unsigned int quirks;
 	struct irq_domain *irq_domain;
 
 	int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
Signature · 1 line
2.31.1
#5 lore raw

Re: [PATCH v5 2/8] gpio: regmap: Add quirk for aliased data registers

Quoted 8 lines · Sander Vanheule

Some chips have the read-only input and write-only output data registers aliased to the same offset. As a result it is not possible to perform read-modify-writes on the output values, when a line is still configured as input.

Add a quirk for aliased data registers, and document how the regmap should be set up for correct operation.

I still believe that there is no issue with gpio-regmap and we don't need any quirk there.

The issue is in the regmap APIs (implementation) itself. Hardware with the concept of reading X / writing Y at the same offset is okay per se. regmaps doesn't support it properly and should be fixed (amended) in a way that you provide this kind of register description thru regmap configuration or so.

I expressed the idea of trying to implement regmap-8250 as an example of the support for such hardware. And OTOH that this kind of hardware is not unusual.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#6 lore raw

Re: [PATCH v5 2/8] gpio: regmap: Add quirk for aliased data registers

Hi Andy, Mark,

My apologies for the delay in replying, work has been keeping me a bit busy.

Quoted 10 lines · Andy Shevchenko
Quoted 8 lines · Sander Vanheule

Some chips have the read-only input and write-only output data registers aliased to the same offset. As a result it is not possible to perform read-modify-writes on the output values, when a line is still configured as input.

Add a quirk for aliased data registers, and document how the regmap should be set up for correct operation.

Quoted 8 lines

I still believe that there is no issue with gpio-regmap and we don't need any quirk there.

The issue is in the regmap APIs (implementation) itself. Hardware with the concept of reading X / writing Y at the same offset is okay per se. regmaps doesn't support it properly and should be fixed (amended) in a way that you provide this kind of register description thru regmap configuration or so.

I've made an attempt at implementing a "regmap_aliased()", similar to "regmap_volatile()". However, this meant I had to make _regmap_read() aware of wether the read- or write-alias was being read (from cache), touching some parts of the regmap code I'm not using myself. Furthermore, this "aliased" property isn't really perpendicular to "volatile", since writes should never be volatile, and non-volatile reads don't make much sense (to me) on a read-only register.

In addition to entire registers having different meanings on reads or writes, individual bitfields could also have different properties. Some bits of a register could be aliased, while other bits are just 'plain' volatile. This is the case for the last register of the RTL8231, where the low bits are GPIO data (so aliased), and the highest bit is a self-clearing "latch new data" command bit (i.e. RW-volatile).

If a regmap_field could overwrite the specifiers of it's parent register, I think this may provide quite a natural solution to the aliasing problem: just create two regmap_field defintions. One definition would be 'write-only' (and cached for RMW), the other 'volatile read-only'. All regmap_fields could still rely on a single cached register value, I think. I didn't try to implement this though, so maybe I'm missing some behaviour that would disqualify this solution. Would you think this could be an acceptable way to move forward here?

Quoted 3 lines

I expressed the idea of trying to implement regmap-8250 as an example of the support for such hardware. And OTOH that this kind of hardware is not unusual.

This implementation indeed requires the same aliasing support, in addition to register paging even. I've never touched that subsystem before though, so I would need some more time if I wanted to try this.

Best,
Sander

maintainer
#7 lore raw

Re: [PATCH v5 2/8] gpio: regmap: Add quirk for aliased data registers

Quoted 6 lines · Sander Vanheule

I've made an attempt at implementing a "regmap_aliased()", similar to "regmap_volatile()". However, this meant I had to make _regmap_read() aware of wether the read- or write-alias was being read (from cache), touching some parts of the regmap code I'm not using myself. Furthermore, this "aliased" property isn't really perpendicular to "volatile", since writes should never be volatile, and non-volatile reads don't make much sense (to me) on a read-only register.

As far as the abstractions in regmap are concerned these registers are volatile, that's currently how regmap undertands registers where readback won't give you the last written value. Trying to convince the framework to handle these registers as anything other than volatile is going to of need be an invasive change.

Quoted 7 lines

If a regmap_field could overwrite the specifiers of it's parent register, I think this may provide quite a natural solution to the aliasing problem: just create two regmap_field defintions. One definition would be 'write-only' (and cached for RMW), the other 'volatile read-only'. All regmap_fields could still rely on a single cached register value, I think. I didn't try to implement this though, so maybe I'm missing some behaviour that would disqualify this solution. Would you think this could be an acceptable way to move forward here?

This feels like a painful and potentially high overhead approach to things - at the minute fields are layered on top of registers and are totally invisible at the register level, pulling the two together would touch a lot of places and make things tense, especially if we ended up with two different fields aliasing each other. I'd need to see code but it feels like a difficult approach to take.

#8 lore raw

PATCH v5 3/8 dt-bindings: leds: Binding for RTL8231 scan matrix

In reply to 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.

---
v4:
- Add Rob's review tag

---
 .../bindings/leds/realtek,rtl8231-leds.yaml   | 166 ++++++++++++++++++
 1 file changed, 166 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml new file +166 -0
new file mode 100644
index 000000000000..560249cd7e8c
@@ -0,0 +1,166 @@
+# 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 <[email protected]>
+
+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@":
+    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:
+          - description: port index
+            maximum: 31
+          - description: led index
+            maximum: 2
+
+    allOf:
+      - $ref: ../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;
+        };
+    };
Signature · 1 line
2.31.1
#9 lore raw

PATCH v5 4/8 dt-bindings: mfd: Binding for RTL8231

In reply to 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.

---
v4:
- Add Rob's and Linus' review tags

---
 .../bindings/mfd/realtek,rtl8231.yaml         | 190 ++++++++++++++++++
 1 file changed, 190 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml new file +190 -0
new file mode 100644
index 000000000000..4e2326401560
@@ -0,0 +1,190 @@
+# 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 <[email protected]>
+
+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, LED matrix scan line/column, or as a
+  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: ../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: ../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-pins {
+                pins = "gpio0", "gpio1", "gpio3", "gpio4";
+                function = "led";
+            };
+
+            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@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>;
+                };
+            };
+        };
+    };
Signature · 1 line
2.31.1
#10 lore raw

PATCH v5 5/8 mfd: Add RTL8231 core device

In reply to 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 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 parent SoC's reset line.

To provide different read and write semantics for the GPIO data registers, a secondary virtual register range is used to enable separate caching properties of pin input and output values.

---
v5:
- Optional reset-gpios error handling
- Drop virtual registers, since custom reg_read/reg_write weren't used

v4:
- Define more bit masks
- Force writes to critical bit fields (reset, output enable)
- Add virtual addresses and caching

v3:
- Replace CONFIG_PM-ifdef'ery

v2:
- A missing MDIO_BUS dependency, as was reported by kernel test robot <[email protected]>, is provided via REGMAP_MDIO.

---
 drivers/mfd/Kconfig         |   9 ++
 drivers/mfd/Makefile        |   1 +
 drivers/mfd/rtl8231.c       | 186 ++++++++++++++++++++++++++++++++++++
 include/linux/mfd/rtl8231.h |  71 ++++++++++++++
 4 files changed, 267 insertions(+)
 create mode 100644 drivers/mfd/rtl8231.c
 create mode 100644 include/linux/mfd/rtl8231.h
drivers/mfd/Kconfig +9 -0
index 5c7f2b100191..68f28a335b8c 100644
@@ -1076,6 +1076,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_RT5033
 	tristate "Richtek RT5033 Power Management IC"
 	depends on I2C
drivers/mfd/Makefile +1 -0
index 4f6d2b8a5f76..4b27c2486ccc 100644
@@ -234,6 +234,7 @@ obj-$(CONFIG_MFD_MENF21BMC)	+= menf21bmc.o
 obj-$(CONFIG_MFD_HI6421_PMIC)	+= hi6421-pmic-core.o
 obj-$(CONFIG_MFD_HI655X_PMIC)   += hi655x-pmic.o
 obj-$(CONFIG_MFD_DLN2)		+= dln2.o
+obj-$(CONFIG_MFD_RTL8231)	+= rtl8231.o
 obj-$(CONFIG_MFD_RT5033)	+= rt5033.o
 obj-$(CONFIG_MFD_SKY81452)	+= sky81452.o
 
drivers/mfd/rtl8231.c new file +186 -0

186 lines not on this page · raw message

include/linux/mfd/rtl8231.h new file +71 -0

71 lines not on this page · raw message

Signature · 1 line
2.31.1
#11 lore raw

PATCH v5 6/8 pinctrl: Add RTL8231 pin control and GPIO support

In reply to 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.

---
v5:
- Add Andy's and Linus' review tags
- Use uintptr_t for integer stored in void* field Reported by: kernel test robot <[email protected]>
- Replace virtual registers by GPIO_REGMAP_QUIRK_ALIASED_DATA

v4:
- Switch to pinmux_generic for pin functions
- Add pin debounce pinconf
- Virtual addresses and caching
- Use PRT_ERR_OR_ZERO in pinctrl/gpio probe
- Drop direction-first quirk for gpio-regmap

v3:
- Use static pin description for pin controller
- Fix gpio consumer resource leak

v2:
- Use gpio-regmap with direction-before-value output

---
 drivers/pinctrl/Kconfig           |  11 +
 drivers/pinctrl/Makefile          |   1 +
 drivers/pinctrl/pinctrl-rtl8231.c | 438 ++++++++++++++++++++++++++++++
 3 files changed, 450 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
drivers/pinctrl/Kconfig +11 -0
index c2c7e7963ed0..a02c1befbee4 100644
@@ -221,6 +221,17 @@ 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 GPIO_REGMAP
+	select GENERIC_PINCONF
+	select GENERIC_PINMUX_FUNCTIONS
+	help
+	  Support for RTL8231 expander's GPIOs and pin controller.
+	  When built as a module, the module will be called pinctrl-rtl8231.
+
 config PINCTRL_SINGLE
 	tristate "One-register-per-pin type device tree based pinctrl driver"
 	depends on OF
drivers/pinctrl/Makefile +1 -0
index 5ef5334a797f..239603efb317 100644
@@ -30,6 +30,7 @@ obj-$(CONFIG_PINCTRL_PALMAS)	+= pinctrl-palmas.o
 obj-$(CONFIG_PINCTRL_PIC32)	+= pinctrl-pic32.o
 obj-$(CONFIG_PINCTRL_PISTACHIO)	+= pinctrl-pistachio.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
+obj-$(CONFIG_PINCTRL_RTL8231)	+= pinctrl-rtl8231.o
 obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SX150X)	+= pinctrl-sx150x.o
 obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
drivers/pinctrl/pinctrl-rtl8231.c new file +438 -0

438 lines not on this page · raw message

Signature · 1 line
2.31.1
#12 lore raw

PATCH v5 7/8 leds: Add support for RTL8231 LED scan matrix

In reply to 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.

---
v5:
- Add Andy's review tag

v4:
- Rename variable addr_count -> err
- Use -EINVAL instead of -ENODEV

v3:
- Rename 'interval' to 'interval_ms'

v2:
- Use fwnode-calls instead of OF-calls

---
 drivers/leds/Kconfig        |  10 ++
 drivers/leds/Makefile       |   1 +
 drivers/leds/leds-rtl8231.c | 291 ++++++++++++++++++++++++++++++++++++
 3 files changed, 302 insertions(+)
 create mode 100644 drivers/leds/leds-rtl8231.c
drivers/leds/Kconfig +10 -0
index 49d99cb084db..8cb869e8cd09 100644
@@ -593,6 +593,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_BD2802
 	tristate "LED driver for BD2802 RGB LED"
 	depends on LEDS_CLASS
drivers/leds/Makefile +1 -0
index 7e604d3028c8..ce0f44a87dee 100644
@@ -80,6 +80,7 @@ obj-$(CONFIG_LEDS_PM8058)		+= leds-pm8058.o
 obj-$(CONFIG_LEDS_POWERNV)		+= leds-powernv.o
 obj-$(CONFIG_LEDS_PWM)			+= leds-pwm.o
 obj-$(CONFIG_LEDS_REGULATOR)		+= leds-regulator.o
+obj-$(CONFIG_LEDS_RTL8231)		+= leds-rtl8231.o
 obj-$(CONFIG_LEDS_S3C24XX)		+= leds-s3c24xx.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_SGM3140)		+= leds-sgm3140.o
drivers/leds/leds-rtl8231.c new file +291 -0

291 lines not on this page · raw message

Signature · 1 line
2.31.1
#13 lore raw

PATCH v5 8/8 MAINTAINERS: Add RTL8231 MFD driver

In reply to Sander Vanheule

Add the files associated with the RTL8231 support, and list Sander Vanheule (myself) as maintainer.

---
 MAINTAINERS | 10 ++++++++++
 1 file changed, 10 insertions(+)
MAINTAINERS +10 -0
index b706dd20ff2b..62d042c14158 100644
@@ -15505,6 +15505,16 @@ S:	Maintained
 F:	include/sound/rt*.h
 F:	sound/soc/codecs/rt*
 
+REALTEK RTL8231 MFD DRIVER
+M:	Sander Vanheule <[email protected]>
+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 <[email protected]>
 S:	Maintained
Signature · 1 line
2.31.1