How to connect an ethernet device directly to a switch in linux?

slobobaby picture slobobaby · Mar 5, 2014 · Viewed 7.2k times · Source

We have an embedded board where the ethernet device is directly connected to a switch without a phy in between. To make things more complicated the ethernet device's mdio bus is connected to the switch's mdio for control.

I have managed to use the fixed mdio/phy driver to enable ethernet and that works by matching the switch's default configuration to the fixed phy's.

How do I now connect to the mdio bus to change the switch settings? Since the ethernet device's attached phy is filled by the fixed phy how do I now attach the real mdio bus to the system so I can configure it. There seems to be no direct userspace interface to an mdio bus. Do I create a fake ethernet device whose only purpose is to access the mdio bus or do I somehow attach it to the ethernet device which will then have two mdio busses attached?

PS: It seems like the physical mdio bus driver finds the switch but how do I talk to it?

Answer

slobobaby picture slobobaby · Mar 12, 2014

This patch allows me to read and write all the registers in the mdio devices detected in a system.

diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index dc92097..668150e 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -439,8 +439,85 @@ phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
    return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
 }

+static ssize_t
+mdio_reg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+   struct phy_device *phydev = to_phy_device(dev);
+   struct mii_bus* bus = phydev->bus;
+   int regnum;
+   int val;
+
+   if (sscanf(attr->attr.name, "%d", &regnum) != 1)
+       return -EINVAL;
+
+   val = mdiobus_read(bus, phydev->addr, regnum);
+   if (val < 0)
+       return -EIO;
+
+   return sprintf(buf, "0x%.4x\n", val);
+}
+
+static ssize_t
+mdio_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+   struct phy_device *phydev = to_phy_device(dev);
+   struct mii_bus* bus = phydev->bus;
+   int regnum;
+   int val;
+   int err;
+
+   if (sscanf(attr->attr.name, "%d", &regnum) != 1)
+       return -EINVAL;
+
+   if (sscanf(buf, "%d", &val) != 1)
+       return -EINVAL;
+
+   if (val < 0 || val > 0xffff)
+       return -EINVAL;
+
+   err = mdiobus_write(bus, phydev->addr, regnum, val);
+   if (err < 0)
+       return -EIO;
+
+   return size;
+}
+
+#define MDIO_REG(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), mdio_reg_show, mdio_reg_store)
+
 static struct device_attribute mdio_dev_attrs[] = {
    __ATTR_RO(phy_id),
+   MDIO_REG(0),
+   MDIO_REG(1),
+   MDIO_REG(2),
+   MDIO_REG(3),
+   MDIO_REG(4),
+   MDIO_REG(5),
+   MDIO_REG(6),
+   MDIO_REG(7),
+   MDIO_REG(8),
+   MDIO_REG(9),
+   MDIO_REG(10),
+   MDIO_REG(11),
+   MDIO_REG(12),
+   MDIO_REG(13),
+   MDIO_REG(14),
+   MDIO_REG(15),
+   MDIO_REG(16),
+   MDIO_REG(17),
+   MDIO_REG(18),
+   MDIO_REG(19),
+   MDIO_REG(20),
+   MDIO_REG(21),
+   MDIO_REG(22),
+   MDIO_REG(23),
+   MDIO_REG(24),
+   MDIO_REG(25),
+   MDIO_REG(26),
+   MDIO_REG(27),
+   MDIO_REG(28),
+   MDIO_REG(29),
+   MDIO_REG(30),
+   MDIO_REG(31),
    __ATTR_NULL
 };

It extends the sysfs interface already present with the 32 register addresses each mdio device can contain. Since the mdio devices were not phys they didn't follow the phy standard so I had to hack the phy detection to allow all of the devices to appear:

--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -339,9 +339,12 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
        if (r)
                return ERR_PTR(r);

+       /* BRM: this is patently not the case for our marvell switch */
+#if 0
        /* If the phy_id is mostly Fs, there is no device there */
        if ((phy_id & 0x1fffffff) == 0x1fffffff)
                return NULL;
+#endif


    dev = phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);

Hope this is helpful for someone else.