net: dsa: mv88e6xxx: Support multiple MDIO busses
authorAndrew Lunn <andrew@lunn.ch>
Tue, 24 Jan 2017 13:53:50 +0000 (14:53 +0100)
committerDavid S. Miller <davem@davemloft.net>
Tue, 24 Jan 2017 20:33:51 +0000 (15:33 -0500)
The mv88e6390 has multiple MDIO busses. Generalize the parsing of the
device tree to support multiple mdio nodes. The external mdio bus has
a compatible strings to indicate it is external.

Keep a linked list of busses, placing the external mdio bus at the
tail of the list. When within the driver an mdio bus is needed,
e.g. for EEE or SERDES, use the head of the list which should be the
internal bus.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/devicetree/bindings/net/dsa/marvell.txt
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h

index b3dd6b40e0de29a71cdd96627cca55a09215ea9d..64dbdd825981dcc5807f42832c435b13df8d1766 100644 (file)
@@ -26,8 +26,12 @@ Optional properties:
 - interrupt-controller : Indicates the switch is itself an interrupt
                          controller. This is used for the PHY interrupts.
 #interrupt-cells = <2> : Controller uses two cells, number and flag
-- mdio                 : container of PHY and devices on the switches MDIO
-                         bus
+- mdio                 : Container of PHY and devices on the switches MDIO
+                         bus.
+- mdio?                        : Container of PHYs and devices on the external MDIO
+                         bus. The node must contains a compatible string of
+                         "marvell,mv88e6xxx-mdio-external"
+
 Example:
 
        mdio {
@@ -53,3 +57,36 @@ Example:
                        };
                };
        };
+
+       mdio {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               interrupt-parent = <&gpio0>;
+               interrupts = <27 IRQ_TYPE_LEVEL_LOW>;
+               interrupt-controller;
+               #interrupt-cells = <2>;
+
+               switch0: switch@0 {
+                       compatible = "marvell,mv88e6390";
+                       reg = <0>;
+                      reset-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
+               };
+               mdio {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       switch1phy0: switch1phy0@0 {
+                               reg = <0>;
+                               interrupt-parent = <&switch0>;
+                               interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
+                       };
+               };
+
+               mdio1 {
+                       compatible = "marvell,mv88e6xxx-mdio-external";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       switch1phy9: switch1phy0@9 {
+                               reg = <9>;
+                       };
+               };
+       };
index b0fd1432f4f3a644fa9c0341daba4e92f7f03485..5668e778ed1de42a0e3678a8f051e0f80607e388 100644 (file)
@@ -236,16 +236,29 @@ static int mv88e6165_phy_write(struct mv88e6xxx_chip *chip,
        return mv88e6xxx_write(chip, addr, reg, val);
 }
 
+static struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip)
+{
+       struct mv88e6xxx_mdio_bus *mdio_bus;
+
+       mdio_bus = list_first_entry(&chip->mdios, struct mv88e6xxx_mdio_bus,
+                                   list);
+       if (!mdio_bus)
+               return NULL;
+
+       return mdio_bus->bus;
+}
+
 static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
                              int reg, u16 *val)
 {
        int addr = phy; /* PHY devices addresses start at 0x0 */
-       struct mii_bus *bus = chip->mdio_bus;
+       struct mii_bus *bus;
 
-       if (!chip->info->ops->phy_read)
+       bus = mv88e6xxx_default_mdio_bus(chip);
+       if (!bus)
                return -EOPNOTSUPP;
 
-       if (!bus)
+       if (!chip->info->ops->phy_read)
                return -EOPNOTSUPP;
 
        return chip->info->ops->phy_read(chip, bus, addr, reg, val);
@@ -255,12 +268,13 @@ static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
                               int reg, u16 val)
 {
        int addr = phy; /* PHY devices addresses start at 0x0 */
-       struct mii_bus *bus = chip->mdio_bus;
+       struct mii_bus *bus;
 
-       if (!chip->info->ops->phy_write)
+       bus = mv88e6xxx_default_mdio_bus(chip);
+       if (!bus)
                return -EOPNOTSUPP;
 
-       if (!bus)
+       if (!chip->info->ops->phy_write)
                return -EOPNOTSUPP;
 
        return chip->info->ops->phy_write(chip, bus, addr, reg, val);
@@ -2845,7 +2859,7 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
        int i;
 
        chip->ds = ds;
-       ds->slave_mii_bus = chip->mdio_bus;
+       ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip);
 
        mutex_lock(&chip->reg_lock);
 
@@ -2940,22 +2954,23 @@ static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
 }
 
 static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
-                                  struct device_node *np)
+                                  struct device_node *np,
+                                  bool external)
 {
        static int index;
        struct mv88e6xxx_mdio_bus *mdio_bus;
        struct mii_bus *bus;
        int err;
 
-       if (np)
-               chip->mdio_np = of_get_child_by_name(np, "mdio");
-
        bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus));
        if (!bus)
                return -ENOMEM;
 
        mdio_bus = bus->priv;
+       mdio_bus->bus = bus;
        mdio_bus->chip = chip;
+       INIT_LIST_HEAD(&mdio_bus->list);
+       mdio_bus->external = external;
 
        if (np) {
                bus->name = np->full_name;
@@ -2969,34 +2984,72 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
        bus->write = mv88e6xxx_mdio_write;
        bus->parent = chip->dev;
 
-       if (chip->mdio_np)
-               err = of_mdiobus_register(bus, chip->mdio_np);
+       if (np)
+               err = of_mdiobus_register(bus, np);
        else
                err = mdiobus_register(bus);
        if (err) {
                dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
-               goto out;
+               return err;
        }
-       chip->mdio_bus = bus;
+
+       if (external)
+               list_add_tail(&mdio_bus->list, &chip->mdios);
+       else
+               list_add(&mdio_bus->list, &chip->mdios);
 
        return 0;
+}
 
-out:
-       if (chip->mdio_np)
-               of_node_put(chip->mdio_np);
+static const struct of_device_id mv88e6xxx_mdio_external_match[] = {
+       { .compatible = "marvell,mv88e6xxx-mdio-external",
+         .data = (void *)true },
+       { },
+};
 
-       return err;
+static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip,
+                                   struct device_node *np)
+{
+       const struct of_device_id *match;
+       struct device_node *child;
+       int err;
+
+       /* Always register one mdio bus for the internal/default mdio
+        * bus. This maybe represented in the device tree, but is
+        * optional.
+        */
+       child = of_get_child_by_name(np, "mdio");
+       err = mv88e6xxx_mdio_register(chip, child, false);
+       if (err)
+               return err;
+
+       /* Walk the device tree, and see if there are any other nodes
+        * which say they are compatible with the external mdio
+        * bus.
+        */
+       for_each_available_child_of_node(np, child) {
+               match = of_match_node(mv88e6xxx_mdio_external_match, child);
+               if (match) {
+                       err = mv88e6xxx_mdio_register(chip, child, true);
+                       if (err)
+                               return err;
+               }
+       }
+
+       return 0;
 }
 
-static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
+static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
 
 {
-       struct mii_bus *bus = chip->mdio_bus;
+       struct mv88e6xxx_mdio_bus *mdio_bus;
+       struct mii_bus *bus;
 
-       mdiobus_unregister(bus);
+       list_for_each_entry(mdio_bus, &chip->mdios, list) {
+               bus = mdio_bus->bus;
 
-       if (chip->mdio_np)
-               of_node_put(chip->mdio_np);
+               mdiobus_unregister(bus);
+       }
 }
 
 static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
@@ -4123,6 +4176,7 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
        chip->dev = dev;
 
        mutex_init(&chip->reg_lock);
+       INIT_LIST_HEAD(&chip->mdios);
 
        return chip;
 }
@@ -4197,7 +4251,7 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
 
        mv88e6xxx_phy_init(chip);
 
-       err = mv88e6xxx_mdio_register(chip, NULL);
+       err = mv88e6xxx_mdios_register(chip, NULL);
        if (err)
                goto free;
 
@@ -4398,7 +4452,7 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
                }
        }
 
-       err = mv88e6xxx_mdio_register(chip, np);
+       err = mv88e6xxx_mdios_register(chip, np);
        if (err)
                goto out_g2_irq;
 
@@ -4409,7 +4463,7 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
        return 0;
 
 out_mdio:
-       mv88e6xxx_mdio_unregister(chip);
+       mv88e6xxx_mdios_unregister(chip);
 out_g2_irq:
        if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0)
                mv88e6xxx_g2_irq_free(chip);
@@ -4430,7 +4484,7 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
 
        mv88e6xxx_phy_destroy(chip);
        mv88e6xxx_unregister_switch(chip);
-       mv88e6xxx_mdio_unregister(chip);
+       mv88e6xxx_mdios_unregister(chip);
 
        if (chip->irq > 0) {
                if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
index 6f7ddb59480951784af6fd9d66a9e00f44201b95..7d24add45e74cb26945fb247c8b8b4f5cee717de 100644 (file)
@@ -730,11 +730,8 @@ struct mv88e6xxx_chip {
        /* set to size of eeprom if supported by the switch */
        int             eeprom_len;
 
-       /* Device node for the MDIO bus */
-       struct device_node *mdio_np;
-
-       /* And the MDIO bus itself */
-       struct mii_bus *mdio_bus;
+       /* List of mdio busses */
+       struct list_head mdios;
 
        /* There can be two interrupt controllers, which are chained
         * off a GPIO as interrupt source
@@ -751,7 +748,10 @@ struct mv88e6xxx_bus_ops {
 };
 
 struct mv88e6xxx_mdio_bus {
+       struct mii_bus *bus;
        struct mv88e6xxx_chip *chip;
+       struct list_head list;
+       bool external;
 };
 
 struct mv88e6xxx_ops {