MIIM regmap and RTL8231 GPIO expander support
Subsystems (5)
cover MIIM regmap and RTL8231 GPIO expander support
The RTL8231 GPIO and LED expander can be configured for use as an MIIM or I2C bus device. To provide uniform data access between these two modes, the regmap interface is used. Since no regmap interface exists for MIIM busses, a basic implementation is provided.
Currently outstanding questions:
- The REGMAP_MIIM symbol should depend on MDIO_DEVICE (or a better suited, related symbol), but this results in circular Kconfig dependencies:
drivers/of/Kconfig:69:error: recursive dependency detected!
drivers/of/Kconfig:69: symbol OF_IRQ depends on IRQ_DOMAIN
kernel/irq/Kconfig:59: symbol IRQ_DOMAIN is selected by REGMAP
drivers/base/regmap/Kconfig:7: symbol REGMAP default is visible depending on REGMAP_MIIM
drivers/base/regmap/Kconfig:39: symbol REGMAP_MIIM depends on MDIO_DEVICE
drivers/net/mdio/Kconfig:6: symbol MDIO_DEVICE is selected by PHYLIB
drivers/net/phy/Kconfig:16: symbol PHYLIB is selected by ARC_EMAC_CORE
drivers/net/ethernet/arc/Kconfig:19: symbol ARC_EMAC_CORE is selected by ARC_EMAC
drivers/net/ethernet/arc/Kconfig:25: symbol ARC_EMAC depends on OF_IRQ
Suggestions on how to resolve this are welcome.
- Providing no compatible for an MDIO child node is considered to be equivalent to a c22 ethernet phy, so one must be provided. However, this node is then not automatically probed. Is it okay to provide a binding without a driver? If some code is required, where should this be put?
Current devicetree structure:
mdio-bus {
compatible = "vendor,mdio";
...
expander0: expander@0 {
/*
* Provide compatible for working registration of mdio device.
* Device probing happens in gpio1 node.
*/
compatible = "realtek,rtl8231-expander";
reg = <0>;
};
};
gpio1 : ext_gpio {
compatible = "realtek,rtl8231-mdio";
gpio-controller;
...
};- MFD driver:
The RTL8231 is not just a GPIO expander, but also a pin controller and LED matrix controller. Regmap initialisation could probably be moved to a parent MFD, with gpio, led, and pinctrl cells. Is this a hard requirement if only a GPIO controller is provided?
Sander Vanheule (2):
regmap: add miim bus support
gpio: Add Realtek RTL8231 support
drivers/base/regmap/Kconfig | 6 +- drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-miim.c | 58 +++++ drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-rtl8231.c | 404 ++++++++++++++++++++++++++++++ include/linux/regmap.h | 36 +++ 7 files changed, 514 insertions(+), 1 deletion(-) create mode 100644 drivers/base/regmap/regmap-miim.c create mode 100644 drivers/gpio/gpio-rtl8231.c
Signature · 1 line
2.30.2
RFC PATCH 1/2 regmap: add miim bus support
Basic support for MIIM bus access. Support only includes clause-22 register access, with 5-bit addresses, and 16-bit wide registers.
--- drivers/base/regmap/Kconfig | 6 +++- drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-miim.c | 58 +++++++++++++++++++++++++++++++ include/linux/regmap.h | 36 +++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 drivers/base/regmap/regmap-miim.c
drivers/base/regmap/Kconfig +5 -1
@@ -4,7 +4,7 @@ # subsystems should select the appropriate symbols. config REGMAP - default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM) + default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MIIM) select IRQ_DOMAIN if REGMAP_IRQ bool
@@ -36,6 +36,10 @@ config REGMAP_W1 tristate depends on W1 +config REGMAP_MIIM + tristate + depends on MDIO_DEVICE + config REGMAP_MMIO tristate
drivers/base/regmap/Makefile +1 -0
@@ -19,3 +19,4 @@ obj-$(CONFIG_REGMAP_SOUNDWIRE_MBQ) += regmap-sdw-mbq.o obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o obj-$(CONFIG_REGMAP_I3C) += regmap-i3c.o obj-$(CONFIG_REGMAP_SPI_AVMM) += regmap-spi-avmm.o +obj-$(CONFIG_REGMAP_MIIM) += regmap-miim.o
drivers/base/regmap/regmap-miim.c new file +58 -0
58 lines not on this page · raw message
include/linux/regmap.h +36 -0
@@ -27,6 +27,7 @@ struct device_node;struct i2c_client ;struct i3c_device ;struct irq_domain ; +struct mdio_device ;struct slim_device ;struct spi_device ;struct spmi_device ;
@@ -538,6 +539,10 @@ struct regmap *__regmap_init_i2c(struct i2c_client *i2c,const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *__regmap_init_miim (struct mdio_device *mdio_dev, +const struct regmap_config *config, +struct lock_class_key *lock_key, +const char *lock_name); struct regmap *__regmap_init_sccb(struct i2c_client *i2c, const struct regmap_config *config, struct lock_class_key *lock_key,
@@ -594,6 +599,10 @@ struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *__devm_regmap_init_miim (struct mdio_device *mdio_dev, +const struct regmap_config *config, +struct lock_class_key *lock_key, +const char *lock_name); struct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c, const struct regmap_config *config, struct lock_class_key *lock_key,
@@ -697,6 +706,19 @@ int regmap_attach_dev(struct device *dev, struct regmap *map, __regmap_lockdep_wrapper(__regmap_init_i2c, #config, \ i2c, config) +/** +* regmap_init_miim() - Initialise register map +* +* @mdio_dev: Device that will be interacted with +* @config: Configuration for register map +* +* The return value will be an ERR_PTR() on error or a valid pointer to +* a struct regmap. + */ +#define regmap_init_miim (mdio_dev, config) \ + __regmap_lockdep_wrapper(__regmap_init_miim, #config, \ + mdio_dev, config) +/** * regmap_init_sccb() - Initialise register map *
@@ -888,6 +910,20 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); __regmap_lockdep_wrapper(__devm_regmap_init_i2c, #config, \ i2c, config) +/** +* devm_regmap_init_miim() - Initialise managed register map +* +* @mdio_dev: Device that will be interacted with +* @config: Configuration for register map +* +* The return value will be an ERR_PTR() on error or a valid pointer +* to a struct regmap. The regmap will be automatically freed by the +* device management code. + */ +#define devm_regmap_init_miim (mdio_dev, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_miim, #config, \ + mdio_dev, config) +/** * devm_regmap_init_sccb() - Initialise managed register map *
Signature · 1 line
2.30.2
Mark Brown
maintainerRe: [RFC PATCH 1/2] regmap: add miim bus support
Quoted 2 lines · Sander Vanheule
Basic support for MIIM bus access. Support only includes clause-22 register access, with 5-bit addresses, and 16-bit wide registers.
What is "MIIM"? A quick search isn't showing up useful hits for that. Why not just call this MDIO like the rest of the kernel is doing, it seems like using something else is at best going to make it harder to discover this code? If MIIM is some subset or something it's not obvious how we're limited to that.
Re: [RFC PATCH 1/2] regmap: add miim bus support
Hi Mark,
Quoted 9 lines · Mark Brown
Quoted 2 lines · Sander Vanheule
Basic support for MIIM bus access. Support only includes clause-22 register access, with 5-bit addresses, and 16-bit wide registers.
What is "MIIM"? A quick search isn't showing up useful hits for that. Why not just call this MDIO like the rest of the kernel is doing, it seems like using something else is at best going to make it harder to discover this code? If MIIM is some subset or something it's not obvious how we're limited to that.
MIIM stands for "MII management", i.e. the management bus for devices with some form of MII interface. MDIO is also frequently used to refer to the data pin of the bus (there's also MDC: the clock pin), so I wanted to make the distinction.
The kernel has the mii_bus struct to describe the bus master, but like you noted the bus is generaly refered to as an MDIO interface. I'm fine with naming it MDIO to make it easier to spot.
Best,
Sander
Mark Brown
maintainerRe: [RFC PATCH 1/2] regmap: add miim bus support
Quoted 3 lines · Sander Vanheule
The kernel has the
mii_busstruct to describe the bus master, but like you noted the bus is generaly refered to as an MDIO interface. I'm fine with naming it MDIO to make it easier to spot.
Either mii_bus or mdio seem like an improvement - something matching existing kernel terminology, I guess mdio is consistent with the API it works with so...
Re: [RFC PATCH 1/2] regmap: add miim bus support
Quoted 9 lines · Mark Brown
Quoted 3 lines · Sander Vanheule
The kernel has the
mii_busstruct to describe the bus master, but like you noted the bus is generaly refered to as an MDIO interface. I'm fine with naming it MDIO to make it easier to spot.Either
mii_busor mdio seem like an improvement - something matching existing kernel terminology, I guess mdio is consistent with the API it works with so...
I would also suggest mdio. The mii_bus code is an old framework which does not see any work done to it.
Andrew
RFC PATCH 2/2 gpio: Add Realtek RTL8231 support
In reply to Sander VanheuleThe RTL8231 GPIO and LED expander chip can be controlled via a MIIM or I2C bus. A regmap interface is used to allow for portable code.
This patch only provides GPIO support, since this is the only known use, as found on commercial devices.
--- drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-rtl8231.c | 404 ++++++++++++++++++++++++++++++++++++ 3 files changed, 414 insertions(+) create mode 100644 drivers/gpio/gpio-rtl8231.c
drivers/gpio/Kconfig +9 -0
@@ -521,6 +521,15 @@ config GPIO_REG A 32-bit single register GPIO fixed in/out implementation. This can be used to represent any register as a set of GPIO signals. +config GPIO_RTL8231 + tristate "Realtek RTL8231 GPIO expander" + select REGMAP_I2C + select REGMAP_MIIM + select OF_MDIO + help + RTL8231 GPIO and LED expander support. + When built as a module, the module will be called rtl8231_expander. + config GPIO_SAMA5D2_PIOBU tristate "SAMA5D2 PIOBU GPIO support" depends on MFD_SYSCON
drivers/gpio/Makefile +1 -0
@@ -126,6 +126,7 @@ obj-$(CONFIG_GPIO_RDA) += gpio-rda.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o obj-$(CONFIG_GPIO_REG) += gpio-reg.o +obj-$(CONFIG_GPIO_RTL8231) += gpio-rtl8231.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
drivers/gpio/gpio-rtl8231.c new file +404 -0
404 lines not on this page · raw message
Signature · 1 line
2.30.2
Quoted 3 lines
- Providing no compatible for an MDIO child node is considered to be equivalent to a c22 ethernet phy, so one must be provided. However, this node is then not automatically probed.
It cannot be automatically probed, since register 2 and 3 do not contain an ID, which PHYs do. So you need to explicitly list in on the MDIO bus, and when the of_mdiobus_register() is called, the device will be instantiated.
Is it okay to provide a binding without a driver?
Quoted 6 lines
If some code is required, where should this be put?
Current devicetree structure:
mdio-bus {
compatible = "vendor,mdio";
...Quoted 15 lines
expander0: expander@0 {
/*
* Provide compatible for working registration of mdio device.
* Device probing happens in gpio1 node.
*/
compatible = "realtek,rtl8231-expander";
reg = <0>;
};
};
gpio1 : ext_gpio {
compatible = "realtek,rtl8231-mdio";
gpio-controller;
...
};I don't understand this split. Why not
mdio-bus {
compatible = "vendor,mdio";
...
expander0: expander@0 {
/*
* Provide compatible for working registration of mdio device.
* Device probing happens in gpio1 node.
*/
compatible = "realtek,rtl8231-expander";
reg = <0>;
gpio-controller;
};
};You can list whatever properties you need in the node. Ethernet switches have interrupt-controller, embedded MDIO busses with PHYs on them etc.
Quoted 5 lines
- MFD driver:
The RTL8231 is not just a GPIO expander, but also a pin controller and LED matrix controller. Regmap initialisation could probably be moved to a parent MFD, with gpio, led, and pinctrl cells. Is this a hard requirement if only a GPIO controller is provided?
You need to think about forward/backwards compatibility. You are defining a binding now, which you need to keep. Do you see how an MFD could be added without breaking backwards compatibility?
Andrew
Hi Andrew,
Thank you for the feedback. You can find a (leaked) datasheet at:
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_1.2.pdf
Quoted 54 lines · Andrew Lunn
Quoted 5 lines
- Providing no compatible for an MDIO child node is considered to be equivalent
to a c22 ethernet phy, so one must be provided. However, this node is then
not automatically probed.It cannot be automatically probed, since register 2 and 3 do not contain an ID, which PHYs do. So you need to explicitly list in on the
MDIO bus, and when theof_mdiobus_register()is called, the device will be instantiated.Is it okay to provide a binding without a driver?
Quoted 16 lines
If some code is required, where should this be put? Current devicetree structure:
mdio-bus {
compatible = "vendor,mdio";
...expander0: expander@0 {
/*
* Provide compatible for working registration of mdio device.
* Device probing happens in gpio1 node.
*/
compatible = "realtek,rtl8231-expander"; reg = <0>;
};Quoted 6 lines
};
gpio1 :ext_gpio{
compatible = "realtek,rtl8231-mdio";
gpio-controller;
...
};I don't understand this split. Why not
mdio-bus {
compatible = "vendor,mdio";
...
expander0: expander@0 {
/*
* Provide compatible for working registration of mdio device.
* Device probing happens in gpio1 node.
*/
compatible = "realtek,rtl8231-expander"; reg = <0>;
gpio-controller;
};
};
Quoted 3 lines
You can list whatever properties you need in the node. Ethernet switches have interrupt-controller, embedded MDIO busses with PHYs on them etc.
This is what I tried initially, but it doesn't seem to work. The node is probably still added as an MDIO device, but rtl8231_gpio_probe() doesn't appear to get called at all. I do agree it would be preferable over the split specification.
Having another look, I see mdio_device_id is used for ethernet phys, but like you said this requires and ID in registers 2 & 3. These registers contain pin configuration on the RTL8231, so this can't be used.
Registering as a phy_driver appears to have the same issue, although it looks like I could use a custom match_phy_device(). I do feel like this would be stretching the meaning of what a PHY is.
Quoted 9 lines
Quoted 8 lines
- MFD driver:
The RTL8231 is not just a GPIO expander, but also a pin controller and LED
matrix controller. Regmap initialisation could probably be moved to a parent
MFD, with gpio, led, and pinctrl cells. Is this a hard requirement if only a
GPIO controller is provided?
Quoted 3 lines
You need to think about forward/backwards compatibility. You are defining a binding now, which you need to keep. Do you see how an MFD could be added without breaking backwards compatibility?
There are pin-/gpio-controllers that have the gpio and pinctrl nodes in the device's root node. So I think adding pinctrl later shouldn't be an issue. The LED matrix description would probably need a dedicated sub- node. I'll see if I can write some preliminary bindings later today or this weekend.
Best,
Sander
Quoted 4 lines · Sander Vanheule
Hi Andrew,
Thank you for the feedback. You can find a (leaked) datasheet at:
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_1.2.pdf
So this is not really an MFD. It has different ways of making use of pins, which could be used for GPIO, but can also be used for LEDs. You could look if it better fits in drivers/leds. But you can also use GPIO drivers for LEDs via led-gpio.
Quoted 18 lines
Quoted 18 lines
I don't understand this split. Why not
mdio-bus {
compatible = "vendor,mdio";
...
expander0: expander@0 {
/*
* Provide compatible for working registration of mdio device.
* Device probing happens in gpio1 node.
*/
compatible = "realtek,rtl8231-expander"; reg = <0>;
gpio-controller;
};
};
Quoted 8 lines
Quoted 3 lines
You can list whatever properties you need in the node. Ethernet switches have interrupt-controller, embedded MDIO busses with PHYs on them etc.
This is what I tried initially, but it doesn't seem to work. The node is probably still added as an MDIO device, but
rtl8231_gpio_probe()doesn't appear to get called at all. I do agree it would be preferable over the split specification.
Look at drivers/net/dsa/mv88e6xxx/chip.c for how to register an mdio driver. If you still cannot get it to work, post your code and i will take a look.
Andrew
Hi Andrew,
Quoted 10 lines · Andrew Lunn
Quoted 4 lines · Sander Vanheule
Hi Andrew,
Thank you for the feedback. You can find a (leaked) datasheet at:
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_1.2.pdfSo this is not really an MFD. It has different ways of making use of pins, which could be used for GPIO, but can also be used for LEDs. You could look if it better fits in
drivers/leds. But you can also use GPIO drivers for LEDs via led-gpio.
The chip provides LED scanning matrix functionality, for which one needs to set up row and column pins. The chip supports 3×12 + 3×12 + 2×8 (88) LEDs; a lot more than it has pins available. There is also (limited) support for hardware-accelerated blinking.
For example, single LED color scan matrix ("group A" for ports 0-11) would be wired up as follows:
Row and column pins of scan matrix for LED0, LED1, LED2 Columns control LED0/1/2 for ports [n] and [n+6]
L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]
| | | | | |
P0/P6 --X--------X--------X--------X--------X--------X (3)
| | | | | |
P1/P7 --X--------X--------X--------X--------X--------X (4)
| | | | | |
P2/P8 --X--------X--------X--------X--------X--------X (5)
| | | | | |
P3/P9 --X--------X--------X--------X--------X--------X (6)
| | | | | |
P4/P10 --X--------X--------X--------X--------X--------X (7)
| | | | | |
P5/P11 --X--------X--------X--------X--------X--------X (8)
(0) (1) (2) (9) (10) (11)
So far, I haven't seen any actual hardware implementation that uses the scanning matrix functionality (or the buzzer control feature with frequency selection).
1:1 use of GPIO pins for LEDs is indeed trivial with led-gpio, and I am currently using this on one of my devices.
Quoted 22 lines
Quoted 22 lines
Quoted 21 lines
I don't understand this split. Why not
mdio-bus {
compatible = "vendor,mdio";
...
expander0: expander@0 {
/*
* Provide compatible for working registration of mdio device.
* Device probing happens in gpio1 node.
*/
compatible = "realtek,rtl8231-expander"; reg = <0>;
gpio-controller;
};
};You can list whatever properties you need in the node. Ethernet switches have interrupt-controller, embedded MDIO busses with PHYs on them etc.
Quoted 8 lines
Quoted 4 lines
This is what I tried initially, but it doesn't seem to work. The node is probably still added as an MDIO device, but
rtl8231_gpio_probe()doesn't appear to get called at all. I do agree it would be preferable over the split specification.Look at
drivers/net/dsa/mv88e6xxx/chip.cfor how to register an mdio driver. If you still cannot get it to work, post your code and i will take a look.
Thanks for the suggestion. I've managed to create a cleaner mdio_device driver with a single corresponding DT node.
Would the following make sense for a more complete DT description? Or would I need sub-nodes to group e.g. the pin control or LED nodes/properties?
expander@31 {
/* Either "realtek,rtl8231-mdio" or "realtek,rtl8231-smi" */
compatible = "realtek,rtl8231-mdio";
reg = <31>;
/* Must be <1> (8 bit) or <2> (16 bit); only for "realtek,rtl8231-smi" */
realtek,smi-regnum-width = <1>;
/** GPIO controller properties **/
gpio-controller;
#gpio-cells = <2>;
ngpios = <37>;
poe_enable {
gpio-hog;
gpios = <10 0>;
output-high;
}; /** Pin controller properties **/
/* Can only set a global drive strength, 4mA or 8mA */ realtek,gpio-drive-strength = <4>;
/* Global LED scan matrix setting, 0 (single-color) or 1 (bi-color) */
realtek,led-color-scan-mode = <0>;
pinctrl-names = "default";
pinctrl-0 = <&user_button>, <&port_leds>;
user_button : user_button_cfg {
pins = "gpio31";
function = "gpio";
/* Only GPIOs 31-35 can do hardware debouncing */
/* Debouncing is either disabled or 100ms */
input-debounce = <100000>;
};
port_leds : port_leds_cfg {
/* Select two columns (LED colors) for switch ports 0-7 */
pins = "gpio0", "gpio1", "gpio9", "gpio10",
"gpio3", "gpio4", "gpio5", "gpio6";
function = "led";
};
/** LED config **/
#address-cells = <2>;
#size-cells = <0>;
[email protected] {
/* LED0 for port 0, corresponds to bits [2:0] in regnum 0x09 */
reg = <0 0>;
...
};
[email protected] {
/* LED1 for port 0 */
reg = <0 1>;
...
};
/* LEDs 1.x, 2.x, 3.x, 6.x, 7.x, 8.x, 9.x omitted */
};As a final remark, I have found out that this chip doesn't actually talk I2C, but rather Realtek's proprietary SMI. These two protocols are very similar w.r.t. to byte framing, but SMI requires the bus master to write the register number byte(s) on both READ and WRITE frames. I2C/SMBUS does a WRITE for the register number first, then a separate READ for the register value. This means I can't get regmap_i2c to work.
There is an existing, bit-banged implementation of this SMI protocol (see "realtek,smi-mdio", realtek- smi.c). If I could re-use this in some way, there would only need to be an MDIO implementation. However, we have noticed that the larger phy address space (7-bit in SMI vs. 5-bit in MDIO) did require a patch to make it work on ethernet switches with more than 32 ports (and corresponding phys) on a single SMI bus.
Best,
Sander