static int bcm_sf2_mdio_register(struct dsa_switch *ds) { struct bcm_sf2_priv *priv = ds_to_priv(ds); struct device_node *dn; static int index; int err; /* Find our integrated MDIO bus node */ dn = of_find_compatible_node(NULL, NULL, "brcm,unimac-mdio"); priv->master_mii_bus = of_mdio_find_bus(dn); if (!priv->master_mii_bus) return -EPROBE_DEFER; get_device(&priv->master_mii_bus->dev); priv->master_mii_dn = dn; priv->slave_mii_bus = devm_mdiobus_alloc(ds->dev); if (!priv->slave_mii_bus) return -ENOMEM; priv->slave_mii_bus->priv = priv; priv->slave_mii_bus->name = "sf2 slave mii"; priv->slave_mii_bus->read = bcm_sf2_sw_mdio_read; priv->slave_mii_bus->write = bcm_sf2_sw_mdio_write; snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "sf2-%d", index++); priv->slave_mii_bus->dev.of_node = dn; /* Include the pseudo-PHY address to divert reads towards our * workaround. This is only required for 7445D0, since 7445E0 * disconnects the internal switch pseudo-PHY such that we can use the * regular SWITCH_MDIO master controller instead. * * Here we flag the pseudo PHY as needing special treatment and would * otherwise make all other PHY read/writes go to the master MDIO bus * controller that comes with this switch backed by the "mdio-unimac" * driver. */ if (of_machine_is_compatible("brcm,bcm7445d0")) priv->indir_phy_mask |= (1 << BRCM_PSEUDO_PHY_ADDR); else priv->indir_phy_mask = 0; ds->phys_mii_mask = priv->indir_phy_mask; ds->slave_mii_bus = priv->slave_mii_bus; priv->slave_mii_bus->parent = ds->dev->parent; priv->slave_mii_bus->phy_mask = ~priv->indir_phy_mask; if (dn) err = of_mdiobus_register(priv->slave_mii_bus, dn); else err = mdiobus_register(priv->slave_mii_bus); if (err) of_node_put(dn); return err; }
int mdio_mux_init(struct device *dev, struct device_node *mux_node, int (*switch_fn)(int cur, int desired, void *data), void **mux_handle, void *data, struct mii_bus *mux_bus) { struct device_node *parent_bus_node; struct device_node *child_bus_node; int r, ret_val; struct mii_bus *parent_bus; struct mdio_mux_parent_bus *pb; struct mdio_mux_child_bus *cb; if (!mux_node) return -ENODEV; if (!mux_bus) { parent_bus_node = of_parse_phandle(mux_node, "mdio-parent-bus", 0); if (!parent_bus_node) return -ENODEV; parent_bus = of_mdio_find_bus(parent_bus_node); if (!parent_bus) { ret_val = -EPROBE_DEFER; goto err_parent_bus; } } else { parent_bus_node = NULL; parent_bus = mux_bus; get_device(&parent_bus->dev); } pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL); if (!pb) { ret_val = -ENOMEM; goto err_pb_kz; } pb->switch_data = data; pb->switch_fn = switch_fn; pb->current_child = -1; pb->parent_id = parent_count++; pb->mii_bus = parent_bus; ret_val = -ENODEV; for_each_available_child_of_node(mux_node, child_bus_node) { int v; r = of_property_read_u32(child_bus_node, "reg", &v); if (r) { dev_err(dev, "Error: Failed to find reg for child %pOF\n", child_bus_node); continue; } cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL); if (!cb) { ret_val = -ENOMEM; continue; } cb->bus_number = v; cb->parent = pb; cb->mii_bus = mdiobus_alloc(); if (!cb->mii_bus) { ret_val = -ENOMEM; devm_kfree(dev, cb); continue; } cb->mii_bus->priv = cb; cb->mii_bus->name = "mdio_mux"; snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x", pb->parent_id, v); cb->mii_bus->parent = dev; cb->mii_bus->read = mdio_mux_read; cb->mii_bus->write = mdio_mux_write; r = of_mdiobus_register(cb->mii_bus, child_bus_node); if (r) { dev_err(dev, "Error: Failed to register MDIO bus for child %pOF\n", child_bus_node); mdiobus_free(cb->mii_bus); devm_kfree(dev, cb); } else { cb->next = pb->children; pb->children = cb; } }