diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 30383c4f8fd0..260e38c5c6e6 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3680,11 +3680,218 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip) return mv88e6xxx_software_reset(chip); } +/* prod_id for switch families which do not have a PHY model number */ +static const u16 family_prod_id_table[] = { + [MV88E6XXX_FAMILY_6341] = MV88E6XXX_PORT_SWITCH_ID_PROD_6341, + [MV88E6XXX_FAMILY_6390] = MV88E6XXX_PORT_SWITCH_ID_PROD_6390, + [MV88E6XXX_FAMILY_6393] = MV88E6XXX_PORT_SWITCH_ID_PROD_6393X, +}; + +static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg) +{ + struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; + struct mv88e6xxx_chip *chip = mdio_bus->chip; + u16 prod_id; + u16 val; + int err; + + if (!chip->info->ops->phy_read) + return -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); + err = chip->info->ops->phy_read(chip, bus, phy, reg, &val); + mv88e6xxx_reg_unlock(chip); + + /* Some internal PHYs don't have a model number. */ + if (reg == MII_PHYSID2 && !(val & 0x3f0) && + chip->info->family < ARRAY_SIZE(family_prod_id_table)) { + prod_id = family_prod_id_table[chip->info->family]; + if (prod_id) + val |= prod_id >> 4; + } + + return err ? err : val; +} + +static int mv88e6xxx_mdio_read_c45(struct mii_bus *bus, int phy, int devad, + int reg) +{ + struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; + struct mv88e6xxx_chip *chip = mdio_bus->chip; + u16 val; + int err; + + if (!chip->info->ops->phy_read_c45) + return -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); + err = chip->info->ops->phy_read_c45(chip, bus, phy, devad, reg, &val); + mv88e6xxx_reg_unlock(chip); + + return err ? err : val; +} + +static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) +{ + struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; + struct mv88e6xxx_chip *chip = mdio_bus->chip; + int err; + + if (!chip->info->ops->phy_write) + return -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); + err = chip->info->ops->phy_write(chip, bus, phy, reg, val); + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_mdio_write_c45(struct mii_bus *bus, int phy, int devad, + int reg, u16 val) +{ + struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; + struct mv88e6xxx_chip *chip = mdio_bus->chip; + int err; + + if (!chip->info->ops->phy_write_c45) + return -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); + err = chip->info->ops->phy_write_c45(chip, bus, phy, devad, reg, val); + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, + struct device_node *np, + bool external) +{ + static int index; + struct mv88e6xxx_mdio_bus *mdio_bus; + struct mii_bus *bus; + int err; + + if (external) { + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true); + mv88e6xxx_reg_unlock(chip); + + if (err) + return err; + } + + bus = mdiobus_alloc_size(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; + snprintf(bus->id, MII_BUS_ID_SIZE, "%pOF", np); + } else { + bus->name = "mv88e6xxx SMI"; + snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++); + } + + bus->read = mv88e6xxx_mdio_read; + bus->write = mv88e6xxx_mdio_write; + bus->read_c45 = mv88e6xxx_mdio_read_c45; + bus->write_c45 = mv88e6xxx_mdio_write_c45; + bus->parent = chip->dev; + bus->phy_mask = GENMASK(31, mv88e6xxx_num_ports(chip)); + + if (!external) { + err = mv88e6xxx_g2_irq_mdio_setup(chip, bus); + if (err) + goto out; + } + + err = of_mdiobus_register(bus, np); + if (err) { + dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err); + mv88e6xxx_g2_irq_mdio_free(chip, bus); + goto out; + } + + if (external) + list_add_tail(&mdio_bus->list, &chip->mdios); + else + list_add(&mdio_bus->list, &chip->mdios); + + return 0; + +out: + mdiobus_free(bus); + return err; +} + +static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip) + +{ + struct mv88e6xxx_mdio_bus *mdio_bus, *p; + struct mii_bus *bus; + + list_for_each_entry_safe(mdio_bus, p, &chip->mdios, list) { + bus = mdio_bus->bus; + + if (!mdio_bus->external) + mv88e6xxx_g2_irq_mdio_free(chip, bus); + + mdiobus_unregister(bus); + mdiobus_free(bus); + } +} + +static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip) +{ + struct device_node *np = chip->dev->of_node; + 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); + of_node_put(child); + 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) { + if (of_device_is_compatible( + child, "marvell,mv88e6xxx-mdio-external")) { + err = mv88e6xxx_mdio_register(chip, child, true); + if (err) { + mv88e6xxx_mdios_unregister(chip); + of_node_put(child); + return err; + } + } + } + + return 0; +} + static void mv88e6xxx_teardown(struct dsa_switch *ds) { + struct mv88e6xxx_chip *chip = ds->priv; + mv88e6xxx_teardown_devlink_params(ds); dsa_devlink_resources_unregister(ds); mv88e6xxx_teardown_devlink_regions_global(ds); + mv88e6xxx_mdios_unregister(chip); } static int mv88e6xxx_setup(struct dsa_switch *ds) @@ -3694,6 +3901,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) int err; int i; + err = mv88e6xxx_mdios_register(chip); + if (err) + return err; + chip->ds = ds; ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); @@ -3820,7 +4031,7 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) mv88e6xxx_reg_unlock(chip); if (err) - return err; + goto out_mdios; /* Have to be called without holding the register lock, since * they take the devlink lock, and we later take the locks in @@ -3829,7 +4040,7 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) */ err = mv88e6xxx_setup_devlink_resources(ds); if (err) - return err; + goto out_mdios; err = mv88e6xxx_setup_devlink_params(ds); if (err) @@ -3845,6 +4056,8 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) mv88e6xxx_teardown_devlink_params(ds); out_resources: dsa_devlink_resources_unregister(ds); +out_mdios: + mv88e6xxx_mdios_unregister(chip); return err; } @@ -3859,209 +4072,6 @@ static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port) mv88e6xxx_teardown_devlink_regions_port(ds, port); } -/* prod_id for switch families which do not have a PHY model number */ -static const u16 family_prod_id_table[] = { - [MV88E6XXX_FAMILY_6341] = MV88E6XXX_PORT_SWITCH_ID_PROD_6341, - [MV88E6XXX_FAMILY_6390] = MV88E6XXX_PORT_SWITCH_ID_PROD_6390, - [MV88E6XXX_FAMILY_6393] = MV88E6XXX_PORT_SWITCH_ID_PROD_6393X, -}; - -static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg) -{ - struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; - struct mv88e6xxx_chip *chip = mdio_bus->chip; - u16 prod_id; - u16 val; - int err; - - if (!chip->info->ops->phy_read) - return -EOPNOTSUPP; - - mv88e6xxx_reg_lock(chip); - err = chip->info->ops->phy_read(chip, bus, phy, reg, &val); - mv88e6xxx_reg_unlock(chip); - - /* Some internal PHYs don't have a model number. */ - if (reg == MII_PHYSID2 && !(val & 0x3f0) && - chip->info->family < ARRAY_SIZE(family_prod_id_table)) { - prod_id = family_prod_id_table[chip->info->family]; - if (prod_id) - val |= prod_id >> 4; - } - - return err ? err : val; -} - -static int mv88e6xxx_mdio_read_c45(struct mii_bus *bus, int phy, int devad, - int reg) -{ - struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; - struct mv88e6xxx_chip *chip = mdio_bus->chip; - u16 val; - int err; - - if (!chip->info->ops->phy_read_c45) - return -EOPNOTSUPP; - - mv88e6xxx_reg_lock(chip); - err = chip->info->ops->phy_read_c45(chip, bus, phy, devad, reg, &val); - mv88e6xxx_reg_unlock(chip); - - return err ? err : val; -} - -static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) -{ - struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; - struct mv88e6xxx_chip *chip = mdio_bus->chip; - int err; - - if (!chip->info->ops->phy_write) - return -EOPNOTSUPP; - - mv88e6xxx_reg_lock(chip); - err = chip->info->ops->phy_write(chip, bus, phy, reg, val); - mv88e6xxx_reg_unlock(chip); - - return err; -} - -static int mv88e6xxx_mdio_write_c45(struct mii_bus *bus, int phy, int devad, - int reg, u16 val) -{ - struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; - struct mv88e6xxx_chip *chip = mdio_bus->chip; - int err; - - if (!chip->info->ops->phy_write_c45) - return -EOPNOTSUPP; - - mv88e6xxx_reg_lock(chip); - err = chip->info->ops->phy_write_c45(chip, bus, phy, devad, reg, val); - mv88e6xxx_reg_unlock(chip); - - return err; -} - -static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, - struct device_node *np, - bool external) -{ - static int index; - struct mv88e6xxx_mdio_bus *mdio_bus; - struct mii_bus *bus; - int err; - - if (external) { - mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true); - mv88e6xxx_reg_unlock(chip); - - if (err) - return err; - } - - bus = mdiobus_alloc_size(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; - snprintf(bus->id, MII_BUS_ID_SIZE, "%pOF", np); - } else { - bus->name = "mv88e6xxx SMI"; - snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++); - } - - bus->read = mv88e6xxx_mdio_read; - bus->write = mv88e6xxx_mdio_write; - bus->read_c45 = mv88e6xxx_mdio_read_c45; - bus->write_c45 = mv88e6xxx_mdio_write_c45; - bus->parent = chip->dev; - - if (!external) { - err = mv88e6xxx_g2_irq_mdio_setup(chip, bus); - if (err) - goto out; - } - - err = of_mdiobus_register(bus, np); - if (err) { - dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err); - mv88e6xxx_g2_irq_mdio_free(chip, bus); - goto out; - } - - if (external) - list_add_tail(&mdio_bus->list, &chip->mdios); - else - list_add(&mdio_bus->list, &chip->mdios); - - return 0; - -out: - mdiobus_free(bus); - return err; -} - -static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip) - -{ - struct mv88e6xxx_mdio_bus *mdio_bus, *p; - struct mii_bus *bus; - - list_for_each_entry_safe(mdio_bus, p, &chip->mdios, list) { - bus = mdio_bus->bus; - - if (!mdio_bus->external) - mv88e6xxx_g2_irq_mdio_free(chip, bus); - - mdiobus_unregister(bus); - mdiobus_free(bus); - } -} - -static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, - struct device_node *np) -{ - 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); - of_node_put(child); - 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) { - if (of_device_is_compatible( - child, "marvell,mv88e6xxx-mdio-external")) { - err = mv88e6xxx_mdio_register(chip, child, true); - if (err) { - mv88e6xxx_mdios_unregister(chip); - of_node_put(child); - return err; - } - } - } - - return 0; -} - static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds) { struct mv88e6xxx_chip *chip = ds->priv; @@ -7228,18 +7238,12 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) if (err) goto out_g1_atu_prob_irq; - err = mv88e6xxx_mdios_register(chip, np); + err = mv88e6xxx_register_switch(chip); if (err) goto out_g1_vtu_prob_irq; - err = mv88e6xxx_register_switch(chip); - if (err) - goto out_mdio; - return 0; -out_mdio: - mv88e6xxx_mdios_unregister(chip); out_g1_vtu_prob_irq: mv88e6xxx_g1_vtu_prob_irq_free(chip); out_g1_atu_prob_irq: @@ -7276,7 +7280,6 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) mv88e6xxx_phy_destroy(chip); mv88e6xxx_unregister_switch(chip); - mv88e6xxx_mdios_unregister(chip); mv88e6xxx_g1_vtu_prob_irq_free(chip); mv88e6xxx_g1_atu_prob_irq_free(chip); diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index ed3b2f88e783..a26546d3d7b5 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -1176,31 +1176,19 @@ int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip, struct mii_bus *bus) { - int phy, irq, err, err_phy; + int phy, irq; for (phy = 0; phy < chip->info->num_internal_phys; phy++) { irq = irq_find_mapping(chip->g2_irq.domain, phy); - if (irq < 0) { - err = irq; - goto out; - } + if (irq < 0) + return irq; + bus->irq[chip->info->phy_base_addr + phy] = irq; } return 0; -out: - err_phy = phy; - - for (phy = 0; phy < err_phy; phy++) - irq_dispose_mapping(bus->irq[phy]); - - return err; } void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip, struct mii_bus *bus) { - int phy; - - for (phy = 0; phy < chip->info->num_internal_phys; phy++) - irq_dispose_mapping(bus->irq[phy]); }