我们有一个embedded式板卡,以太网设备直接连接到交换机之间,而不需要phy。 为了使事情更复杂,以太网设备的mdio总线连接到交换机的mdio进行控制。
我已经设法使用固定的mdio / phy驱动程序来启用以太网,并通过将交换机的默认configuration匹配到固定的phy。
现在如何连接到mdio总线来更改开关设置? 由于以太网设备连接的phy由固定的phy填充,我现在如何将真正的mdio总线连接到系统,所以我可以configuration它。 有一个mdio总线似乎没有直接的用户空间接口。 我是否创build一个伪造的以太网设备,其唯一目的是访问mdio总线,或者我以某种方式将它连接到以太网设备,然后将有两个mdio总线连接?
PS:似乎物理audio总线驱动程序find了交换机,但我该如何与它交谈?
这个补丁允许我读取和写入系统中检测到的mdio设备中的所有寄存器。
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", ®num) != 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", ®num) != 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 };
它扩展了每个mdio设备可以包含32个寄存器地址的sysfs接口。 由于mdio设备不是物理的,所以他们没有遵循phy标准,所以我必须破解phy检测以允许所有设备出现:
--- 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);
希望这对别人有帮助。
您可以根据您的phy ID编写伪phy驱动程序。 您可以通过读取phy寄存器来获得phy ID。 这个驱动程序会给你处理交换机连接到的音频总线。 这是我的驱动程序,在我的情况下,i.MX6连接到marvell 88E6065开关。 然后我已经导出sysfs接口,我能够通过sysfs接口配置从用户空间切换。 希望这会帮助别人。
#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);