From: Markus Stockhausen <markus.stockhausen@gmx.de>
To: andrew@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, netdev@vger.kernel.org,
chris.packham@alliedtelesis.co.nz, robh@kernel.org,
krzk+dt@kernel.org, conor+dt@kernel.org,
devicetree@vger.kernel.org
Cc: Markus Stockhausen <markus.stockhausen@gmx.de>
Subject: [PATCH net-next v4 5/5] net: mdio: realtek-rtl9300: support non-default clock frequencies
Date: Sat, 12 Sep 2026 15:04:51 +0200 [thread overview]
Message-ID: <20260912130451.3757276-6-markus.stockhausen@gmx.de> (raw)
In-Reply-To: <20260912130451.3757276-1-markus.stockhausen@gmx.de>
The Ethernet MDIO controllers in Realtek Otto switches use an MDC clock
frequency of 2.5 MHz by default. Hardware configuration fields allow the
following frequencies to be selected:
- RTL838x: 2.5 MHz or 10 MHz
- RTL839x, RTL930x and RTL931x: 1.25 MHz, 2.5 MHz or 5 MHz
Add support for configuring the MDC frequency through the
clock-frequency property of each MDIO bus node. Lower frequencies can
simplify protocol analysis with a logic analyzer, while higher
frequencies reduce MDIO transaction times during normal operation.
Program the frequency selector during every probe. If clock-frequency is
absent, select the defined default of 2.5 MHz. This matches the
configuration observed with a protocol analyzer under the stock firmware
or after issuing "rtk network on" in the bootloader.
RTL839x is an exception because both MDIO buses share a single frequency
field. Use an explicitly specified frequency as the shared frequency and
let buses without the property inherit it. Use the 2.5 MHz default if
neither bus specifies a frequency. Reject conflicting explicit
frequencies because the hardware cannot configure them independently.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/net/mdio/mdio-realtek-rtl9300.c | 100 +++++++++++++++++++++++-
1 file changed, 99 insertions(+), 1 deletion(-)
diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c
index 45cbefc52100..bde0cf5e0b79 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -129,6 +129,7 @@
#define RTL8380_NUM_PAGES 4096
#define RTL8380_NUM_PORTS 28
#define RTL8380_SMI_GLB_CTRL 0xa100
+#define RTL8380_SMI_FREQ_SEL BIT(1)
#define RTL8380_SMI_PHY_PATCH_DONE BIT(15)
#define RTL8380_SMI_ACCESS_PHY_CTRL_0 0xa1b8
#define RTL8380_SMI_ACCESS_PHY_CTRL_1 0xa1bc
@@ -168,12 +169,15 @@
#define RTL8390_PHYREG_PORT_CTRL_LOW 0x03e4
#define RTL8390_PHYREG_PORT_CTRL_HIGH 0x03e8
#define RTL8390_SMI_PORT_POLLING_CTRL 0x03fc
+#define RTL8390_SMI_GLB_CTRL 0x03f8
+#define RTL8390_SMI_FREQ_SEL GENMASK(6, 5)
#define RTL9300_NUM_BUSES 4
#define RTL9300_NUM_PAGES 4096
#define RTL9300_NUM_PORTS 28
#define RTL9300_SMI_GLB_CTRL 0xca00
#define RTL9300_GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf))
+#define RTL9300_SMI_FREQ_SEL(intf) GENMASK((intf) * 2 + 9, (intf) * 2 + 8)
#define RTL9300_SMI_PORT0_15_POLLING_SEL 0xca08
#define RTL9300_SMI_ACCESS_PHY_CTRL_0 0xcb70
#define RTL9300_SMI_ACCESS_PHY_CTRL_1 0xcb74
@@ -195,6 +199,8 @@
#define RTL9310_NUM_BUSES 4
#define RTL9310_NUM_PAGES 8192
#define RTL9310_NUM_PORTS 56
+#define RTL9310_SMI_GLB_CTRL0 0x0cc0
+#define RTL9310_SMI_FREQ_SEL(intf) GENMASK((intf) * 2 + 5, (intf) * 2 + 4)
#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
@@ -268,6 +274,7 @@ struct otto_emdio_info {
u8 num_ports;
u16 num_pages;
u32 poll_ctrl;
+ int (*set_bus_frequency)(struct mii_bus *bus, u32 freq);
int (*setup_controller)(struct otto_emdio_priv *priv);
int (*read_c22)(struct mii_bus *bus, int port, int regnum, u32 *value);
int (*read_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u32 *value);
@@ -739,6 +746,17 @@ static int otto_emdio_setup_topology(struct otto_emdio_priv *priv)
return 0;
}
+static int otto_emdio_8380_set_bus_frequency(struct mii_bus *bus, u32 freq)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+
+ if (freq != 2500000 && freq != 10000000)
+ return -EINVAL;
+
+ return regmap_assign_bits(priv->regmap, RTL8380_SMI_GLB_CTRL,
+ RTL8380_SMI_FREQ_SEL, freq == 10000000);
+}
+
static int otto_emdio_8380_setup_controller(struct otto_emdio_priv *priv)
{
/*
@@ -748,6 +766,60 @@ static int otto_emdio_8380_setup_controller(struct otto_emdio_priv *priv)
return regmap_set_bits(priv->regmap, RTL8380_SMI_GLB_CTRL, RTL8380_SMI_PHY_PATCH_DONE);
}
+static int otto_emdio_8390_set_bus_frequency(struct mii_bus *bus, u32 freq)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+ u32 mdio_bus, child_freq, val;
+ bool freq_set = false;
+ int err;
+
+ /*
+ * RTL839x uses one frequency shared by both MDIO buses. Use the frequency passed by the
+ * caller as fallback, but prefer an explicitly requested frequency from any valid,
+ * available bus. Reject conflicting explicit requests. Only the first call programs the
+ * shared frequency.
+ */
+ if (priv->bus[0] || priv->bus[1])
+ return 0;
+
+ for_each_available_child_of_node_scoped(bus->parent->of_node, child) {
+ err = of_property_read_u32(child, "reg", &mdio_bus);
+ if (err || mdio_bus >= priv->info->num_buses)
+ continue;
+
+ if (of_property_read_u32(child, "clock-frequency", &child_freq))
+ continue;
+
+ if (freq_set && freq != child_freq)
+ return -EINVAL;
+
+ freq = child_freq;
+ freq_set = true;
+ }
+
+ if (freq != 1250000 && freq != 2500000 && freq != 5000000)
+ return -EINVAL;
+
+ val = FIELD_PREP(RTL8390_SMI_FREQ_SEL, freq / 2500000);
+
+ return regmap_update_bits(priv->regmap, RTL8390_SMI_GLB_CTRL, RTL8390_SMI_FREQ_SEL, val);
+}
+
+static int otto_emdio_9300_set_bus_frequency(struct mii_bus *bus, u32 freq)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+ struct otto_emdio_chan *chan = bus->priv;
+ u32 mask, val;
+
+ if (freq != 1250000 && freq != 2500000 && freq != 5000000)
+ return -EINVAL;
+
+ mask = RTL9300_SMI_FREQ_SEL(chan->mdio_bus);
+ val = field_prep(mask, freq / 2500000);
+
+ return regmap_update_bits(priv->regmap, RTL9300_SMI_GLB_CTRL, mask, val);
+}
+
static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv)
{
u32 glb_ctrl_mask = 0, glb_ctrl_val = 0;
@@ -768,6 +840,21 @@ static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv)
return 0;
}
+static int otto_emdio_9310_set_bus_frequency(struct mii_bus *bus, u32 freq)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+ struct otto_emdio_chan *chan = bus->priv;
+ u32 mask, val;
+
+ if (freq != 1250000 && freq != 2500000 && freq != 5000000)
+ return -EINVAL;
+
+ mask = RTL9310_SMI_FREQ_SEL(chan->mdio_bus);
+ val = field_prep(mask, freq / 2500000);
+
+ return regmap_update_bits(priv->regmap, RTL9310_SMI_GLB_CTRL0, mask, val);
+}
+
static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)
{
int i, err;
@@ -834,7 +921,7 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv
{
struct otto_emdio_chan *chan;
struct mii_bus *bus;
- u32 mdio_bus;
+ u32 mdio_bus, freq;
int err;
err = of_property_read_u32(node, "reg", &mdio_bus);
@@ -867,6 +954,13 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", dev_name(dev), mdio_bus);
+ if (of_property_read_u32(node, "clock-frequency", &freq))
+ freq = 2500000;
+ err = priv->info->set_bus_frequency(bus, freq);
+ if (err)
+ return dev_err_probe(dev, err, "cannot set frequency %u for MDIO bus %d\n",
+ freq, mdio_bus);
+
err = devm_of_mdiobus_register(dev, bus, node);
if (err)
return dev_err_probe(dev, err, "cannot register MDIO bus\n");
@@ -1055,6 +1149,7 @@ static const struct otto_emdio_info otto_emdio_8380_info = {
.num_pages = RTL8380_NUM_PAGES,
.num_ports = RTL8380_NUM_PORTS,
.poll_ctrl = RTL8380_SMI_POLL_CTRL,
+ .set_bus_frequency = otto_emdio_8380_set_bus_frequency,
.setup_controller = otto_emdio_8380_setup_controller,
.read_c22 = otto_emdio_8380_read_c22,
.read_c45 = otto_emdio_8380_read_c45,
@@ -1079,6 +1174,7 @@ static const struct otto_emdio_info otto_emdio_8390_info = {
.num_pages = RTL8390_NUM_PAGES,
.num_ports = RTL8390_NUM_PORTS,
.poll_ctrl = RTL8390_SMI_PORT_POLLING_CTRL,
+ .set_bus_frequency = otto_emdio_8390_set_bus_frequency,
.read_c22 = otto_emdio_8390_read_c22,
.read_c45 = otto_emdio_8390_read_c45,
.write_c22 = otto_emdio_8390_write_c22,
@@ -1101,6 +1197,7 @@ static const struct otto_emdio_info otto_emdio_9300_info = {
.num_ports = RTL9300_NUM_PORTS,
.num_pages = RTL9300_NUM_PAGES,
.poll_ctrl = RTL9300_SMI_POLL_CTRL,
+ .set_bus_frequency = otto_emdio_9300_set_bus_frequency,
.setup_controller = otto_emdio_9300_setup_controller,
.read_c22 = otto_emdio_9300_read_c22,
.read_c45 = otto_emdio_9300_read_c45,
@@ -1127,6 +1224,7 @@ static const struct otto_emdio_info otto_emdio_9310_info = {
.num_pages = RTL9310_NUM_PAGES,
.num_ports = RTL9310_NUM_PORTS,
.poll_ctrl = RTL9310_SMI_PORT_POLLING_CTRL,
+ .set_bus_frequency = otto_emdio_9310_set_bus_frequency,
.setup_controller = otto_emdio_9310_setup_controller,
.read_c22 = otto_emdio_9310_read_c22,
.read_c45 = otto_emdio_9310_read_c45,
--
2.55.0
prev parent reply other threads:[~2026-09-12 13:05 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-12 13:04 [PATCH net-next v4 0/5] net: mdio: realtek-rtl9300: add bus frequency handling Markus Stockhausen
2026-09-12 13:04 ` [PATCH net-next v4 1/5] dt-bindings: net: realtek,rtl9301-mdio: restrict MDIO buses by family Markus Stockhausen
2026-09-12 13:04 ` [PATCH net-next v4 2/5] dt-bindings: net: realtek,rtl9301-mdio: add clock-frequency Markus Stockhausen
2026-09-12 13:04 ` [PATCH net-next v4 3/5] net: mdio: realtek-rtl9300: convert "fwnode" left-overs to "of" Markus Stockhausen
2026-09-12 13:04 ` [PATCH net-next v4 4/5] net: mdio: realtek-rtl9300: reject duplicate MDIO bus IDs Markus Stockhausen
2026-09-12 13:04 ` Markus Stockhausen [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260912130451.3757276-6-markus.stockhausen@gmx.de \
--to=markus.stockhausen@gmx.de \
--cc=andrew@lunn.ch \
--cc=chris.packham@alliedtelesis.co.nz \
--cc=conor+dt@kernel.org \
--cc=davem@davemloft.net \
--cc=devicetree@vger.kernel.org \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=kuba@kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox