]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
realtek: mdio: backport upstream patches 23768/head
authorMarkus Stockhausen <markus.stockhausen@gmx.de>
Sat, 13 Jun 2026 14:36:31 +0000 (16:36 +0200)
committerMarkus Stockhausen <markus.stockhausen@gmx.de>
Sat, 13 Jun 2026 20:15:02 +0000 (22:15 +0200)
Another round of MDIO driver patches was accepted upstream.
Not production relevant as OpenWrt still uses the downstream
driver.

Link: https://github.com/openwrt/openwrt/pull/23768
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
target/linux/realtek/patches-6.18/031-21-v7.2-net-mdio-realtek-rtl9300-Add-prefix-to-register-fiel.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-22-v7.2-net-mdio-realtek-rtl9300-Make-otto_emdio_read_cmd-ge.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-23-v7.2-net-mdio-realtek-rtl9300-Add-registers-for-high-port.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-24-v7.2-net-mdio-realtek-rtl9300-Add-support-for-RTL931x.patch [new file with mode: 0644]

diff --git a/target/linux/realtek/patches-6.18/031-21-v7.2-net-mdio-realtek-rtl9300-Add-prefix-to-register-fiel.patch b/target/linux/realtek/patches-6.18/031-21-v7.2-net-mdio-realtek-rtl9300-Add-prefix-to-register-fiel.patch
new file mode 100644 (file)
index 0000000..e89eb4f
--- /dev/null
@@ -0,0 +1,147 @@
+From 474400cf3f9faec064a35a7725c72a52f5ec6e5f Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Sun, 7 Jun 2026 09:10:20 +0200
+Subject: [PATCH 04/15] net: mdio: realtek-rtl9300: Add prefix to register
+ field defines
+
+The current Realtek Otto MDIO driver has some define leftovers without
+a SoC prefix. When adding new devices there will be an overlap for some
+of them. Sort this out as follows:
+
+- PHY_CTRL_CMD/PHY_CTRL_MMD_DEVAD/PHY_CTRL_MMD_REG are common for all
+  series. Leave them as is but move them into a separate block.
+- Add RTL9300 prefix to all other defines and adapt the callers.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 63 +++++++++++++------------
+ 1 file changed, 32 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -59,23 +59,24 @@
+ #define RTL9300_SMI_PORT0_15_POLLING_SEL      0xca08
+ #define RTL9300_SMI_ACCESS_PHY_CTRL_0         0xcb70
+ #define RTL9300_SMI_ACCESS_PHY_CTRL_1         0xcb74
+-#define   PHY_CTRL_REG_ADDR                   GENMASK(24, 20)
+-#define   PHY_CTRL_PARK_PAGE                  GENMASK(19, 15)
+-#define   PHY_CTRL_MAIN_PAGE                  GENMASK(14, 3)
+-#define   PHY_CTRL_WRITE                      BIT(2)
+-#define   PHY_CTRL_READ                               0
+-#define   PHY_CTRL_TYPE_C45                   BIT(1)
+-#define   PHY_CTRL_TYPE_C22                   0
+-#define   PHY_CTRL_CMD                                BIT(0)
+-#define   PHY_CTRL_FAIL                               BIT(25)
++#define   RTL9300_PHY_CTRL_REG_ADDR           GENMASK(24, 20)
++#define   RTL9300_PHY_CTRL_PARK_PAGE          GENMASK(19, 15)
++#define   RTL9300_PHY_CTRL_MAIN_PAGE          GENMASK(14, 3)
++#define   RTL9300_PHY_CTRL_WRITE              BIT(2)
++#define   RTL9300_PHY_CTRL_READ                       0
++#define   RTL9300_PHY_CTRL_TYPE_C45           BIT(1)
++#define   RTL9300_PHY_CTRL_TYPE_C22           0
++#define   RTL9300_PHY_CTRL_FAIL                       BIT(25)
+ #define RTL9300_SMI_ACCESS_PHY_CTRL_2         0xcb78
+-#define   PHY_CTRL_INDATA                     GENMASK(31, 16)
+-#define   PHY_CTRL_DATA                               GENMASK(15, 0)
++#define   RTL9300_PHY_CTRL_INDATA             GENMASK(31, 16)
++#define   RTL9300_PHY_CTRL_DATA                       GENMASK(15, 0)
+ #define RTL9300_SMI_ACCESS_PHY_CTRL_3         0xcb7c
+-#define   PHY_CTRL_MMD_DEVAD                  GENMASK(20, 16)
+-#define   PHY_CTRL_MMD_REG                    GENMASK(15, 0)
+ #define RTL9300_SMI_PORT0_5_ADDR_CTRL         0xcb80
++#define PHY_CTRL_CMD                          BIT(0)
++#define PHY_CTRL_MMD_DEVAD                    GENMASK(20, 16)
++#define PHY_CTRL_MMD_REG                      GENMASK(15, 0)
++
+ #define MAP_ADDRS_PER_REG                     6
+ #define MAP_BITS_PER_ADDR                     5
+ #define MAP_BITS_PER_BUS                      2
+@@ -204,7 +205,7 @@ static int otto_emdio_read_cmd(struct mi
+       if (ret)
+               return ret;
+-      *value = FIELD_GET(PHY_CTRL_DATA, *value);
++      *value = FIELD_GET(RTL9300_PHY_CTRL_DATA, *value);
+       return 0;
+ }
+@@ -223,27 +224,27 @@ static int otto_emdio_9300_read_c22(stru
+ {
+       struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+       struct otto_emdio_cmd_regs cmd_data = {
+-              .c22_data       = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
+-                                FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
+-                                FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
+-              .io_data        = FIELD_PREP(PHY_CTRL_INDATA, port),
++              .c22_data       = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) |
++                                FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) |
++                                FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
++              .io_data        = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port),
+       };
+-      return otto_emdio_read_cmd(bus, PHY_CTRL_TYPE_C22, &cmd_data, value);
++      return otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C22, &cmd_data, value);
+ }
+ static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)
+ {
+       struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+       struct otto_emdio_cmd_regs cmd_data = {
+-              .c22_data       = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
+-                                FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
+-                                FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
+-              .io_data        = FIELD_PREP(PHY_CTRL_INDATA, value),
++              .c22_data       = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) |
++                                FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) |
++                                FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
++              .io_data        = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, value),
+               .port_mask_low  = BIT(port),
+       };
+-      return otto_emdio_write_cmd(bus, PHY_CTRL_TYPE_C22, &cmd_data);
++      return otto_emdio_write_cmd(bus, RTL9300_PHY_CTRL_TYPE_C22, &cmd_data);
+ }
+ static int otto_emdio_9300_read_c45(struct mii_bus *bus, int port,
+@@ -252,10 +253,10 @@ static int otto_emdio_9300_read_c45(stru
+       struct otto_emdio_cmd_regs cmd_data = {
+               .c45_data       = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+                                 FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
+-              .io_data        = FIELD_PREP(PHY_CTRL_INDATA, port),
++              .io_data        = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port),
+       };
+-      return otto_emdio_read_cmd(bus, PHY_CTRL_TYPE_C45, &cmd_data, value);
++      return otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, &cmd_data, value);
+ }
+ static int otto_emdio_9300_write_c45(struct mii_bus *bus, int port,
+@@ -264,11 +265,11 @@ static int otto_emdio_9300_write_c45(str
+       struct otto_emdio_cmd_regs cmd_data = {
+               .c45_data       = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+                                 FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
+-              .io_data        = FIELD_PREP(PHY_CTRL_INDATA, value),
++              .io_data        = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, value),
+               .port_mask_low  = BIT(port),
+       };
+-      return otto_emdio_write_cmd(bus, PHY_CTRL_TYPE_C45, &cmd_data);
++      return otto_emdio_write_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, &cmd_data);
+ }
+ static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
+@@ -582,9 +583,9 @@ static int otto_emdio_probe(struct platf
+ static const struct otto_emdio_info otto_emdio_9300_info = {
+       .addr_map_base = RTL9300_SMI_PORT0_5_ADDR_CTRL,
+       .bus_map_base = RTL9300_SMI_PORT0_15_POLLING_SEL,
+-      .cmd_fail = PHY_CTRL_FAIL,
+-      .cmd_read = PHY_CTRL_READ,
+-      .cmd_write = PHY_CTRL_WRITE,
++      .cmd_fail = RTL9300_PHY_CTRL_FAIL,
++      .cmd_read = RTL9300_PHY_CTRL_READ,
++      .cmd_write = RTL9300_PHY_CTRL_WRITE,
+       .cmd_regs = {
+               .c22_data = RTL9300_SMI_ACCESS_PHY_CTRL_1,
+               .c45_data = RTL9300_SMI_ACCESS_PHY_CTRL_3,
diff --git a/target/linux/realtek/patches-6.18/031-22-v7.2-net-mdio-realtek-rtl9300-Make-otto_emdio_read_cmd-ge.patch b/target/linux/realtek/patches-6.18/031-22-v7.2-net-mdio-realtek-rtl9300-Make-otto_emdio_read_cmd-ge.patch
new file mode 100644 (file)
index 0000000..a5998f1
--- /dev/null
@@ -0,0 +1,63 @@
+From 2b24f54b8751e483a67443f93941a81c05ccce67 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Sun, 7 Jun 2026 10:12:51 +0200
+Subject: [PATCH 05/15] net: mdio: realtek-rtl9300: Make otto_emdio_read_cmd()
+ generic
+
+The otto_emdio_read_cmd() helper still uses RTL9300 specific properties.
+This cannot be made generic as the I/O register has different layouts for
+the different SoCs. E.g.
+
+- RTL930x: data in bits 31-16, data out bits 15-0
+- RTL931x: data in bits 15-0, data out bits 31-16
+
+Add a mask parameter to the function signature and fill it properly
+in the callers. As the masks will always have bits set from constant
+defines, there is no need for a consistency check.
+
+Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -191,7 +191,7 @@ static int otto_emdio_run_cmd(struct mii
+ }
+ static int otto_emdio_read_cmd(struct mii_bus *bus, u32 cmd,
+-                             struct otto_emdio_cmd_regs *cmd_data, u32 *value)
++                             struct otto_emdio_cmd_regs *cmd_data, u32 mask, u32 *value)
+ {
+       struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+       int ret;
+@@ -205,7 +205,7 @@ static int otto_emdio_read_cmd(struct mi
+       if (ret)
+               return ret;
+-      *value = FIELD_GET(RTL9300_PHY_CTRL_DATA, *value);
++      *value = field_get(mask, *value);
+       return 0;
+ }
+@@ -230,7 +230,8 @@ static int otto_emdio_9300_read_c22(stru
+               .io_data        = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port),
+       };
+-      return otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C22, &cmd_data, value);
++      return otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C22, &cmd_data,
++                                 RTL9300_PHY_CTRL_DATA, value);
+ }
+ static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)
+@@ -256,7 +257,8 @@ static int otto_emdio_9300_read_c45(stru
+               .io_data        = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port),
+       };
+-      return otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, &cmd_data, value);
++      return otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, &cmd_data,
++                                 RTL9300_PHY_CTRL_DATA, value);
+ }
+ static int otto_emdio_9300_write_c45(struct mii_bus *bus, int port,
diff --git a/target/linux/realtek/patches-6.18/031-23-v7.2-net-mdio-realtek-rtl9300-Add-registers-for-high-port.patch b/target/linux/realtek/patches-6.18/031-23-v7.2-net-mdio-realtek-rtl9300-Add-registers-for-high-port.patch
new file mode 100644 (file)
index 0000000..06e6607
--- /dev/null
@@ -0,0 +1,58 @@
+From 2628d4b57215ff57c062644523badbabeb22a095 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Sun, 7 Jun 2026 09:24:09 +0200
+Subject: [PATCH 06/15] net: mdio: realtek-rtl9300: Add registers for high port
+ count models
+
+The high port count models of the Realtek Otto switches have additional
+registers to instrument the MDIO controller. These are:
+
+- High port mask: A bitfield that extends the already existing low port
+  mask to select ports starting from 32.
+- Broadcast: This takes the port number during reads on the RTL931x.
+- Extended page: Some additional page info. The SDK does not give much
+  information about this. Basically some fixed value must be written
+  into it during access.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -91,6 +91,10 @@ struct otto_emdio_cmd_regs {
+       u32 c45_data;
+       u32 io_data;
+       u32 port_mask_low;
++      /* additional registers for high port count models RTL839x/RTL931x */
++      u32 port_mask_high;
++      u32 broadcast;
++      u32 ext_page;
+ };
+ struct otto_emdio_priv {
+@@ -164,6 +168,22 @@ static int otto_emdio_run_cmd(struct mii
+               return ret;
+       /* Fill all registers. Hardware will read only the needed bits depending on command */
++      if (info->cmd_regs.port_mask_high) {
++              /* Fill extra registers for high port count models */
++              ret = regmap_write(priv->regmap, info->cmd_regs.broadcast, cmd_data->broadcast);
++              if (ret)
++                      return ret;
++
++              ret = regmap_write(priv->regmap, info->cmd_regs.ext_page, cmd_data->ext_page);
++              if (ret)
++                      return ret;
++
++              ret = regmap_write(priv->regmap,
++                                 info->cmd_regs.port_mask_high, cmd_data->port_mask_high);
++              if (ret)
++                      return ret;
++      }
++
+       ret = regmap_write(priv->regmap, info->cmd_regs.port_mask_low, cmd_data->port_mask_low);
+       if (ret)
+               return ret;
diff --git a/target/linux/realtek/patches-6.18/031-24-v7.2-net-mdio-realtek-rtl9300-Add-support-for-RTL931x.patch b/target/linux/realtek/patches-6.18/031-24-v7.2-net-mdio-realtek-rtl9300-Add-support-for-RTL931x.patch
new file mode 100644 (file)
index 0000000..acb9e90
--- /dev/null
@@ -0,0 +1,187 @@
+From 390dfbbd1ead27523853a21fd587b1285006a4ef Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Sun, 7 Jun 2026 10:09:44 +0200
+Subject: [PATCH 07/15] net: mdio: realtek-rtl9300: Add support for RTL931x
+
+The MDIO driver has been prepared for multiple device support. Add all
+required bits for the RTL931x (aka mango) series. This is straightforward
+but some things are worth to be mentioned.
+
+- In contrast to RTL930x the I/O register has the input/output fields
+  swapped. Upper 16 bits are for read/outputs, and the lower 16 bits
+  are for write/inputs.
+- The supported "pages" are 8192 and thus the raw page is 8191
+- The devices support up to 56 ports. Thus the MAX_PORTS definition
+  is increased by this commit.
+- There are multiple global SMI controller registers with a different
+  layout from RTL930x devices. Therefore a separate setup_controller()
+  callback is added.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 123 +++++++++++++++++++++++-
+ 1 file changed, 122 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -73,6 +73,31 @@
+ #define RTL9300_SMI_ACCESS_PHY_CTRL_3         0xcb7c
+ #define RTL9300_SMI_PORT0_5_ADDR_CTRL         0xcb80
++#define RTL9310_NUM_BUSES                     4
++#define RTL9310_NUM_PAGES                     8192
++#define RTL9310_NUM_PORTS                     56
++#define RTL9310_SMI_GLB_CTRL1                 0x0cbc
++#define   RTL9310_SMI_GLB_FMT_SEL_C45(intf)   BIT((intf) * 2 + 1)
++#define RTL9310_SMI_INDRT_ACCESS_CTRL_0               0x0c00
++#define   RTL9310_PHY_CTRL_REG_ADDR           GENMASK(10, 6)
++#define   RTL9310_PHY_CTRL_MAIN_PAGE          GENMASK(23, 11)
++#define   RTL9310_PHY_CTRL_READ                       0
++#define   RTL9310_PHY_CTRL_WRITE              BIT(4)
++#define   RTL9310_PHY_CTRL_TYPE_C45           BIT(3)
++#define   RTL9310_PHY_CTRL_TYPE_C22           0
++#define   RTL9310_PHY_CTRL_FAIL                       BIT(1)
++#define RTL9310_SMI_INDRT_ACCESS_BC_PHYID_CTRL        0x0c14
++#define   RTL9310_BC_PORT_ID                  GENMASK(10, 5)
++#define RTL9310_SMI_INDRT_ACCESS_CTRL_1               0x0c04
++#define RTL9310_SMI_INDRT_ACCESS_CTRL_2_LOW   0x0c08
++#define RTL9310_SMI_INDRT_ACCESS_CTRL_2_HIGH  0x0c0c
++#define RTL9310_SMI_INDRT_ACCESS_CTRL_3               0x0c10 /* I/O fields flipped */
++#define   RTL9310_PHY_CTRL_DATA                       GENMASK(31, 16)
++#define   RTL9310_PHY_CTRL_INDATA             GENMASK(15, 0)
++#define RTL9310_SMI_INDRT_ACCESS_MMD_CTRL     0x0c18
++#define RTL9310_SMI_PORT_ADDR_CTRL            0x0c74
++#define RTL9310_SMI_PORT_POLLING_SEL          0x0c9c
++
+ #define PHY_CTRL_CMD                          BIT(0)
+ #define PHY_CTRL_MMD_DEVAD                    GENMASK(20, 16)
+ #define PHY_CTRL_MMD_REG                      GENMASK(15, 0)
+@@ -81,7 +106,7 @@
+ #define MAP_BITS_PER_ADDR                     5
+ #define MAP_BITS_PER_BUS                      2
+ #define MAP_BUSES_PER_REG                     16
+-#define MAX_PORTS                             28
++#define MAX_PORTS                             56
+ #define MAX_SMI_BUSSES                                4
+ #define RAW_PAGE(priv)                                ((priv)->info->num_pages - 1)
+@@ -294,6 +319,60 @@ static int otto_emdio_9300_write_c45(str
+       return otto_emdio_write_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, &cmd_data);
+ }
++static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      struct otto_emdio_cmd_regs cmd_data = {
++              .broadcast      = FIELD_PREP(RTL9310_BC_PORT_ID, port),
++              .c22_data       = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |
++                                FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
++      };
++
++      return otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, &cmd_data,
++                                 RTL9310_PHY_CTRL_DATA, value);
++}
++
++static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      struct otto_emdio_cmd_regs cmd_data = {
++              .c22_data       = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |
++                                FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
++              .io_data        = FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value),
++              .port_mask_high = (u32)(BIT_ULL(port) >> 32),
++              .port_mask_low  = (u32)(BIT_ULL(port)),
++      };
++
++      return otto_emdio_write_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, &cmd_data);
++}
++
++static int otto_emdio_9310_read_c45(struct mii_bus *bus, int port,
++                                  int dev_addr, int regnum, u32 *value)
++{
++      struct otto_emdio_cmd_regs cmd_data = {
++              .broadcast      = FIELD_PREP(RTL9310_BC_PORT_ID, port),
++              .c45_data       = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
++                                FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
++      };
++
++      return otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C45, &cmd_data,
++                                 RTL9310_PHY_CTRL_DATA, value);
++}
++
++static int otto_emdio_9310_write_c45(struct mii_bus *bus, int port,
++                                   int dev_addr, int regnum, u16 value)
++{
++      struct otto_emdio_cmd_regs cmd_data = {
++              .c45_data       = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
++                                FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
++              .io_data        = FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value),
++              .port_mask_high = (u32)(BIT_ULL(port) >> 32),
++              .port_mask_low  = (u32)(BIT_ULL(port)),
++      };
++
++      return otto_emdio_write_cmd(bus, RTL9310_PHY_CTRL_TYPE_C45, &cmd_data);
++}
++
+ static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
+ {
+       struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+@@ -413,6 +492,22 @@ static int otto_emdio_9300_setup_control
+       return 0;
+ }
++static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)
++{
++      int i, err;
++
++      /* Put the interfaces into C45 mode if required */
++      for (i = 0; i < priv->info->num_buses; i++) {
++              err = regmap_assign_bits(priv->regmap, RTL9310_SMI_GLB_CTRL1,
++                                       RTL9310_SMI_GLB_FMT_SEL_C45(i),
++                                       priv->smi_bus_is_c45[i]);
++              if (err)
++                      return err;
++      }
++
++      return 0;
++}
++
+ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,
+                                struct fwnode_handle *node)
+ {
+@@ -624,8 +719,34 @@ static const struct otto_emdio_info otto
+       .write_c45 = otto_emdio_9300_write_c45,
+ };
++static const struct otto_emdio_info otto_emdio_9310_info = {
++      .addr_map_base = RTL9310_SMI_PORT_ADDR_CTRL,
++      .bus_map_base = RTL9310_SMI_PORT_POLLING_SEL,
++      .cmd_fail = RTL9310_PHY_CTRL_FAIL,
++      .cmd_read = RTL9310_PHY_CTRL_READ,
++      .cmd_write = RTL9310_PHY_CTRL_WRITE,
++      .cmd_regs = {
++              .broadcast = RTL9310_SMI_INDRT_ACCESS_BC_PHYID_CTRL,
++              .c22_data = RTL9310_SMI_INDRT_ACCESS_CTRL_0,
++              .c45_data = RTL9310_SMI_INDRT_ACCESS_MMD_CTRL,
++              .ext_page = RTL9310_SMI_INDRT_ACCESS_CTRL_1,
++              .io_data = RTL9310_SMI_INDRT_ACCESS_CTRL_3,
++              .port_mask_low = RTL9310_SMI_INDRT_ACCESS_CTRL_2_LOW,
++              .port_mask_high = RTL9310_SMI_INDRT_ACCESS_CTRL_2_HIGH,
++      },
++      .num_buses = RTL9310_NUM_BUSES,
++      .num_pages = RTL9310_NUM_PAGES,
++      .num_ports = RTL9310_NUM_PORTS,
++      .setup_controller = otto_emdio_9310_setup_controller,
++      .read_c22 = otto_emdio_9310_read_c22,
++      .read_c45 = otto_emdio_9310_read_c45,
++      .write_c22 = otto_emdio_9310_write_c22,
++      .write_c45 = otto_emdio_9310_write_c45,
++};
++
+ static const struct of_device_id otto_emdio_ids[] = {
+       { .compatible = "realtek,rtl9301-mdio", .data = &otto_emdio_9300_info },
++      { .compatible = "realtek,rtl9311-mdio", .data = &otto_emdio_9310_info },
+       {}
+ };
+ MODULE_DEVICE_TABLE(of, otto_emdio_ids);