Not all MACs are connected to PHYs. They can for example be connected to a switch. Using the fixed PHY properties with the MAC is possible, but requires support in the MAC. It is however simpler to make use of the phy-handle property to point to a PHY. To achieve this, the PHY must be in the device tree.
Allow virtual MDIO busses to be represented in device tree, which contains virtual fixed-phys. Signed-off-by: Andrew Lunn <and...@lunn.ch> --- .../devicetree/bindings/net/fixed-link.txt | 39 +++++++ drivers/net/phy/fixed_phy.c | 122 ++++++++++++++++++++- 2 files changed, 157 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/net/fixed-link.txt b/Documentation/devicetree/bindings/net/fixed-link.txt index ec5d889fe3d8..2a22b92007fa 100644 --- a/Documentation/devicetree/bindings/net/fixed-link.txt +++ b/Documentation/devicetree/bindings/net/fixed-link.txt @@ -52,3 +52,42 @@ ethernet@1 { }; ... }; + +Fixed link PHYs on an MDIO bus +------------------------------ + +An alternative to using the fixed link properties in the MAC is to +define an MDIO bus with a number of fixed link phys on it. + +Required properties: +- compatible = ""linux,mdio-fixed-phy"; +- #address-cells = <1>; +- #size-cells = <0>; + +Child nodes represent PHYs on this mdio bus. Standard properties for +fixed links, 'speed', 'full-duplex', 'pause', 'asym-pause', +'link-gpios', as defined above are used. Additionally a 'reg' property +is required for the address of the PHY on the bus. This should be of +value 0 to 31. + +Example +------- + +mdio { + compatible = "linux,mdio-fixed-phy"; + #address-cells = <1>; + #size-cells = <0>; + + phy0: phy@0 { + reg = <0>; + speed = <1000>; + pause; + link-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>; + }; + + phy1: phy@8 { + reg = <8>; + speed = <100>; + full-duplex; + }; +}; diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index fc07a8866020..4df2fcdcee68 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -22,6 +22,8 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_mdio.h> #include <linux/gpio.h> #define MII_REGS_NUM 29 @@ -241,12 +243,12 @@ int fixed_phy_update_state(struct phy_device *phydev, } EXPORT_SYMBOL(fixed_phy_update_state); -int fixed_phy_add(unsigned int irq, int phy_addr, - struct fixed_phy_status *status, - int link_gpio) +int fixed_phy_add_fmb(struct fixed_mdio_bus *fmb, + unsigned int irq, int phy_addr, + struct fixed_phy_status *status, + int link_gpio) { int ret; - struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp; fp = kzalloc(sizeof(*fp), GFP_KERNEL); @@ -283,6 +285,14 @@ err_regs: kfree(fp); return ret; } + +int fixed_phy_add(unsigned int irq, int phy_addr, + struct fixed_phy_status *status, + int link_gpio) +{ + return fixed_phy_add_fmb(&platform_fmb, irq, phy_addr, status, + link_gpio); +} EXPORT_SYMBOL_GPL(fixed_phy_add); static void fixed_phy_del(int phy_addr) @@ -378,6 +388,103 @@ void fixed_phy_unregister(struct phy_device *phy) } EXPORT_SYMBOL_GPL(fixed_phy_unregister); +static int mdio_fixed_phy_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fixed_phy_status status; + struct fixed_mdio_bus *fmb; + struct device_node *child; + struct mii_bus *bus; + int phy_addr, irq; + int link_gpio; + + int ret; + + if (!np) + return -EINVAL; + + bus = mdiobus_alloc_size(sizeof(*fmb)); + if (!bus) + return -ENOMEM; + + if (strlen(pdev->name) >= MII_BUS_ID_SIZE) + return -EINVAL; + + strncpy(bus->id, pdev->name, MII_BUS_ID_SIZE - 1); + bus->name = bus->id; + bus->parent = &pdev->dev; + bus->read = fixed_mdio_read; + bus->write = fixed_mdio_write; + fmb = bus->priv; + fmb->mii_bus = bus; + INIT_LIST_HEAD(&fmb->phys); + + for_each_available_child_of_node(np, child) { + phy_addr = of_mdio_parse_addr(&pdev->dev, child); + if (phy_addr < 0) { + ret = phy_addr; + goto err_out_free_phys; + } + irq = irq_of_parse_and_map(child, 0); + + ret = of_phy_parse_fixed_link(child, &status, &link_gpio); + if (ret < 0) + goto err_out_free_phys; + + ret = fixed_phy_add_fmb(fmb, irq, phy_addr, + &status, link_gpio); + if (ret < 0) + goto err_out_free_phys; + } + + ret = of_mdiobus_register(bus, np); + if (ret < 0) + goto err_out_free_mdiobus; + + platform_set_drvdata(pdev, bus); + + return 0; + +err_out_free_phys: + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) + fixed_phy_del(phy_addr); + +err_out_free_mdiobus: + mdiobus_free(bus); + return ret; +} + +static int mdio_fixed_phy_remove(struct platform_device *pdev) +{ + struct mii_bus *bus = platform_get_drvdata(pdev); + int phy_addr; + + mdiobus_unregister(bus); + + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) + fixed_phy_del(phy_addr); + + mdiobus_free(bus); + + return 0; +} + +static const struct of_device_id mdio_fixed_phy_dt_ids[] = { + { .compatible = "linux,mdio-fixed-phy" }, + { } +}; + +MODULE_DEVICE_TABLE(of, mdio_fixed_phy_dt_ids); + +static struct platform_driver mdio_fixed_phy_driver = { + .probe = mdio_fixed_phy_probe, + .remove = mdio_fixed_phy_remove, + .driver = { + .name = "mdio-fixed-phy", + .of_match_table = mdio_fixed_phy_dt_ids, + }, +}; + static int __init fixed_mdio_bus_init(void) { struct fixed_mdio_bus *fmb = &platform_fmb; @@ -406,8 +513,14 @@ static int __init fixed_mdio_bus_init(void) if (ret) goto err_mdiobus_alloc; + ret = platform_driver_register(&mdio_fixed_phy_driver); + if (ret) + goto err_mdiobus_register; + return 0; +err_mdiobus_register: + mdiobus_unregister(fmb->mii_bus); err_mdiobus_alloc: mdiobus_free(fmb->mii_bus); err_mdiobus_reg: @@ -422,6 +535,7 @@ static void __exit fixed_mdio_bus_exit(void) struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp, *tmp; + platform_driver_unregister(&mdio_fixed_phy_driver); mdiobus_unregister(fmb->mii_bus); mdiobus_free(fmb->mii_bus); platform_device_unregister(pdev); -- 2.7.0