How to connect an Ethernet device directly to a switch in Linux?

We have a built-in board in which the ethernet device is directly connected to the switch without using phy between them. To complicate matters, the mdio-bus Ethernet device connects to the mdio switch for management.

I managed to use the mdio / phy fixed driver to enable ethernet, and this works by comparing the default configuration for the switch with fixed phy's.

How to connect to the mdio bus to change the switch settings? Since the phy connected to the Ethernet device is filled with fixed phy, how do I now connect the real mdio bus to the system so that I can configure it. There seems to be no direct user interface to the mdio bus. Am I creating a fake Ethernet device whose sole purpose is to access the mdio bus, or am I somehow connecting it to an ethernet device that will be connected to two mdio buses?

PS: It seems that the physical mdio bus driver is finding a switch, but how can I talk to it?

+7
linux linux-kernel embedded-linux linux-device-driver ethernet
source share
2 answers

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

diff --git a/drivers/net/phy/mdio_bus.cb/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 existing 32-register sysfs interface, which can contain all mdio devices. Since the mdio devices were not physical, they did not follow the phy standard, so I had to hack phy detection so that all devices displayed:

 --- 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 helps someone else.

+6
source share

You can write pseudo phy driver based on your phy id. You can get the phy id by reading phy registers. This driver will provide you with the handle of the mdio bus that the switch is connected to. This is my driver. In my case, i.MX6 was connected to a marveell 88E6065 switch. Then I exported the sysfs interface, and I was able to configure the transition from user space through the sysfs interface. Hope this helps someone.

 #include <linux/kernel.h> #include <linux/module.h> #include <linux/phy.h> #include <linux/asai_iotg.h> MODULE_DESCRIPTION("psuedo phy driver"); MODULE_LICENSE("GPLv2"); MODULE_AUTHOR("jags"); static int phy_id = 0; static u32 reg_num = 0; static u16 data = 0; static struct mii_bus *mvl_bus = NULL; static struct kobject *kobj_switch; /* Supported Device ID Tables */ static struct mdio_device_id marvell_88E6065_id[] = { {0x01410c89, 0xfffffc00}, {} }; MODULE_DEVICE_TABLE(mdio, marvell_88E6065_id); static ssize_t switch_conf_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%x\n", data); } static ssize_t switch_conf_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size) { u32 value; sscanf(buffer, "%x", &value); if (value & 0xFF000000) { phy_id = (value & 0xFF000000) >> 24; reg_num = (value & 0x00FF0000) >> 16; data = (value & 0x0000FFFF); mdiobus_write(mvl_bus, phy_id, reg_num, data); } else { phy_id = (value & 0xFF00) >> 8; reg_num = (value & 0x00FF); data = mdiobus_read(mvl_bus, phy_id, reg_num); } return size; } static struct kobj_attribute switch_conf_attribute = __ATTR(switch_conf, 0666, switch_conf_show, switch_conf_store); static struct attribute *attrs_switch[] = { &switch_conf_attribute.attr, NULL, }; static struct attribute_group attr_group_switch = { .attrs = attrs_switch, }; /* Initialize the Marvell 88E6065 switch in managed mode */ static int marvell_88E6065_probe(struct phy_device *phydev) { int err = 0; mvl_bus = phydev->bus; if(mvl_bus == NULL) return -1; pr_debug("Detected Marvell switch !!\n"); pr_debug("switch id is %04x%04x\n", mdiobus_read(mvl_bus, 0x8, 0x2), mdiobus_read(mvl_bus, 0x08, 0x03)); if(err) { printk(KERN_INFO "mdio write failed for marvell pseudo phy\n"); return -1; } return 0; } /* PHY Driver */ static struct phy_driver marvell_88E6065_driver = { .phy_id = 0x01410c89, .phy_id_mask = 0xffffffff, .name = "Marvell 88E6065", .features = (PHY_BASIC_FEATURES), .flags = PHY_HAS_INTERRUPT, .probe = marvell_88E6065_probe, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .driver = { .owner = THIS_MODULE }, }; /*Switch initialize function */ /* Init exit */ static int __init mdio_88E6065_init() { int ret = 0; kobj_switch = kobject_create_and_add("switch", &asai_iotg_kset->kobj); if (!kobj_switch) return -ENOMEM; ret = sysfs_create_group(kobj_switch, &attr_group_switch); if (ret) { kobject_put(kobj_switch); return ret; } return phy_driver_register(&marvell_88E6065_driver); } static void __exit mdio_88E6065_exit() { kobject_put(kobj_switch); phy_driver_unregister(&marvell_88E6065_driver); } module_init(mdio_88E6065_init); module_exit(mdio_88E6065_exit); 
0
source share

All Articles