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

Reply via email to