From: Vladimir Oltean The KSZ switch families are sufficiently different that a common ds->ops->setup() - ksz_setup() with micro-managed dev_ops->reset(), dev_ops->pcs_create(), dev_ops->config_cpu_port(), dev_ops->enable_stp_addr(), dev_ops->setup() seems to be too convoluted. I am proposing to make each KSZ switch family part ways for dsa_switch_ops :: setup() and teardown(), to allow them greater flexibility. This here is the implementation for ksz9477, which is nothing other than a copy of ksz_setup() with the dev_ops function pointers replaced with direct function calls. Signed-off-by: Vladimir Oltean Signed-off-by: Bastien Curutchet (Schneider Electric) --- drivers/net/dsa/microchip/ksz9477.c | 127 +++++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 9 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 88a5ff62aae89..7385aa4e788a1 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -1477,9 +1477,54 @@ int ksz9477_enable_stp_addr(struct ksz_device *dev) static int ksz9477_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; - const u16 *regs = dev->info->regs; - int ret = 0; + u16 storm_mask, storm_rate; + struct dsa_port *dp; + struct ksz_port *p; + const u16 *regs; + int ret; + + regs = dev->info->regs; + + dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), + dev->info->num_vlans, GFP_KERNEL); + if (!dev->vlan_cache) + return -ENOMEM; + + ret = ksz9477_reset_switch(dev); + if (ret) { + dev_err(ds->dev, "failed to reset switch\n"); + return ret; + } + + ret = ksz_parse_drive_strength(dev); + if (ret) + return ret; + if (ksz_has_sgmii_port(dev)) { + ret = ksz9477_pcs_create(dev); + if (ret) + return ret; + } + + /* set broadcast storm protection 10% rate */ + storm_mask = BROADCAST_STORM_RATE; + storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100; + regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL], + storm_mask, storm_rate); + + ksz9477_config_cpu_port(ds); + + ksz9477_enable_stp_addr(dev); + + ds->num_tx_queues = dev->info->num_tx_queues; + + regmap_update_bits(ksz_regmap_8(dev), regs[S_MULTICAST_CTRL], + MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE); + + ksz_init_mib_timer(dev); + + ds->configure_vlan_while_not_filtering = false; + ds->dscp_prio_mapping_is_global = true; ds->mtu_enforcement_ingress = true; /* Required for port partitioning. */ @@ -1512,7 +1557,76 @@ static int ksz9477_setup(struct dsa_switch *ds) * be enabled by ksz_wol_pre_shutdown(). Otherwise, some PMICs * do not like PME events changes before shutdown. */ - return ksz_write8(dev, regs[REG_SW_PME_CTRL], 0); + ret = ksz_write8(dev, regs[REG_SW_PME_CTRL], 0); + if (ret < 0) + return ret; + + /* Start with learning disabled on standalone user ports, and enabled + * on the CPU port. In lack of other finer mechanisms, learning on the + * CPU port will avoid flooding bridge local addresses on the network + * in some cases. + */ + p = &dev->ports[dev->cpu_port]; + p->learning = true; + + if (dev->irq > 0) { + ret = ksz_girq_setup(dev); + if (ret) + return ret; + + dsa_switch_for_each_user_port(dp, dev->ds) { + ret = ksz_pirq_setup(dev, dp->index); + if (ret) + goto port_release; + + if (dev->info->ptp_capable) { + ret = ksz_ptp_irq_setup(ds, dp->index); + if (ret) + goto pirq_release; + } + } + } + + if (dev->info->ptp_capable) { + ret = ksz_ptp_clock_register(ds); + if (ret) { + dev_err(dev->dev, "Failed to register PTP clock: %d\n", + ret); + goto port_release; + } + } + + ret = ksz_mdio_register(dev); + if (ret < 0) { + dev_err(dev->dev, "failed to register the mdio"); + goto out_ptp_clock_unregister; + } + + ret = ksz_dcb_init(dev); + if (ret) + goto out_ptp_clock_unregister; + + /* start switch */ + regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL], + SW_START, SW_START); + + return 0; + +out_ptp_clock_unregister: + if (dev->info->ptp_capable) + ksz_ptp_clock_unregister(ds); +port_release: + if (dev->irq > 0) { + dsa_switch_for_each_user_port_continue_reverse(dp, dev->ds) { + if (dev->info->ptp_capable) + ksz_ptp_irq_free(ds, dp->index); +pirq_release: + ksz_irq_free(&dev->ports[dp->index].pirq); + } + ksz_irq_free(&dev->girq); + } + + return ret; } u32 ksz9477_get_port_addr(int port, int offset) @@ -1779,7 +1893,6 @@ const struct phylink_mac_ops ksz9477_phylink_mac_ops = { }; const struct ksz_dev_ops ksz9477_dev_ops = { - .setup = ksz9477_setup, .get_port_addr = ksz9477_get_port_addr, .cfg_port_member = ksz9477_cfg_port_member, .port_setup = ksz9477_port_setup, @@ -1793,19 +1906,15 @@ const struct ksz_dev_ops ksz9477_dev_ops = { .pme_write8 = ksz_write8, .pme_pread8 = ksz_pread8, .pme_pwrite8 = ksz_pwrite8, - .config_cpu_port = ksz9477_config_cpu_port, .tc_cbs_set_cinc = ksz9477_tc_cbs_set_cinc, - .enable_stp_addr = ksz9477_enable_stp_addr, - .reset = ksz9477_reset_switch, .init = ksz9477_switch_init, - .pcs_create = ksz9477_pcs_create, }; const struct dsa_switch_ops ksz9477_switch_ops = { .get_tag_protocol = ksz9477_get_tag_protocol, .connect_tag_protocol = ksz9477_connect_tag_protocol, .get_phy_flags = ksz_get_phy_flags, - .setup = ksz_setup, + .setup = ksz9477_setup, .teardown = ksz_teardown, .phy_read = ksz_phy_read16, .phy_write = ksz_phy_write16, -- 2.53.0