// SPDX-License-Identifier: GPL-2.0-only
/*
- * MDIO controller for RTL9300 switches with integrated SoC.
+ * Realtek switches of the Otto series (RTL838x, RTL839x, RTL930x and RTL931x SoCs) have multiple
+ * integrated MDIO controllers. This driver targets the ethernet MDIO controller. It serves only
+ * 1G/2.5G/10G ethernet PHYs attached to up to 4 individual buses.
*
- * The MDIO communication is abstracted by the switch. At the software level
- * communication uses the switch port to address the PHY. We work out the
- * mapping based on the MDIO bus described in device tree and phandles on the
- * ethernet-ports property.
+ * The controller is programmed through MMIO. The MDIO communication is abstracted by the hardware
+ * and uses the switch port number for its addressing. For this to work, mapping registers need to
+ * be setup in advance. With that the controller translates each port based I/O operation into the
+ * physical bus and address. This gives the following end-to-end communication
+ *
+ * +----------+ +----------+ +----------+ +----------+
+ * | phydev | ... | phydev | | phydev | ... | phydev |
+ * +----------+ +----------+ +----------+ +----------+
+ * | | | |
+ * mii_bus 0 +------------------+ +------------------+ mii_bus 1
+ * | |
+ * +-----------------------------------------------------+
+ * | MDIO driver |
+ * | translate bus/address -> port |
+ * +-----------------------------------------------------+
+ * | Software
+ * - - - - - - - - - - - - - - - - - - - - - - - - -
+ * | Hardware
+ * +-----------------------------------------------------+
+ * | MDIO controller |
+ * | translate port -> bus/address |
+ * +-----------------------------------------------------+
+ * | |
+ * bus 0 +------------------+ +------------------+ bus 1
+ * | | | |
+ * +----------+ +----------+ +----------+ +----------+
+ * | PHY 0/1 | ... | PHY 0/31 | | PHY 1/1 | ... | PHY 1/31 |
+ * +----------+ +----------+ +----------+ +----------+
+ *
+ * The driver works out the mapping based on the MDIO bus described in device tree and phandles on
+ * the ethernet-ports property.
*/
#include <linux/bitfield.h>
#define MAX_SMI_BUSSES 4
#define MAX_SMI_ADDR 0x1f
-struct rtl9300_mdio_priv {
+struct otto_emdio_priv {
struct regmap *regmap;
struct mutex lock; /* protect HW access */
DECLARE_BITMAP(valid_ports, MAX_PORTS);
struct mii_bus *bus[MAX_SMI_BUSSES];
};
-struct rtl9300_mdio_chan {
- struct rtl9300_mdio_priv *priv;
+struct otto_emdio_chan {
+ struct otto_emdio_priv *priv;
u8 mdio_bus;
};
-static int rtl9300_mdio_phy_to_port(struct mii_bus *bus, int phy_id)
+static int otto_emdio_phy_to_port(struct mii_bus *bus, int phy_id)
{
- struct rtl9300_mdio_chan *chan = bus->priv;
- struct rtl9300_mdio_priv *priv;
+ struct otto_emdio_chan *chan = bus->priv;
+ struct otto_emdio_priv *priv;
int i;
priv = chan->priv;
return -ENOENT;
}
-static int rtl9300_mdio_wait_ready(struct rtl9300_mdio_priv *priv)
+static int otto_emdio_wait_ready(struct otto_emdio_priv *priv)
{
struct regmap *regmap = priv->regmap;
u32 val;
val, !(val & PHY_CTRL_CMD), 10, 1000);
}
-static int rtl9300_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
+static int otto_emdio_9300_read_c22(struct mii_bus *bus, int phy_id, int regnum)
{
- struct rtl9300_mdio_chan *chan = bus->priv;
- struct rtl9300_mdio_priv *priv;
+ struct otto_emdio_chan *chan = bus->priv;
+ struct otto_emdio_priv *priv;
struct regmap *regmap;
int port;
u32 val;
priv = chan->priv;
regmap = priv->regmap;
- port = rtl9300_mdio_phy_to_port(bus, phy_id);
+ port = otto_emdio_phy_to_port(bus, phy_id);
if (port < 0)
return port;
mutex_lock(&priv->lock);
- err = rtl9300_mdio_wait_ready(priv);
+ err = otto_emdio_wait_ready(priv);
if (err)
goto out_err;
if (err)
goto out_err;
- err = rtl9300_mdio_wait_ready(priv);
+ err = otto_emdio_wait_ready(priv);
if (err)
goto out_err;
return err;
}
-static int rtl9300_mdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)
+static int otto_emdio_9300_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)
{
- struct rtl9300_mdio_chan *chan = bus->priv;
- struct rtl9300_mdio_priv *priv;
+ struct otto_emdio_chan *chan = bus->priv;
+ struct otto_emdio_priv *priv;
struct regmap *regmap;
int port;
u32 val;
priv = chan->priv;
regmap = priv->regmap;
- port = rtl9300_mdio_phy_to_port(bus, phy_id);
+ port = otto_emdio_phy_to_port(bus, phy_id);
if (port < 0)
return port;
mutex_lock(&priv->lock);
- err = rtl9300_mdio_wait_ready(priv);
+ err = otto_emdio_wait_ready(priv);
if (err)
goto out_err;
return err;
}
-static int rtl9300_mdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum)
+static int otto_emdio_9300_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum)
{
- struct rtl9300_mdio_chan *chan = bus->priv;
- struct rtl9300_mdio_priv *priv;
+ struct otto_emdio_chan *chan = bus->priv;
+ struct otto_emdio_priv *priv;
struct regmap *regmap;
int port;
u32 val;
priv = chan->priv;
regmap = priv->regmap;
- port = rtl9300_mdio_phy_to_port(bus, phy_id);
+ port = otto_emdio_phy_to_port(bus, phy_id);
if (port < 0)
return port;
mutex_lock(&priv->lock);
- err = rtl9300_mdio_wait_ready(priv);
+ err = otto_emdio_wait_ready(priv);
if (err)
goto out_err;
if (err)
goto out_err;
- err = rtl9300_mdio_wait_ready(priv);
+ err = otto_emdio_wait_ready(priv);
if (err)
goto out_err;
return err;
}
-static int rtl9300_mdio_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,
+static int otto_emdio_9300_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,
int regnum, u16 value)
{
- struct rtl9300_mdio_chan *chan = bus->priv;
- struct rtl9300_mdio_priv *priv;
+ struct otto_emdio_chan *chan = bus->priv;
+ struct otto_emdio_priv *priv;
struct regmap *regmap;
int port;
u32 val;
priv = chan->priv;
regmap = priv->regmap;
- port = rtl9300_mdio_phy_to_port(bus, phy_id);
+ port = otto_emdio_phy_to_port(bus, phy_id);
if (port < 0)
return port;
mutex_lock(&priv->lock);
- err = rtl9300_mdio_wait_ready(priv);
+ err = otto_emdio_wait_ready(priv);
if (err)
goto out_err;
return err;
}
-static int rtl9300_mdiobus_init(struct rtl9300_mdio_priv *priv)
+static int otto_emdio_9300_mdiobus_init(struct otto_emdio_priv *priv)
{
u32 glb_ctrl_mask = 0, glb_ctrl_val = 0;
struct regmap *regmap = priv->regmap;
return 0;
}
-static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_priv *priv,
- struct fwnode_handle *node)
+static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,
+ struct fwnode_handle *node)
{
- struct rtl9300_mdio_chan *chan;
+ struct otto_emdio_chan *chan;
struct mii_bus *bus;
u32 mdio_bus;
int err;
bus->name = "Realtek Switch MDIO Bus";
if (priv->smi_bus_is_c45[mdio_bus]) {
- bus->read_c45 = rtl9300_mdio_read_c45;
- bus->write_c45 = rtl9300_mdio_write_c45;
+ bus->read_c45 = otto_emdio_9300_read_c45;
+ bus->write_c45 = otto_emdio_9300_write_c45;
} else {
- bus->read = rtl9300_mdio_read_c22;
- bus->write = rtl9300_mdio_write_c22;
+ bus->read = otto_emdio_9300_read_c22;
+ bus->write = otto_emdio_9300_write_c22;
}
bus->parent = dev;
chan = bus->priv;
* ethernet-ports node and build a mapping of the switch port to MDIO bus/addr
* based on the phy-handle.
*/
-static int rtl9300_mdiobus_map_ports(struct device *dev)
+static int otto_emdio_map_ports(struct device *dev)
{
- struct rtl9300_mdio_priv *priv = dev_get_drvdata(dev);
+ struct otto_emdio_priv *priv = dev_get_drvdata(dev);
struct device *parent = dev->parent;
int err;
return 0;
}
-static int rtl9300_mdiobus_probe(struct platform_device *pdev)
+static int otto_emdio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct rtl9300_mdio_priv *priv;
+ struct otto_emdio_priv *priv;
int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
platform_set_drvdata(pdev, priv);
- err = rtl9300_mdiobus_map_ports(dev);
+ err = otto_emdio_map_ports(dev);
if (err)
return err;
device_for_each_child_node_scoped(dev, child) {
- err = rtl9300_mdiobus_probe_one(dev, priv, child);
+ err = otto_emdio_probe_one(dev, priv, child);
if (err)
return err;
}
- err = rtl9300_mdiobus_init(priv);
+ err = otto_emdio_9300_mdiobus_init(priv);
if (err)
return dev_err_probe(dev, err, "failed to initialise MDIO bus controller\n");
return 0;
}
-static const struct of_device_id rtl9300_mdio_ids[] = {
+static const struct of_device_id otto_emdio_ids[] = {
{ .compatible = "realtek,rtl9301-mdio" },
{}
};
-MODULE_DEVICE_TABLE(of, rtl9300_mdio_ids);
+MODULE_DEVICE_TABLE(of, otto_emdio_ids);
-static struct platform_driver rtl9300_mdio_driver = {
- .probe = rtl9300_mdiobus_probe,
+static struct platform_driver otto_emdio_driver = {
+ .probe = otto_emdio_probe,
.driver = {
.name = "mdio-rtl9300",
- .of_match_table = rtl9300_mdio_ids,
+ .of_match_table = otto_emdio_ids,
},
};
-module_platform_driver(rtl9300_mdio_driver);
+module_platform_driver(otto_emdio_driver);
MODULE_DESCRIPTION("RTL9300 MDIO driver");
MODULE_LICENSE("GPL");