summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/mdio/mdio-realtek-rtl9300.c262
1 files changed, 168 insertions, 94 deletions
diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c
index 8d5fb014ca06..65c59b8457d4 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -1,11 +1,40 @@
// 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>
@@ -22,11 +51,14 @@
#include <linux/property.h>
#include <linux/regmap.h>
+#define RTL9300_NUM_BUSES 4
+#define RTL9300_NUM_PAGES 4096
+#define RTL9300_NUM_PORTS 28
#define SMI_GLB_CTRL 0xca00
#define GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf))
#define SMI_PORT0_15_POLLING_SEL 0xca08
-#define SMI_ACCESS_PHY_CTRL_0 0xcb70
-#define SMI_ACCESS_PHY_CTRL_1 0xcb74
+#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)
@@ -36,10 +68,10 @@
#define PHY_CTRL_TYPE_C22 0
#define PHY_CTRL_CMD BIT(0)
#define PHY_CTRL_FAIL BIT(25)
-#define SMI_ACCESS_PHY_CTRL_2 0xcb78
+#define RTL9300_SMI_ACCESS_PHY_CTRL_2 0xcb78
#define PHY_CTRL_INDATA GENMASK(31, 16)
#define PHY_CTRL_DATA GENMASK(15, 0)
-#define SMI_ACCESS_PHY_CTRL_3 0xcb7c
+#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 SMI_PORT0_5_ADDR_CTRL 0xcb80
@@ -47,8 +79,29 @@
#define MAX_PORTS 28
#define MAX_SMI_BUSSES 4
#define MAX_SMI_ADDR 0x1f
+#define RAW_PAGE(priv) ((priv)->info->num_pages - 1)
+
+
+struct otto_emdio_cmd_regs {
+ u32 c22_data;
+ u32 c45_data;
+ u32 io_data;
+ u32 port_mask_low;
+};
-struct rtl9300_mdio_priv {
+struct otto_emdio_info {
+ struct otto_emdio_cmd_regs cmd_regs;
+ u8 num_buses;
+ u8 num_ports;
+ u16 num_pages;
+ int (*read_c22)(struct mii_bus *bus, int phy_id, int regnum);
+ int (*read_c45)(struct mii_bus *bus, int phy_id, int dev_addr, int regnum);
+ int (*write_c22)(struct mii_bus *bus, int phy_id, int regnum, u16 value);
+ int (*write_c45)(struct mii_bus *bus, int phy_id, int dev_addr, int regnum, u16 value);
+};
+
+struct otto_emdio_priv {
+ const struct otto_emdio_info *info;
struct regmap *regmap;
struct mutex lock; /* protect HW access */
DECLARE_BITMAP(valid_ports, MAX_PORTS);
@@ -58,20 +111,20 @@ struct rtl9300_mdio_priv {
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;
- for_each_set_bit(i, priv->valid_ports, MAX_PORTS)
+ for_each_set_bit(i, priv->valid_ports, priv->info->num_ports)
if (priv->smi_bus[i] == chan->mdio_bus &&
priv->smi_addr[i] == phy_id)
return i;
@@ -79,55 +132,57 @@ static int rtl9300_mdio_phy_to_port(struct mii_bus *bus, int phy_id)
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;
+ u32 cmd_reg, val;
lockdep_assert_held(&priv->lock);
+ cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
- return regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1,
- val, !(val & PHY_CTRL_CMD), 10, 1000);
+ return regmap_read_poll_timeout(regmap, cmd_reg, 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;
+ u32 io_reg, cmd_reg, val;
struct regmap *regmap;
int port;
- u32 val;
int err;
priv = chan->priv;
regmap = priv->regmap;
+ io_reg = priv->info->cmd_regs.io_data;
+ cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
- 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;
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, FIELD_PREP(PHY_CTRL_INDATA, port));
+ err = regmap_write(regmap, io_reg, FIELD_PREP(PHY_CTRL_INDATA, port));
if (err)
goto out_err;
val = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
- FIELD_PREP(PHY_CTRL_MAIN_PAGE, 0xfff) |
+ FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)) |
PHY_CTRL_READ | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD;
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, val);
+ err = regmap_write(regmap, cmd_reg, val);
if (err)
goto out_err;
- err = rtl9300_mdio_wait_ready(priv);
+ err = otto_emdio_wait_ready(priv);
if (err)
goto out_err;
- err = regmap_read(regmap, SMI_ACCESS_PHY_CTRL_2, &val);
+ err = regmap_read(regmap, io_reg, &val);
if (err)
goto out_err;
@@ -139,45 +194,46 @@ 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;
+ u32 io_reg, cmd_reg, val;
struct regmap *regmap;
int port;
- u32 val;
int err;
priv = chan->priv;
regmap = priv->regmap;
+ io_reg = priv->info->cmd_regs.io_data;
+ cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
- 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;
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_0, BIT(port));
+ err = regmap_write(regmap, priv->info->cmd_regs.port_mask_low, BIT(port));
if (err)
goto out_err;
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, FIELD_PREP(PHY_CTRL_INDATA, value));
+ err = regmap_write(regmap, io_reg, FIELD_PREP(PHY_CTRL_INDATA, value));
if (err)
goto out_err;
val = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
- FIELD_PREP(PHY_CTRL_MAIN_PAGE, 0xfff) |
+ FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)) |
PHY_CTRL_WRITE | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD;
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, val);
+ err = regmap_write(regmap, cmd_reg, val);
if (err)
goto out_err;
- err = regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1,
- val, !(val & PHY_CTRL_CMD), 10, 100);
+ err = regmap_read_poll_timeout(regmap, cmd_reg, val, !(val & PHY_CTRL_CMD), 10, 100);
if (err)
goto out_err;
@@ -194,48 +250,49 @@ 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;
+ u32 io_reg, cmd_reg, val;
struct regmap *regmap;
int port;
- u32 val;
int err;
priv = chan->priv;
regmap = priv->regmap;
+ io_reg = priv->info->cmd_regs.io_data;
+ cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
- 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;
val = FIELD_PREP(PHY_CTRL_INDATA, port);
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, val);
+ err = regmap_write(regmap, io_reg, val);
if (err)
goto out_err;
val = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
FIELD_PREP(PHY_CTRL_MMD_REG, regnum);
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_3, val);
+ err = regmap_write(regmap, priv->info->cmd_regs.c45_data, val);
if (err)
goto out_err;
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1,
- PHY_CTRL_READ | PHY_CTRL_TYPE_C45 | PHY_CTRL_CMD);
+ err = regmap_write(regmap, cmd_reg, PHY_CTRL_READ | PHY_CTRL_TYPE_C45 | PHY_CTRL_CMD);
if (err)
goto out_err;
- err = rtl9300_mdio_wait_ready(priv);
+ err = otto_emdio_wait_ready(priv);
if (err)
goto out_err;
- err = regmap_read(regmap, SMI_ACCESS_PHY_CTRL_2, &val);
+ err = regmap_read(regmap, io_reg, &val);
if (err)
goto out_err;
@@ -247,50 +304,50 @@ 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;
+ u32 io_reg, cmd_reg, val;
struct regmap *regmap;
int port;
- u32 val;
int err;
priv = chan->priv;
regmap = priv->regmap;
+ io_reg = priv->info->cmd_regs.io_data;
+ cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
- 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;
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_0, BIT(port));
+ err = regmap_write(regmap, priv->info->cmd_regs.port_mask_low, BIT(port));
if (err)
goto out_err;
val = FIELD_PREP(PHY_CTRL_INDATA, value);
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, val);
+ err = regmap_write(regmap, io_reg, val);
if (err)
goto out_err;
val = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
FIELD_PREP(PHY_CTRL_MMD_REG, regnum);
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_3, val);
+ err = regmap_write(regmap, priv->info->cmd_regs.c45_data, val);
if (err)
goto out_err;
- err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1,
- PHY_CTRL_TYPE_C45 | PHY_CTRL_WRITE | PHY_CTRL_CMD);
+ err = regmap_write(regmap, cmd_reg, PHY_CTRL_TYPE_C45 | PHY_CTRL_WRITE | PHY_CTRL_CMD);
if (err)
goto out_err;
- err = regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1,
- val, !(val & PHY_CTRL_CMD), 10, 100);
+ err = regmap_read_poll_timeout(regmap, cmd_reg, val, !(val & PHY_CTRL_CMD), 10, 100);
if (err)
goto out_err;
@@ -307,7 +364,7 @@ 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;
@@ -316,7 +373,7 @@ static int rtl9300_mdiobus_init(struct rtl9300_mdio_priv *priv)
int i, err;
/* Associate the port with the SMI interface and PHY */
- for_each_set_bit(i, priv->valid_ports, MAX_PORTS) {
+ for_each_set_bit(i, priv->valid_ports, priv->info->num_ports) {
int pos;
pos = (i % 6) * 5;
@@ -328,7 +385,7 @@ static int rtl9300_mdiobus_init(struct rtl9300_mdio_priv *priv)
/* Put the interfaces into C45 mode if required */
glb_ctrl_mask = GENMASK(19, 16);
- for (i = 0; i < MAX_SMI_BUSSES; i++)
+ for (i = 0; i < priv->info->num_buses; i++)
if (priv->smi_bus_is_c45[i])
glb_ctrl_val |= GLB_CTRL_INTF_SEL(i);
@@ -350,10 +407,10 @@ static int rtl9300_mdiobus_init(struct rtl9300_mdio_priv *priv)
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;
@@ -380,11 +437,11 @@ static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_pri
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 = priv->info->read_c45;
+ bus->write_c45 = priv->info->write_c45;
} else {
- bus->read = rtl9300_mdio_read_c22;
- bus->write = rtl9300_mdio_write_c22;
+ bus->read = priv->info->read_c22;
+ bus->write = priv->info->write_c22;
}
bus->parent = dev;
chan = bus->priv;
@@ -404,9 +461,9 @@ static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_pri
* 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;
@@ -437,7 +494,7 @@ static int rtl9300_mdiobus_map_ports(struct device *dev)
if (err)
return err;
- if (pn >= MAX_PORTS)
+ if (pn >= priv->info->num_ports)
return dev_err_probe(dev, -EINVAL, "illegal port number %d\n", pn);
if (test_bit(pn, priv->valid_ports))
@@ -447,7 +504,7 @@ static int rtl9300_mdiobus_map_ports(struct device *dev)
if (err)
return err;
- if (bus >= MAX_SMI_BUSSES)
+ if (bus >= priv->info->num_buses)
return dev_err_probe(dev, -EINVAL, "illegal smi bus number %d\n", bus);
err = of_property_read_u32(phy_dn, "reg", &addr);
@@ -462,10 +519,10 @@ static int rtl9300_mdiobus_map_ports(struct device *dev)
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);
@@ -476,44 +533,61 @@ static int rtl9300_mdiobus_probe(struct platform_device *pdev)
if (err)
return err;
+ priv->info = device_get_match_data(dev);
priv->regmap = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
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[] = {
- { .compatible = "realtek,rtl9301-mdio" },
+static const struct otto_emdio_info otto_emdio_9300_info = {
+ .cmd_regs = {
+ .c22_data = RTL9300_SMI_ACCESS_PHY_CTRL_1,
+ .c45_data = RTL9300_SMI_ACCESS_PHY_CTRL_3,
+ .io_data = RTL9300_SMI_ACCESS_PHY_CTRL_2,
+ .port_mask_low = RTL9300_SMI_ACCESS_PHY_CTRL_0,
+ },
+ .num_buses = RTL9300_NUM_BUSES,
+ .num_ports = RTL9300_NUM_PORTS,
+ .num_pages = RTL9300_NUM_PAGES,
+ .read_c22 = otto_emdio_9300_read_c22,
+ .read_c45 = otto_emdio_9300_read_c45,
+ .write_c22 = otto_emdio_9300_write_c22,
+ .write_c45 = otto_emdio_9300_write_c45,
+};
+
+static const struct of_device_id otto_emdio_ids[] = {
+ { .compatible = "realtek,rtl9301-mdio", .data = &otto_emdio_9300_info },
{}
};
-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");