net: dsa: Provide a slave MII bus if needed
[deliverable/linux.git] / net / dsa / dsa.c
index c28c47463b7edfc58b08e2d059e0e69b66a3fff7..ce3b942dce76f6db4078ca1e556819fff2e86a60 100644 (file)
 
 char dsa_driver_version[] = "0.1";
 
+static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
+                                           struct net_device *dev)
+{
+       /* Just return the original SKB */
+       return skb;
+}
+
+static const struct dsa_device_ops none_ops = {
+       .xmit   = dsa_slave_notag_xmit,
+       .rcv    = NULL,
+};
+
+const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
+#ifdef CONFIG_NET_DSA_TAG_DSA
+       [DSA_TAG_PROTO_DSA] = &dsa_netdev_ops,
+#endif
+#ifdef CONFIG_NET_DSA_TAG_EDSA
+       [DSA_TAG_PROTO_EDSA] = &edsa_netdev_ops,
+#endif
+#ifdef CONFIG_NET_DSA_TAG_TRAILER
+       [DSA_TAG_PROTO_TRAILER] = &trailer_netdev_ops,
+#endif
+#ifdef CONFIG_NET_DSA_TAG_BRCM
+       [DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops,
+#endif
+       [DSA_TAG_PROTO_NONE] = &none_ops,
+};
 
 /* switch driver registration ***********************************************/
 static DEFINE_MUTEX(dsa_switch_drivers_mutex);
@@ -51,11 +78,12 @@ void unregister_switch_driver(struct dsa_switch_driver *drv)
 EXPORT_SYMBOL_GPL(unregister_switch_driver);
 
 static struct dsa_switch_driver *
-dsa_switch_probe(struct device *host_dev, int sw_addr, char **_name)
+dsa_switch_probe(struct device *parent, struct device *host_dev, int sw_addr,
+                const char **_name, void **priv)
 {
        struct dsa_switch_driver *ret;
        struct list_head *list;
-       char *name;
+       const char *name;
 
        ret = NULL;
        name = NULL;
@@ -66,7 +94,7 @@ dsa_switch_probe(struct device *host_dev, int sw_addr, char **_name)
 
                drv = list_entry(list, struct dsa_switch_driver, list);
 
-               name = drv->probe(host_dev, sw_addr);
+               name = drv->probe(parent, host_dev, sw_addr, priv);
                if (name != NULL) {
                        ret = drv;
                        break;
@@ -179,46 +207,70 @@ __ATTRIBUTE_GROUPS(dsa_hwmon);
 #endif /* CONFIG_NET_DSA_HWMON */
 
 /* basic switch operations **************************************************/
-static int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct net_device *master)
+int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev,
+                     struct device_node *port_dn, int port)
 {
-       struct dsa_chip_data *cd = ds->pd;
-       struct device_node *port_dn;
        struct phy_device *phydev;
-       int ret, port, mode;
+       int ret, mode;
+
+       if (of_phy_is_fixed_link(port_dn)) {
+               ret = of_phy_register_fixed_link(port_dn);
+               if (ret) {
+                       dev_err(dev, "failed to register fixed PHY\n");
+                       return ret;
+               }
+               phydev = of_phy_find_device(port_dn);
+
+               mode = of_get_phy_mode(port_dn);
+               if (mode < 0)
+                       mode = PHY_INTERFACE_MODE_NA;
+               phydev->interface = mode;
+
+               genphy_config_init(phydev);
+               genphy_read_status(phydev);
+               if (ds->drv->adjust_link)
+                       ds->drv->adjust_link(ds, port, phydev);
+       }
+
+       return 0;
+}
+
+static int dsa_cpu_dsa_setups(struct dsa_switch *ds, struct device *dev)
+{
+       struct device_node *port_dn;
+       int ret, port;
 
        for (port = 0; port < DSA_MAX_PORTS; port++) {
                if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)))
                        continue;
 
-               port_dn = cd->port_dn[port];
-               if (of_phy_is_fixed_link(port_dn)) {
-                       ret = of_phy_register_fixed_link(port_dn);
-                       if (ret) {
-                               netdev_err(master,
-                                          "failed to register fixed PHY\n");
-                               return ret;
-                       }
-                       phydev = of_phy_find_device(port_dn);
-
-                       mode = of_get_phy_mode(port_dn);
-                       if (mode < 0)
-                               mode = PHY_INTERFACE_MODE_NA;
-                       phydev->interface = mode;
-
-                       genphy_config_init(phydev);
-                       genphy_read_status(phydev);
-                       if (ds->drv->adjust_link)
-                               ds->drv->adjust_link(ds, port, phydev);
-               }
+               port_dn = ds->ports[port].dn;
+               ret = dsa_cpu_dsa_setup(ds, dev, port_dn, port);
+               if (ret)
+                       return ret;
        }
        return 0;
 }
 
+const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol)
+{
+       const struct dsa_device_ops *ops;
+
+       if (tag_protocol >= DSA_TAG_LAST)
+               return ERR_PTR(-EINVAL);
+       ops = dsa_device_ops[tag_protocol];
+
+       if (!ops)
+               return ERR_PTR(-ENOPROTOOPT);
+
+       return ops;
+}
+
 static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
 {
        struct dsa_switch_driver *drv = ds->drv;
        struct dsa_switch_tree *dst = ds->dst;
-       struct dsa_chip_data *pd = ds->pd;
+       struct dsa_chip_data *cd = ds->cd;
        bool valid_name_found = false;
        int index = ds->index;
        int i, ret;
@@ -229,7 +281,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
        for (i = 0; i < DSA_MAX_PORTS; i++) {
                char *name;
 
-               name = pd->port_names[i];
+               name = cd->port_names[i];
                if (name == NULL)
                        continue;
 
@@ -242,10 +294,11 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
                        }
                        dst->cpu_switch = index;
                        dst->cpu_port = i;
+                       ds->cpu_port_mask |= 1 << i;
                } else if (!strcmp(name, "dsa")) {
                        ds->dsa_port_mask |= 1 << i;
                } else {
-                       ds->phys_port_mask |= 1 << i;
+                       ds->enabled_port_mask |= 1 << i;
                }
                valid_name_found = true;
        }
@@ -258,7 +311,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
        /* Make the built-in MII bus mask match the number of ports,
         * switch drivers can override this later
         */
-       ds->phys_mii_mask = ds->phys_port_mask;
+       ds->phys_mii_mask = ds->enabled_port_mask;
 
        /*
         * If the CPU connects to this switch, set the switch tree
@@ -266,37 +319,17 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
         * switch.
         */
        if (dst->cpu_switch == index) {
-               switch (ds->tag_protocol) {
-#ifdef CONFIG_NET_DSA_TAG_DSA
-               case DSA_TAG_PROTO_DSA:
-                       dst->rcv = dsa_netdev_ops.rcv;
-                       break;
-#endif
-#ifdef CONFIG_NET_DSA_TAG_EDSA
-               case DSA_TAG_PROTO_EDSA:
-                       dst->rcv = edsa_netdev_ops.rcv;
-                       break;
-#endif
-#ifdef CONFIG_NET_DSA_TAG_TRAILER
-               case DSA_TAG_PROTO_TRAILER:
-                       dst->rcv = trailer_netdev_ops.rcv;
-                       break;
-#endif
-#ifdef CONFIG_NET_DSA_TAG_BRCM
-               case DSA_TAG_PROTO_BRCM:
-                       dst->rcv = brcm_netdev_ops.rcv;
-                       break;
-#endif
-               case DSA_TAG_PROTO_NONE:
-                       break;
-               default:
-                       ret = -ENOPROTOOPT;
+               dst->tag_ops = dsa_resolve_tag_protocol(drv->tag_protocol);
+               if (IS_ERR(dst->tag_ops)) {
+                       ret = PTR_ERR(dst->tag_ops);
                        goto out;
                }
 
-               dst->tag_protocol = ds->tag_protocol;
+               dst->rcv = dst->tag_ops->rcv;
        }
 
+       memcpy(ds->rtable, cd->rtable, sizeof(ds->rtable));
+
        /*
         * Do basic register setup.
         */
@@ -308,35 +341,38 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
        if (ret < 0)
                goto out;
 
-       ds->slave_mii_bus = devm_mdiobus_alloc(parent);
-       if (ds->slave_mii_bus == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       dsa_slave_mii_bus_init(ds);
-
-       ret = mdiobus_register(ds->slave_mii_bus);
-       if (ret < 0)
-               goto out;
+       if (!ds->slave_mii_bus && drv->phy_read) {
+               ds->slave_mii_bus = devm_mdiobus_alloc(parent);
+               if (!ds->slave_mii_bus) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               dsa_slave_mii_bus_init(ds);
 
+               ret = mdiobus_register(ds->slave_mii_bus);
+               if (ret < 0)
+                       goto out;
+       }
 
        /*
         * Create network devices for physical switch ports.
         */
        for (i = 0; i < DSA_MAX_PORTS; i++) {
-               if (!(ds->phys_port_mask & (1 << i)))
+               ds->ports[i].dn = cd->port_dn[i];
+
+               if (!(ds->enabled_port_mask & (1 << i)))
                        continue;
 
-               ret = dsa_slave_create(ds, parent, i, pd->port_names[i]);
+               ret = dsa_slave_create(ds, parent, i, cd->port_names[i]);
                if (ret < 0) {
                        netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s): %d\n",
-                                  index, i, pd->port_names[i], ret);
+                                  index, i, cd->port_names[i], ret);
                        ret = 0;
                }
        }
 
        /* Perform configuration of the CPU and DSA ports */
-       ret = dsa_cpu_dsa_setup(ds, dst->master_netdev);
+       ret = dsa_cpu_dsa_setups(ds, parent);
        if (ret < 0) {
                netdev_err(dst->master_netdev, "[%d] : can't configure CPU and DSA ports\n",
                           index);
@@ -378,16 +414,17 @@ static struct dsa_switch *
 dsa_switch_setup(struct dsa_switch_tree *dst, int index,
                 struct device *parent, struct device *host_dev)
 {
-       struct dsa_chip_data *pd = dst->pd->chip + index;
+       struct dsa_chip_data *cd = dst->pd->chip + index;
        struct dsa_switch_driver *drv;
        struct dsa_switch *ds;
        int ret;
-       char *name;
+       const char *name;
+       void *priv;
 
        /*
         * Probe for switch model.
         */
-       drv = dsa_switch_probe(host_dev, pd->sw_addr, &name);
+       drv = dsa_switch_probe(parent, host_dev, cd->sw_addr, &name, &priv);
        if (drv == NULL) {
                netdev_err(dst->master_netdev, "[%d]: could not detect attached switch\n",
                           index);
@@ -400,16 +437,16 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
        /*
         * Allocate and initialise switch state.
         */
-       ds = devm_kzalloc(parent, sizeof(*ds) + drv->priv_size, GFP_KERNEL);
+       ds = devm_kzalloc(parent, sizeof(*ds), GFP_KERNEL);
        if (ds == NULL)
                return ERR_PTR(-ENOMEM);
 
        ds->dst = dst;
        ds->index = index;
-       ds->pd = pd;
+       ds->cd = cd;
        ds->drv = drv;
-       ds->tag_protocol = drv->tag_protocol;
-       ds->master_dev = host_dev;
+       ds->priv = priv;
+       ds->dev = parent;
 
        ret = dsa_switch_setup_one(ds, parent);
        if (ret)
@@ -418,11 +455,21 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
        return ds;
 }
 
-static void dsa_switch_destroy(struct dsa_switch *ds)
+void dsa_cpu_dsa_destroy(struct device_node *port_dn)
 {
-       struct device_node *port_dn;
        struct phy_device *phydev;
-       struct dsa_chip_data *cd = ds->pd;
+
+       if (of_phy_is_fixed_link(port_dn)) {
+               phydev = of_phy_find_device(port_dn);
+               if (phydev) {
+                       phy_device_free(phydev);
+                       fixed_phy_unregister(phydev);
+               }
+       }
+}
+
+static void dsa_switch_destroy(struct dsa_switch *ds)
+{
        int port;
 
 #ifdef CONFIG_NET_DSA_HWMON
@@ -432,29 +479,28 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
 
        /* Destroy network devices for physical switch ports. */
        for (port = 0; port < DSA_MAX_PORTS; port++) {
-               if (!(ds->phys_port_mask & (1 << port)))
+               if (!(ds->enabled_port_mask & (1 << port)))
                        continue;
 
-               if (!ds->ports[port])
+               if (!ds->ports[port].netdev)
                        continue;
 
-               dsa_slave_destroy(ds->ports[port]);
+               dsa_slave_destroy(ds->ports[port].netdev);
        }
 
-       /* Remove any fixed link PHYs */
+       /* Disable configuration of the CPU and DSA ports */
        for (port = 0; port < DSA_MAX_PORTS; port++) {
-               port_dn = cd->port_dn[port];
-               if (of_phy_is_fixed_link(port_dn)) {
-                       phydev = of_phy_find_device(port_dn);
-                       if (phydev) {
-                               phy_device_free(phydev);
-                               of_node_put(port_dn);
-                               fixed_phy_unregister(phydev);
-                       }
-               }
+               if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)))
+                       continue;
+               dsa_cpu_dsa_destroy(ds->ports[port].dn);
+
+               /* Clearing a bit which is not set does no harm */
+               ds->cpu_port_mask |= ~(1 << port);
+               ds->dsa_port_mask |= ~(1 << port);
        }
 
-       mdiobus_unregister(ds->slave_mii_bus);
+       if (ds->slave_mii_bus && ds->drv->phy_read)
+               mdiobus_unregister(ds->slave_mii_bus);
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -467,7 +513,7 @@ static int dsa_switch_suspend(struct dsa_switch *ds)
                if (!dsa_is_port_initialized(ds, i))
                        continue;
 
-               ret = dsa_slave_suspend(ds->ports[i]);
+               ret = dsa_slave_suspend(ds->ports[i].netdev);
                if (ret)
                        return ret;
        }
@@ -493,7 +539,7 @@ static int dsa_switch_resume(struct dsa_switch *ds)
                if (!dsa_is_port_initialized(ds, i))
                        continue;
 
-               ret = dsa_slave_resume(ds->ports[i]);
+               ret = dsa_slave_resume(ds->ports[i].netdev);
                if (ret)
                        return ret;
        }
@@ -585,17 +631,6 @@ static int dsa_of_setup_routing_table(struct dsa_platform_data *pd,
        if (link_sw_addr >= pd->nr_chips)
                return -EINVAL;
 
-       /* First time routing table allocation */
-       if (!cd->rtable) {
-               cd->rtable = kmalloc_array(pd->nr_chips, sizeof(s8),
-                                          GFP_KERNEL);
-               if (!cd->rtable)
-                       return -ENOMEM;
-
-               /* default to no valid uplink/downlink */
-               memset(cd->rtable, -1, pd->nr_chips * sizeof(s8));
-       }
-
        cd->rtable[link_sw_addr] = port_index;
 
        return 0;
@@ -637,7 +672,6 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
                        kfree(pd->chip[i].port_names[port_index]);
                        port_index++;
                }
-               kfree(pd->chip[i].rtable);
 
                /* Drop our reference to the MDIO bus device */
                if (pd->chip[i].host_dev)
@@ -657,9 +691,6 @@ static int dsa_of_probe(struct device *dev)
        const char *port_name;
        int chip_index, port_index;
        const unsigned int *sw_addr, *port_reg;
-       int gpio;
-       enum of_gpio_flags of_flags;
-       unsigned long flags;
        u32 eeprom_len;
        int ret;
 
@@ -738,19 +769,6 @@ static int dsa_of_probe(struct device *dev)
                        put_device(cd->host_dev);
                        cd->host_dev = &mdio_bus_switch->dev;
                }
-               gpio = of_get_named_gpio_flags(child, "reset-gpios", 0,
-                                              &of_flags);
-               if (gpio_is_valid(gpio)) {
-                       flags = (of_flags == OF_GPIO_ACTIVE_LOW ?
-                                GPIOF_ACTIVE_LOW : 0);
-                       ret = devm_gpio_request_one(dev, gpio, flags,
-                                                   "switch_reset");
-                       if (ret)
-                               goto out_free_chip;
-
-                       cd->reset = gpio_to_desc(gpio);
-                       gpiod_direction_output(cd->reset, 0);
-               }
 
                for_each_available_child_of_node(child, port) {
                        port_reg = of_get_property(port, "reg", NULL);
This page took 0.039047 seconds and 5 git commands to generate.