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 lan937x, 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/ksz_common.c | 12 ++-- drivers/net/dsa/microchip/ksz_common.h | 6 ++ drivers/net/dsa/microchip/lan937x_main.c | 116 ++++++++++++++++++++++++++++--- 3 files changed, 116 insertions(+), 18 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 78d77a60a6ce2..c2624b1930f51 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -2488,7 +2488,7 @@ static int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus, * * Return: 0 on success, or a negative error code on failure. */ -static int ksz_mdio_register(struct ksz_device *dev) +int ksz_mdio_register(struct ksz_device *dev) { struct device_node *parent_bus_node; struct mii_bus *parent_bus = NULL; @@ -2644,7 +2644,7 @@ static const struct irq_domain_ops ksz_irq_domain_ops = { .xlate = irq_domain_xlate_twocell, }; -static void ksz_irq_free(struct ksz_irq *kirq) +void ksz_irq_free(struct ksz_irq *kirq) { int irq, virq; @@ -2713,7 +2713,7 @@ static int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq) return ret; } -static int ksz_girq_setup(struct ksz_device *dev) +int ksz_girq_setup(struct ksz_device *dev) { struct ksz_irq *girq = &dev->girq; @@ -2728,7 +2728,7 @@ static int ksz_girq_setup(struct ksz_device *dev) return ksz_irq_common_setup(dev, girq); } -static int ksz_pirq_setup(struct ksz_device *dev, u8 p) +int ksz_pirq_setup(struct ksz_device *dev, u8 p) { struct ksz_irq *pirq = &dev->ports[p].pirq; @@ -2745,8 +2745,6 @@ static int ksz_pirq_setup(struct ksz_device *dev, u8 p) return ksz_irq_common_setup(dev, pirq); } -static int ksz_parse_drive_strength(struct ksz_device *dev); - int ksz_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; @@ -4698,7 +4696,7 @@ static int ksz88x3_drive_strength_write(struct ksz_device *dev, * * Return: 0 on success, error code otherwise */ -static int ksz_parse_drive_strength(struct ksz_device *dev) +int ksz_parse_drive_strength(struct ksz_device *dev) { struct ksz_driver_strength_prop of_props[] = { [KSZ_DRIVER_STRENGTH_HI] = { diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 1cdb6661729a6..5fad56c2d067a 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -518,6 +518,12 @@ int ksz_hsr_leave(struct dsa_switch *ds, int port, int ksz_suspend(struct dsa_switch *ds); int ksz_resume(struct dsa_switch *ds); +int ksz_mdio_register(struct ksz_device *dev); +int ksz_pirq_setup(struct ksz_device *dev, u8 p); +int ksz_girq_setup(struct ksz_device *dev); +void ksz_irq_free(struct ksz_irq *kirq); +int ksz_parse_drive_strength(struct ksz_device *dev); + /* Common register access functions */ static inline struct regmap *ksz_regmap_8(struct ksz_device *dev) { diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index 778e32f568df4..ecb072cd99819 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -626,8 +626,49 @@ static int lan937x_switch_init(struct ksz_device *dev) static int lan937x_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; + 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 = lan937x_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; + + /* 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); + + lan937x_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; + /* The VLAN aware is a global setting. Mixed vlan * filterings are not supported. */ @@ -659,13 +700,71 @@ static int lan937x_setup(struct dsa_switch *ds) return ret; /* Disable global VPHY support. Related to CPU interface only? */ - return ksz_rmw32(dev, REG_SW_CFG_STRAP_OVR, SW_VPHY_DISABLE, - SW_VPHY_DISABLE); -} + ret = ksz_rmw32(dev, REG_SW_CFG_STRAP_OVR, SW_VPHY_DISABLE, + SW_VPHY_DISABLE); + if (ret < 0) + return ret; -static void lan937x_teardown(struct dsa_switch *ds) -{ + /* 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; + + ret = ksz_ptp_irq_setup(ds, dp->index); + if (ret) + goto pirq_release; + } + } + + 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: + ksz_ptp_clock_unregister(ds); +port_release: + if (dev->irq > 0) { + dsa_switch_for_each_user_port_continue_reverse(dp, dev->ds) { + ksz_ptp_irq_free(ds, dp->index); +pirq_release: + ksz_irq_free(&dev->ports[dp->index].pirq); + } + ksz_irq_free(&dev->girq); + } + + return ret; } static enum dsa_tag_protocol lan937x_get_tag_protocol(struct dsa_switch *ds, @@ -698,8 +797,6 @@ const struct phylink_mac_ops lan937x_phylink_mac_ops = { }; const struct ksz_dev_ops lan937x_dev_ops = { - .setup = lan937x_setup, - .teardown = lan937x_teardown, .get_port_addr = ksz9477_get_port_addr, .cfg_port_member = ksz9477_cfg_port_member, .port_setup = lan937x_port_setup, @@ -713,10 +810,7 @@ const struct ksz_dev_ops lan937x_dev_ops = { .freeze_mib = ksz9477_freeze_mib, .port_init_cnt = ksz9477_port_init_cnt, .setup_rgmii_delay = lan937x_setup_rgmii_delay, - .config_cpu_port = lan937x_config_cpu_port, .tc_cbs_set_cinc = lan937x_tc_cbs_set_cinc, - .enable_stp_addr = ksz9477_enable_stp_addr, - .reset = lan937x_reset_switch, .init = lan937x_switch_init, }; @@ -724,7 +818,7 @@ const struct dsa_switch_ops lan937x_switch_ops = { .get_tag_protocol = lan937x_get_tag_protocol, .connect_tag_protocol = lan937x_connect_tag_protocol, .get_phy_flags = ksz_get_phy_flags, - .setup = ksz_setup, + .setup = lan937x_setup, .teardown = ksz_teardown, .phy_read = ksz_phy_read16, .phy_write = ksz_phy_write16, -- 2.53.0