Netdev List
 help / color / mirror / Atom feed
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 v3 4/4] net: mdio: realtek-rtl9300: Support non-default clock frequency
Date: Wed,  9 Sep 2026 17:01:07 +0200	[thread overview]
Message-ID: <20260909150107.3602067-5-markus.stockhausen@gmx.de> (raw)
In-Reply-To: <20260909150107.3602067-1-markus.stockhausen@gmx.de>

The ethernet MDIO controller of the Realtek Otto switches has a default
MDC clock frequency of 2.5MHz. Via configuration bits one can adapt this.
Depending on the series the following can be set

- RTL838x: 2.5 Mhz, 10 MHz
- RTL839x, RTL930x, RTL931x: 1.25 MHz, 2.5 MHz, 5 Mhz

Enhance the driver so it allows to set the clock frequency via device
tree. This helps to

- simplify protocol analysis with a logic analyzer (lower speeds)
- reduce MDIO access times during normal operation (higher speeds)

Note: While the devices normally support a per-bus frequency selection,
the RTL839x is an exception from that. It can only select the speed for
both buses globally. To avoid device tree special treatment for this,
use the per-bus speeds for this series too. Return an error if the
RTL839x Devicetree specifies different frequencies for the first and
second bus.

Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
 drivers/net/mdio/mdio-realtek-rtl9300.c | 97 ++++++++++++++++++++++++-
 1 file changed, 96 insertions(+), 1 deletion(-)

diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c
index 14fc98949fa3..d40fdd0ed2f0 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -130,6 +130,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
@@ -169,12 +170,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
@@ -196,6 +200,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
@@ -269,6 +275,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);
@@ -740,6 +747,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)
 {
 	/*
@@ -749,6 +767,57 @@ 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);
+	bool freq_set = false;
+	u32 child_freq, val;
+	int err;
+
+	/*
+	 * RTL839x uses a single frequency for the two MDIO buses. During registration of first
+	 * bus pick an explicitly requested frequency from any enabled bus. Use the default only
+	 * if no frequency is set at all.
+	 */
+	if (priv->bus[0] || priv->bus[1])
+		return 0;
+
+	freq = 2500000;
+	for_each_available_child_of_node_scoped(bus->parent->of_node, child) {
+		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);
+	err = regmap_update_bits(priv->regmap, RTL8390_SMI_GLB_CTRL, RTL8390_SMI_FREQ_SEL, val);
+
+	return err;
+}
+
+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;
@@ -769,6 +838,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;
@@ -835,7 +919,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);
@@ -868,6 +952,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");
@@ -1056,6 +1147,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,
@@ -1080,6 +1172,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,
@@ -1102,6 +1195,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,
@@ -1128,6 +1222,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


  parent reply	other threads:[~2026-09-09 15:01 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-09 15:01 [PATCH net-next v3 0/4] net: mdio: realtek-rtl9300: add bus frequency handling Markus Stockhausen
2026-09-09 15:01 ` [PATCH net-next v3 1/4] dt-bindings: net: realtek,rtl9301-mdio: Add clock-frequency Markus Stockhausen
2026-09-10 15:04   ` netdev-bot+sashiko
2026-09-09 15:01 ` [PATCH net-next v3 2/4] net: mdio: realtek-rtl9300: Convert "fwnode" left-overs to "of" Markus Stockhausen
2026-09-10 15:04   ` netdev-bot+sashiko
2026-09-09 15:01 ` [PATCH net-next v3 3/4] net: mdio: realtek-rtl9300: Reject duplicate MDIO bus IDs Markus Stockhausen
2026-09-09 15:01 ` Markus Stockhausen [this message]
2026-09-10 15:04   ` [PATCH net-next v3 4/4] net: mdio: realtek-rtl9300: Support non-default clock frequency netdev-bot+sashiko

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=20260909150107.3602067-5-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