diff options
Diffstat (limited to 'drivers/net/dsa')
31 files changed, 1990 insertions, 122 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index c7667645f04a..2d38dbc9dd8c 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -54,6 +54,8 @@ source "drivers/net/dsa/mv88e6xxx/Kconfig" source "drivers/net/dsa/ocelot/Kconfig" +source "drivers/net/dsa/qca/Kconfig" + source "drivers/net/dsa/sja1105/Kconfig" config NET_DSA_QCA8K @@ -103,7 +105,6 @@ config NET_DSA_SMSC_LAN9303_MDIO config NET_DSA_VITESSE_VSC73XX tristate - depends on OF depends on NET_DSA select FIXED_PHY select VITESSE_PHY @@ -114,7 +115,6 @@ config NET_DSA_VITESSE_VSC73XX config NET_DSA_VITESSE_VSC73XX_SPI tristate "Vitesse VSC7385/7388/7395/7398 SPI mode support" - depends on OF depends on NET_DSA depends on SPI select NET_DSA_VITESSE_VSC73XX @@ -124,7 +124,6 @@ config NET_DSA_VITESSE_VSC73XX_SPI config NET_DSA_VITESSE_VSC73XX_PLATFORM tristate "Vitesse VSC7385/7388/7395/7398 Platform mode support" - depends on OF depends on NET_DSA depends on HAS_IOMEM select NET_DSA_VITESSE_VSC73XX diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 9d384a32b3a2..4a943ccc2ca4 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -21,4 +21,5 @@ obj-y += b53/ obj-y += microchip/ obj-y += mv88e6xxx/ obj-y += ocelot/ +obj-y += qca/ obj-y += sja1105/ diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index edacacfc9365..060497512159 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -371,8 +371,6 @@ static void b53_enable_vlan(struct b53_device *dev, bool enable, b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5); } - mgmt &= ~SM_SW_FWD_MODE; - if (enable) { vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN; @@ -573,9 +571,8 @@ EXPORT_SYMBOL(b53_disable_port); void b53_brcm_hdr_setup(struct dsa_switch *ds, int port) { - bool tag_en = !(ds->ops->get_tag_protocol(ds, port) == - DSA_TAG_PROTO_NONE); struct b53_device *dev = ds->priv; + bool tag_en = !(dev->tag_protocol == DSA_TAG_PROTO_NONE); u8 hdr_ctl, val; u16 reg; @@ -595,6 +592,22 @@ void b53_brcm_hdr_setup(struct dsa_switch *ds, int port) break; } + /* Enable management mode if tagging is requested */ + b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &hdr_ctl); + if (tag_en) + hdr_ctl |= SM_SW_FWD_MODE; + else + hdr_ctl &= ~SM_SW_FWD_MODE; + b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, hdr_ctl); + + /* Configure the appropriate IMP port */ + b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &hdr_ctl); + if (port == 8) + hdr_ctl |= GC_FRM_MGMT_PORT_MII; + else if (port == 5) + hdr_ctl |= GC_FRM_MGMT_PORT_M; + b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, hdr_ctl); + /* Enable Broadcom tags for IMP port */ b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, &hdr_ctl); if (tag_en) @@ -1866,36 +1879,57 @@ static bool b53_possible_cpu_port(struct dsa_switch *ds, int port) return false; } -static bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port) +static bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port, + enum dsa_tag_protocol tag_protocol) { bool ret = b53_possible_cpu_port(ds, port); - if (!ret) + if (!ret) { dev_warn(ds->dev, "Port %d is not Broadcom tag capable\n", port); + return ret; + } + + switch (tag_protocol) { + case DSA_TAG_PROTO_BRCM: + case DSA_TAG_PROTO_BRCM_PREPEND: + dev_warn(ds->dev, + "Port %d is stacked to Broadcom tag switch\n", port); + ret = false; + break; + default: + ret = true; + break; + } + return ret; } -enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port) +enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mprot) { struct b53_device *dev = ds->priv; /* Older models (5325, 5365) support a different tag format that we do - * not support in net/dsa/tag_brcm.c yet. 539x and 531x5 require managed - * mode to be turned on which means we need to specifically manage ARL - * misses on multicast addresses (TBD). + * not support in net/dsa/tag_brcm.c yet. */ - if (is5325(dev) || is5365(dev) || is539x(dev) || is531x5(dev) || - !b53_can_enable_brcm_tags(ds, port)) - return DSA_TAG_PROTO_NONE; + if (is5325(dev) || is5365(dev) || + !b53_can_enable_brcm_tags(ds, port, mprot)) { + dev->tag_protocol = DSA_TAG_PROTO_NONE; + goto out; + } /* Broadcom BCM58xx chips have a flow accelerator on Port 8 * which requires us to use the prepended Broadcom tag type */ - if (dev->chip_id == BCM58XX_DEVICE_ID && port == B53_CPU_PORT) - return DSA_TAG_PROTO_BRCM_PREPEND; + if (dev->chip_id == BCM58XX_DEVICE_ID && port == B53_CPU_PORT) { + dev->tag_protocol = DSA_TAG_PROTO_BRCM_PREPEND; + goto out; + } - return DSA_TAG_PROTO_BRCM; + dev->tag_protocol = DSA_TAG_PROTO_BRCM; +out: + return dev->tag_protocol; } EXPORT_SYMBOL(b53_get_tag_protocol); diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 1877acf05081..3c30f3a7eb29 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -118,6 +118,7 @@ struct b53_device { u8 jumbo_size_reg; int reset_gpio; u8 num_arl_entries; + enum dsa_tag_protocol tag_protocol; /* used ports mask */ u16 enabled_ports; @@ -359,7 +360,8 @@ int b53_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb); int b53_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress); -enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port); +enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mprot); void b53_mirror_del(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror); int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index c8d7ef27fd72..fdcb70b9f0e4 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -61,7 +61,8 @@ struct dsa_loop_priv { static struct phy_device *phydevs[PHY_MAX_ADDR]; static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol mp) { dev_dbg(ds->dev, "%s: port: %d\n", __func__, port); diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index e3c333a8f45d..cc17a44dd3a8 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -883,7 +883,8 @@ static int lan9303_check_device(struct lan9303 *chip) /* ---------------------------- DSA -----------------------------------*/ static enum dsa_tag_protocol lan9303_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol mp) { return DSA_TAG_PROTO_LAN9303; } diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 955324968b74..0369c22fe3e1 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -841,7 +841,8 @@ static int gswip_setup(struct dsa_switch *ds) } static enum dsa_tag_protocol gswip_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol mp) { return DSA_TAG_PROTO_GSWIP; } diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 24a5e99f7fd5..47d65b77caf7 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -645,7 +645,8 @@ static void ksz8795_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) } static enum dsa_tag_protocol ksz8795_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol mp) { return DSA_TAG_PROTO_KSZ8795; } diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 50ffc63d6231..9a51b8a4de5d 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -295,7 +295,8 @@ static void ksz9477_port_init_cnt(struct ksz_device *dev, int port) } static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol mp) { enum dsa_tag_protocol proto = DSA_TAG_PROTO_KSZ9477; struct ksz_device *dev = ds->priv; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index ed1ec10ec62b..022466ca1c19 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1223,7 +1223,8 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port, } static enum dsa_tag_protocol -mtk_get_tag_protocol(struct dsa_switch *ds, int port) +mtk_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mp) { struct mt7530_priv *priv = ds->priv; diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index a5a37f47b320..24b8219fd607 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -43,7 +43,8 @@ static const char *mv88e6060_get_name(struct mii_bus *bus, int sw_addr) } static enum dsa_tag_protocol mv88e6060_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol m) { return DSA_TAG_PROTO_TRAILER; } diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3bd988529178..8c9289549688 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -340,11 +340,14 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip) */ irq_set_lockdep_class(chip->irq, &lock_key, &request_key); + snprintf(chip->irq_name, sizeof(chip->irq_name), + "mv88e6xxx-%s", dev_name(chip->dev)); + mv88e6xxx_reg_unlock(chip); err = request_threaded_irq(chip->irq, NULL, mv88e6xxx_g1_irq_thread_fn, IRQF_ONESHOT | IRQF_SHARED, - dev_name(chip->dev), chip); + chip->irq_name, chip); mv88e6xxx_reg_lock(chip); if (err) mv88e6xxx_g1_irq_free_common(chip); @@ -2303,10 +2306,14 @@ static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port, if (!irq) return 0; + snprintf(dev_id->serdes_irq_name, sizeof(dev_id->serdes_irq_name), + "mv88e6xxx-%s-serdes-%d", dev_name(chip->dev), port); + /* Requesting the IRQ will trigger IRQ callbacks, so release the lock */ mv88e6xxx_reg_unlock(chip); err = request_threaded_irq(irq, NULL, mv88e6xxx_serdes_irq_thread_fn, - IRQF_ONESHOT, "mv88e6xxx-serdes", dev_id); + IRQF_ONESHOT, dev_id->serdes_irq_name, + dev_id); mv88e6xxx_reg_lock(chip); if (err) return err; @@ -3838,6 +3845,9 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .phylink_validate = mv88e6390_phylink_validate, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390_phylink_validate, }; @@ -3889,6 +3899,9 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .phylink_validate = mv88e6390_phylink_validate, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390x_phylink_validate, }; @@ -3939,6 +3952,9 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .phylink_validate = mv88e6390_phylink_validate, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .phylink_validate = mv88e6390_phylink_validate, @@ -4085,6 +4101,9 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .phylink_validate = mv88e6390_phylink_validate, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -4424,6 +4443,9 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, .phylink_validate = mv88e6390_phylink_validate, }; @@ -4476,6 +4498,9 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -5207,7 +5232,8 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) } static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol m) { struct mv88e6xxx_chip *chip = ds->priv; diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 8a8e38bfb161..f332cb4b2fbf 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -236,6 +236,7 @@ struct mv88e6xxx_port { bool mirror_ingress; bool mirror_egress; unsigned int serdes_irq; + char serdes_irq_name[32]; }; struct mv88e6xxx_chip { @@ -292,11 +293,16 @@ struct mv88e6xxx_chip { struct mv88e6xxx_irq g1_irq; struct mv88e6xxx_irq g2_irq; int irq; + char irq_name[32]; int device_irq; + char device_irq_name[32]; int watchdog_irq; + char watchdog_irq_name[32]; int atu_prob_irq; + char atu_prob_irq_name[32]; int vtu_prob_irq; + char vtu_prob_irq_name[32]; struct kthread_worker *kworker; struct kthread_delayed_work irq_poll_work; diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index bdcd25560dd2..bac9a8a68e50 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -425,9 +425,12 @@ int mv88e6xxx_g1_atu_prob_irq_setup(struct mv88e6xxx_chip *chip) if (chip->atu_prob_irq < 0) return chip->atu_prob_irq; + snprintf(chip->atu_prob_irq_name, sizeof(chip->atu_prob_irq_name), + "mv88e6xxx-%s-g1-atu-prob", dev_name(chip->dev)); + err = request_threaded_irq(chip->atu_prob_irq, NULL, mv88e6xxx_g1_atu_prob_irq_thread_fn, - IRQF_ONESHOT, "mv88e6xxx-g1-atu-prob", + IRQF_ONESHOT, chip->atu_prob_irq_name, chip); if (err) irq_dispose_mapping(chip->atu_prob_irq); diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 33056a609e96..48390b7b18ad 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -631,9 +631,12 @@ int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip) if (chip->vtu_prob_irq < 0) return chip->vtu_prob_irq; + snprintf(chip->vtu_prob_irq_name, sizeof(chip->vtu_prob_irq_name), + "mv88e6xxx-%s-g1-vtu-prob", dev_name(chip->dev)); + err = request_threaded_irq(chip->vtu_prob_irq, NULL, mv88e6xxx_g1_vtu_prob_irq_thread_fn, - IRQF_ONESHOT, "mv88e6xxx-g1-vtu-prob", + IRQF_ONESHOT, chip->vtu_prob_irq_name, chip); if (err) irq_dispose_mapping(chip->vtu_prob_irq); diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 87bfe7c8c9cd..01503014b1ee 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -948,10 +948,13 @@ static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip) if (chip->watchdog_irq < 0) return chip->watchdog_irq; + snprintf(chip->watchdog_irq_name, sizeof(chip->watchdog_irq_name), + "mv88e6xxx-%s-watchdog", dev_name(chip->dev)); + err = request_threaded_irq(chip->watchdog_irq, NULL, mv88e6xxx_g2_watchdog_thread_fn, IRQF_ONESHOT | IRQF_TRIGGER_FALLING, - "mv88e6xxx-watchdog", chip); + chip->watchdog_irq_name, chip); if (err) return err; @@ -1114,9 +1117,12 @@ int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) goto out; } + snprintf(chip->device_irq_name, sizeof(chip->device_irq_name), + "mv88e6xxx-%s-g2", dev_name(chip->dev)); + err = request_threaded_irq(chip->device_irq, NULL, mv88e6xxx_g2_irq_thread_fn, - IRQF_ONESHOT, "mv88e6xxx-g2", chip); + IRQF_ONESHOT, chip->device_irq_name, chip); if (err) goto out; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 902feb398746..8d8b3b74aee1 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -405,22 +405,116 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane, return err; } +struct mv88e6390_serdes_hw_stat { + char string[ETH_GSTRING_LEN]; + int reg; +}; + +static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = { + { "serdes_rx_pkts", 0xf021 }, + { "serdes_rx_bytes", 0xf024 }, + { "serdes_rx_pkts_error", 0xf027 }, +}; + +int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) +{ + if (mv88e6390_serdes_get_lane(chip, port) == 0) + return 0; + + return ARRAY_SIZE(mv88e6390_serdes_hw_stats); +} + +int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, + int port, uint8_t *data) +{ + struct mv88e6390_serdes_hw_stat *stat; + int i; + + if (mv88e6390_serdes_get_lane(chip, port) == 0) + return 0; + + for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) { + stat = &mv88e6390_serdes_hw_stats[i]; + memcpy(data + i * ETH_GSTRING_LEN, stat->string, + ETH_GSTRING_LEN); + } + return ARRAY_SIZE(mv88e6390_serdes_hw_stats); +} + +static uint64_t mv88e6390_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane, + struct mv88e6390_serdes_hw_stat *stat) +{ + u16 reg[3]; + int err, i; + + for (i = 0; i < 3; i++) { + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + stat->reg + i, ®[i]); + if (err) { + dev_err(chip->dev, "failed to read statistic\n"); + return 0; + } + } + + return reg[0] | ((u64)reg[1] << 16) | ((u64)reg[2] << 32); +} + +int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) +{ + struct mv88e6390_serdes_hw_stat *stat; + int lane; + int i; + + lane = mv88e6390_serdes_get_lane(chip, port); + if (lane == 0) + return 0; + + for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) { + stat = &mv88e6390_serdes_hw_stats[i]; + data[i] = mv88e6390_serdes_get_stat(chip, lane, stat); + } + + return ARRAY_SIZE(mv88e6390_serdes_hw_stats); +} + +static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, u8 lane) +{ + u16 reg; + int err; + + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PG_CONTROL, ®); + if (err) + return err; + + reg |= MV88E6390_PG_CONTROL_ENABLE_PC; + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PG_CONTROL, reg); +} + int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, bool up) { u8 cmode = chip->ports[port].cmode; + int err = 0; switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: case MV88E6XXX_PORT_STS_CMODE_1000BASEX: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_power_sgmii(chip, lane, up); + err = mv88e6390_serdes_power_sgmii(chip, lane, up); + break; case MV88E6XXX_PORT_STS_CMODE_XAUI: case MV88E6XXX_PORT_STS_CMODE_RXAUI: - return mv88e6390_serdes_power_10g(chip, lane, up); + err = mv88e6390_serdes_power_10g(chip, lane, up); + break; } - return 0; + if (!err && up) + err = mv88e6390_serdes_enable_checker(chip, lane); + + return err; } static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index bd8df36ab537..d16ef4da20b0 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -74,6 +74,10 @@ #define MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID BIT(11) #define MV88E6390_SGMII_PHY_STATUS_LINK BIT(10) +/* Packet generator pad packet checker */ +#define MV88E6390_PG_CONTROL 0xf010 +#define MV88E6390_PG_CONTROL_ENABLE_PC BIT(0) + u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); @@ -99,6 +103,11 @@ int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t *data); int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data); +int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, + int port, uint8_t *data); +int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data); /* Return the (first) SERDES lane address a port is using, 0 otherwise. */ static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig index 6f9804093150..a5b7cca03d09 100644 --- a/drivers/net/dsa/ocelot/Kconfig +++ b/drivers/net/dsa/ocelot/Kconfig @@ -3,8 +3,10 @@ config NET_DSA_MSCC_FELIX tristate "Ocelot / Felix Ethernet switch support" depends on NET_DSA && PCI depends on NET_VENDOR_MICROSEMI + depends on NET_VENDOR_FREESCALE select MSCC_OCELOT_SWITCH select NET_DSA_TAG_OCELOT + select FSL_ENETC_MDIO help This driver supports the VSC9959 network switch, which is a member of the Vitesse / Microsemi / Microchip Ocelot family of switching cores. diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index b7f92464815d..3257962c147e 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -2,16 +2,22 @@ /* Copyright 2019 NXP Semiconductors */ #include <uapi/linux/if_bridge.h> +#include <soc/mscc/ocelot_qsys.h> +#include <soc/mscc/ocelot_sys.h> +#include <soc/mscc/ocelot_dev.h> +#include <soc/mscc/ocelot_ana.h> #include <soc/mscc/ocelot.h> #include <linux/packing.h> #include <linux/module.h> +#include <linux/of_net.h> #include <linux/pci.h> #include <linux/of.h> #include <net/dsa.h> #include "felix.h" static enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol mp) { return DSA_TAG_PROTO_OCELOT; } @@ -26,14 +32,6 @@ static int felix_set_ageing_time(struct dsa_switch *ds, return 0; } -static void felix_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) -{ - struct ocelot *ocelot = ds->priv; - - ocelot_adjust_link(ocelot, port, phydev); -} - static int felix_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data) { @@ -155,6 +153,141 @@ static void felix_port_disable(struct dsa_switch *ds, int port) return ocelot_port_disable(ocelot, port); } +static void felix_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct ocelot *ocelot = ds->priv; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != ocelot_port->phy_mode) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + } + + /* No half-duplex. */ + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Full); + phylink_set(mask, 1000baseT_Full); + + /* The internal ports that run at 2.5G are overclocked GMII */ + if (state->interface == PHY_INTERFACE_MODE_GMII || + state->interface == PHY_INTERFACE_MODE_2500BASEX || + state->interface == PHY_INTERFACE_MODE_USXGMII) { + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 2500baseX_Full); + } + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->info->pcs_link_state) + felix->info->pcs_link_state(ocelot, port, state); + + return 0; +} + +static void felix_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int link_an_mode, + const struct phylink_link_state *state) +{ + struct ocelot *ocelot = ds->priv; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct felix *felix = ocelot_to_felix(ocelot); + u32 mac_fc_cfg; + + /* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and + * PORT_RST bits in CLOCK_CFG + */ + ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(state->speed), + DEV_CLOCK_CFG); + + /* Flow control. Link speed is only used here to evaluate the time + * specification in incoming pause frames. + */ + mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(state->speed); + + /* handle Rx pause in all cases, with 2500base-X this is used for rate + * adaptation. + */ + mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA; + + if (state->pause & MLO_PAUSE_TX) + mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA | + SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | + SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | + SYS_MAC_FC_CFG_ZERO_PAUSE_ENA; + ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port); + + ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port); + + if (felix->info->pcs_init) + felix->info->pcs_init(ocelot, port, link_an_mode, state); +} + +static void felix_phylink_mac_an_restart(struct dsa_switch *ds, int port) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->info->pcs_an_restart) + felix->info->pcs_an_restart(ocelot, port); +} + +static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int link_an_mode, + phy_interface_t interface) +{ + struct ocelot *ocelot = ds->priv; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG); + ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA, + QSYS_SWITCH_PORT_MODE, port); +} + +static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int link_an_mode, + phy_interface_t interface, + struct phy_device *phydev) +{ + struct ocelot *ocelot = ds->priv; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + /* Enable MAC module */ + ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA | + DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG); + + /* Enable receiving frames on the port, and activate auto-learning of + * MAC addresses. + */ + ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO | + ANA_PORT_PORT_CFG_RECV_ENA | + ANA_PORT_PORT_CFG_PORTID_VAL(port), + ANA_PORT_PORT_CFG, port); + + /* Core: Enable port for frame transfer */ + ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | + QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) | + QSYS_SWITCH_PORT_MODE_PORT_ENA, + QSYS_SWITCH_PORT_MODE, port); +} + static void felix_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data) { @@ -185,10 +318,76 @@ static int felix_get_ts_info(struct dsa_switch *ds, int port, return ocelot_get_ts_info(ocelot, port, info); } +static int felix_parse_ports_node(struct felix *felix, + struct device_node *ports_node, + phy_interface_t *port_phy_modes) +{ + struct ocelot *ocelot = &felix->ocelot; + struct device *dev = felix->ocelot.dev; + struct device_node *child; + + for_each_available_child_of_node(ports_node, child) { + phy_interface_t phy_mode; + u32 port; + int err; + + /* Get switch port number from DT */ + if (of_property_read_u32(child, "reg", &port) < 0) { + dev_err(dev, "Port number not defined in device tree " + "(property \"reg\")\n"); + of_node_put(child); + return -ENODEV; + } + + /* Get PHY mode from DT */ + err = of_get_phy_mode(child, &phy_mode); + if (err) { + dev_err(dev, "Failed to read phy-mode or " + "phy-interface-type property for port %d\n", + port); + of_node_put(child); + return -ENODEV; + } + + err = felix->info->prevalidate_phy_mode(ocelot, port, phy_mode); + if (err < 0) { + dev_err(dev, "Unsupported PHY mode %s on port %d\n", + phy_modes(phy_mode), port); + return err; + } + + port_phy_modes[port] = phy_mode; + } + + return 0; +} + +static int felix_parse_dt(struct felix *felix, phy_interface_t *port_phy_modes) +{ + struct device *dev = felix->ocelot.dev; + struct device_node *switch_node; + struct device_node *ports_node; + int err; + + switch_node = dev->of_node; + + ports_node = of_get_child_by_name(switch_node, "ports"); + if (!ports_node) { + dev_err(dev, "Incorrect bindings: absent \"ports\" node\n"); + return -ENODEV; + } + + err = felix_parse_ports_node(felix, ports_node, port_phy_modes); + of_node_put(ports_node); + + return err; +} + static int felix_init_structs(struct felix *felix, int num_phys_ports) { struct ocelot *ocelot = &felix->ocelot; - resource_size_t base; + phy_interface_t *port_phy_modes; + resource_size_t switch_base; int port, i, err; ocelot->num_phys_ports = num_phys_ports; @@ -203,7 +402,19 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) ocelot->shared_queue_sz = felix->info->shared_queue_sz; ocelot->ops = felix->info->ops; - base = pci_resource_start(felix->pdev, felix->info->pci_bar); + port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t), + GFP_KERNEL); + if (!port_phy_modes) + return -ENOMEM; + + err = felix_parse_dt(felix, port_phy_modes); + if (err) { + kfree(port_phy_modes); + return err; + } + + switch_base = pci_resource_start(felix->pdev, + felix->info->switch_pci_bar); for (i = 0; i < TARGET_MAX; i++) { struct regmap *target; @@ -214,13 +425,14 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) res = &felix->info->target_io_res[i]; res->flags = IORESOURCE_MEM; - res->start += base; - res->end += base; + res->start += switch_base; + res->end += switch_base; target = ocelot_regmap_init(ocelot, res); if (IS_ERR(target)) { dev_err(ocelot->dev, "Failed to map device memory space\n"); + kfree(port_phy_modes); return PTR_ERR(target); } @@ -230,6 +442,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) err = ocelot_regfields_init(ocelot, felix->info->regfields); if (err) { dev_err(ocelot->dev, "failed to init reg fields map\n"); + kfree(port_phy_modes); return err; } @@ -244,26 +457,37 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) if (!ocelot_port) { dev_err(ocelot->dev, "failed to allocate port memory\n"); + kfree(port_phy_modes); return -ENOMEM; } res = &felix->info->port_io_res[port]; res->flags = IORESOURCE_MEM; - res->start += base; - res->end += base; + res->start += switch_base; + res->end += switch_base; port_regs = devm_ioremap_resource(ocelot->dev, res); if (IS_ERR(port_regs)) { dev_err(ocelot->dev, "failed to map registers for port %d\n", port); + kfree(port_phy_modes); return PTR_ERR(port_regs); } + ocelot_port->phy_mode = port_phy_modes[port]; ocelot_port->ocelot = ocelot; ocelot_port->regs = port_regs; ocelot->ports[port] = ocelot_port; } + kfree(port_phy_modes); + + if (felix->info->mdio_bus_alloc) { + err = felix->info->mdio_bus_alloc(ocelot); + if (err < 0) + return err; + } + return 0; } @@ -293,12 +517,22 @@ static int felix_setup(struct dsa_switch *ds) OCELOT_TAG_PREFIX_LONG); } + /* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040) + * isn't instantiated for the Felix PF. + * In-band AN may take a few ms to complete, so we need to poll. + */ + ds->pcs_poll = true; + return 0; } static void felix_teardown(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->info->mdio_bus_free) + felix->info->mdio_bus_free(ocelot); /* stop workqueue thread */ ocelot_deinit(ocelot); @@ -369,7 +603,12 @@ static const struct dsa_switch_ops felix_switch_ops = { .get_ethtool_stats = felix_get_ethtool_stats, .get_sset_count = felix_get_sset_count, .get_ts_info = felix_get_ts_info, - .adjust_link = felix_adjust_link, + .phylink_validate = felix_phylink_validate, + .phylink_mac_link_state = felix_phylink_mac_pcs_get_state, + .phylink_mac_config = felix_phylink_mac_config, + .phylink_mac_an_restart = felix_phylink_mac_an_restart, + .phylink_mac_link_down = felix_phylink_mac_link_down, + .phylink_mac_link_up = felix_phylink_mac_link_up, .port_enable = felix_port_enable, .port_disable = felix_port_disable, .port_fdb_dump = felix_fdb_dump, diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 204296e51d0c..3a7580015b62 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -10,6 +10,7 @@ struct felix_info { struct resource *target_io_res; struct resource *port_io_res; + struct resource *imdio_res; const struct reg_field *regfields; const u32 *const *map; const struct ocelot_ops *ops; @@ -17,7 +18,18 @@ struct felix_info { const struct ocelot_stat_layout *stats_layout; unsigned int num_stats; int num_ports; - int pci_bar; + int switch_pci_bar; + int imdio_pci_bar; + int (*mdio_bus_alloc)(struct ocelot *ocelot); + void (*mdio_bus_free)(struct ocelot *ocelot); + void (*pcs_init)(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + const struct phylink_link_state *state); + void (*pcs_an_restart)(struct ocelot *ocelot, int port); + void (*pcs_link_state)(struct ocelot *ocelot, int port, + struct phylink_link_state *state); + int (*prevalidate_phy_mode)(struct ocelot *ocelot, int port, + phy_interface_t phy_mode); }; extern struct felix_info felix_info_vsc9959; @@ -32,6 +44,8 @@ struct felix { struct pci_dev *pdev; struct felix_info *info; struct ocelot ocelot; + struct mii_bus *imdio; + struct phy_device **pcs; }; #endif diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index b9758b0d18c7..2c812b481778 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -2,12 +2,33 @@ /* Copyright 2017 Microsemi Corporation * Copyright 2018-2019 NXP Semiconductors */ +#include <linux/fsl/enetc_mdio.h> #include <soc/mscc/ocelot_sys.h> #include <soc/mscc/ocelot.h> #include <linux/iopoll.h> #include <linux/pci.h> #include "felix.h" +/* TODO: should find a better place for these */ +#define USXGMII_BMCR_RESET BIT(15) +#define USXGMII_BMCR_AN_EN BIT(12) +#define USXGMII_BMCR_RST_AN BIT(9) +#define USXGMII_BMSR_LNKS(status) (((status) & GENMASK(2, 2)) >> 2) +#define USXGMII_BMSR_AN_CMPL(status) (((status) & GENMASK(5, 5)) >> 5) +#define USXGMII_ADVERTISE_LNKS(x) (((x) << 15) & BIT(15)) +#define USXGMII_ADVERTISE_FDX BIT(12) +#define USXGMII_ADVERTISE_SPEED(x) (((x) << 9) & GENMASK(11, 9)) +#define USXGMII_LPA_LNKS(lpa) ((lpa) >> 15) +#define USXGMII_LPA_DUPLEX(lpa) (((lpa) & GENMASK(12, 12)) >> 12) +#define USXGMII_LPA_SPEED(lpa) (((lpa) & GENMASK(11, 9)) >> 9) + +enum usxgmii_speed { + USXGMII_SPEED_10 = 0, + USXGMII_SPEED_100 = 1, + USXGMII_SPEED_1000 = 2, + USXGMII_SPEED_2500 = 4, +}; + static const u32 vsc9959_ana_regmap[] = { REG(ANA_ADVLEARN, 0x0089a0), REG(ANA_VLANMASK, 0x0089a4), @@ -386,6 +407,15 @@ static struct resource vsc9959_port_io_res[] = { }, }; +/* Port MAC 0 Internal MDIO bus through which the SerDes acting as an + * SGMII/QSGMII MAC PCS can be found. + */ +static struct resource vsc9959_imdio_res = { + .start = 0x8030, + .end = 0x8040, + .name = "imdio", +}; + static const struct reg_field vsc9959_regfields[] = { [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6), [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 5), @@ -565,13 +595,495 @@ static int vsc9959_reset(struct ocelot *ocelot) return 0; } +static void vsc9959_pcs_an_restart_sgmii(struct phy_device *pcs) +{ + phy_set_bits(pcs, MII_BMCR, BMCR_ANRESTART); +} + +static void vsc9959_pcs_an_restart_usxgmii(struct phy_device *pcs) +{ + phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_BMCR, + USXGMII_BMCR_RESET | + USXGMII_BMCR_AN_EN | + USXGMII_BMCR_RST_AN); +} + +static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port) +{ + struct felix *felix = ocelot_to_felix(ocelot); + struct phy_device *pcs = felix->pcs[port]; + + if (!pcs) + return; + + switch (pcs->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + vsc9959_pcs_an_restart_sgmii(pcs); + break; + case PHY_INTERFACE_MODE_USXGMII: + vsc9959_pcs_an_restart_usxgmii(pcs); + break; + default: + dev_err(ocelot->dev, "Invalid PCS interface type %s\n", + phy_modes(pcs->interface)); + break; + } +} + +/* We enable SGMII AN only when the PHY has managed = "in-band-status" in the + * device tree. If we are in MLO_AN_PHY mode, we program directly state->speed + * into the PCS, which is retrieved out-of-band over MDIO. This also has the + * benefit of working with SGMII fixed-links, like downstream switches, where + * both link partners attempt to operate as AN slaves and therefore AN never + * completes. But it also has the disadvantage that some PHY chips don't pass + * traffic if SGMII AN is enabled but not completed (acknowledged by us), so + * setting MLO_AN_INBAND is actually required for those. + */ +static void vsc9959_pcs_init_sgmii(struct phy_device *pcs, + unsigned int link_an_mode, + const struct phylink_link_state *state) +{ + if (link_an_mode == MLO_AN_INBAND) { + int bmsr, bmcr; + + /* Some PHYs like VSC8234 don't like it when AN restarts on + * their system side and they restart line side AN too, going + * into an endless link up/down loop. Don't restart PCS AN if + * link is up already. + * We do check that AN is enabled just in case this is the 1st + * call, PCS detects a carrier but AN is disabled from power on + * or by boot loader. + */ + bmcr = phy_read(pcs, MII_BMCR); + if (bmcr < 0) + return; + + bmsr = phy_read(pcs, MII_BMSR); + if (bmsr < 0) + return; + + if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_LSTATUS)) + return; + + /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001 + * for the MAC PCS in order to acknowledge the AN. + */ + phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | + ADVERTISE_LPACK); + + phy_write(pcs, ENETC_PCS_IF_MODE, + ENETC_PCS_IF_MODE_SGMII_EN | + ENETC_PCS_IF_MODE_USE_SGMII_AN); + + /* Adjust link timer for SGMII */ + phy_write(pcs, ENETC_PCS_LINK_TIMER1, + ENETC_PCS_LINK_TIMER1_VAL); + phy_write(pcs, ENETC_PCS_LINK_TIMER2, + ENETC_PCS_LINK_TIMER2_VAL); + + phy_write(pcs, MII_BMCR, BMCR_ANRESTART | BMCR_ANENABLE); + } else { + int speed; + + if (state->duplex == DUPLEX_HALF) { + phydev_err(pcs, "Half duplex not supported\n"); + return; + } + switch (state->speed) { + case SPEED_1000: + speed = ENETC_PCS_SPEED_1000; + break; + case SPEED_100: + speed = ENETC_PCS_SPEED_100; + break; + case SPEED_10: + speed = ENETC_PCS_SPEED_10; + break; + case SPEED_UNKNOWN: + /* Silently don't do anything */ + return; + default: + phydev_err(pcs, "Invalid PCS speed %d\n", state->speed); + return; + } + + phy_write(pcs, ENETC_PCS_IF_MODE, + ENETC_PCS_IF_MODE_SGMII_EN | + ENETC_PCS_IF_MODE_SGMII_SPEED(speed)); + + /* Yes, not a mistake: speed is given by IF_MODE. */ + phy_write(pcs, MII_BMCR, BMCR_RESET | + BMCR_SPEED1000 | + BMCR_FULLDPLX); + } +} + +/* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane + * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have + * auto-negotiation of any link parameters. Electrically it is compatible with + * a single lane of XAUI. + * The hardware reference manual wants to call this mode SGMII, but it isn't + * really, since the fundamental features of SGMII: + * - Downgrading the link speed by duplicating symbols + * - Auto-negotiation + * are not there. + * The speed is configured at 1000 in the IF_MODE and BMCR MDIO registers + * because the clock frequency is actually given by a PLL configured in the + * Reset Configuration Word (RCW). + * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o + * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a + * lower link speed on line side, the system-side interface remains fixed at + * 2500 Mbps and we do rate adaptation through pause frames. + */ +static void vsc9959_pcs_init_2500basex(struct phy_device *pcs, + unsigned int link_an_mode, + const struct phylink_link_state *state) +{ + if (link_an_mode == MLO_AN_INBAND) { + phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n"); + return; + } + + phy_write(pcs, ENETC_PCS_IF_MODE, + ENETC_PCS_IF_MODE_SGMII_EN | + ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500)); + + phy_write(pcs, MII_BMCR, BMCR_SPEED1000 | + BMCR_FULLDPLX | + BMCR_RESET); +} + +static void vsc9959_pcs_init_usxgmii(struct phy_device *pcs, + unsigned int link_an_mode, + const struct phylink_link_state *state) +{ + if (link_an_mode != MLO_AN_INBAND) { + phydev_err(pcs, "USXGMII only supports in-band AN for now\n"); + return; + } + + /* Configure device ability for the USXGMII Replicator */ + phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE, + USXGMII_ADVERTISE_SPEED(USXGMII_SPEED_2500) | + USXGMII_ADVERTISE_LNKS(1) | + ADVERTISE_SGMII | + ADVERTISE_LPACK | + USXGMII_ADVERTISE_FDX); +} + +static void vsc9959_pcs_init(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + const struct phylink_link_state *state) +{ + struct felix *felix = ocelot_to_felix(ocelot); + struct phy_device *pcs = felix->pcs[port]; + + if (!pcs) + return; + + /* The PCS does not implement the BMSR register fully, so capability + * detection via genphy_read_abilities does not work. Since we can get + * the PHY config word from the LPA register though, there is still + * value in using the generic phy_resolve_aneg_linkmode function. So + * populate the supported and advertising link modes manually here. + */ + linkmode_set_bit_array(phy_basic_ports_array, + ARRAY_SIZE(phy_basic_ports_array), + pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported); + if (pcs->interface == PHY_INTERFACE_MODE_2500BASEX || + pcs->interface == PHY_INTERFACE_MODE_USXGMII) + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + pcs->supported); + if (pcs->interface != PHY_INTERFACE_MODE_2500BASEX) + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + pcs->supported); + phy_advertise_supported(pcs); + + switch (pcs->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + vsc9959_pcs_init_sgmii(pcs, link_an_mode, state); + break; + case PHY_INTERFACE_MODE_2500BASEX: + vsc9959_pcs_init_2500basex(pcs, link_an_mode, state); + break; + case PHY_INTERFACE_MODE_USXGMII: + vsc9959_pcs_init_usxgmii(pcs, link_an_mode, state); + break; + default: + dev_err(ocelot->dev, "Unsupported link mode %s\n", + phy_modes(pcs->interface)); + } +} + +static void vsc9959_pcs_link_state_resolve(struct phy_device *pcs, + struct phylink_link_state *state) +{ + state->an_complete = pcs->autoneg_complete; + state->an_enabled = pcs->autoneg; + state->link = pcs->link; + state->duplex = pcs->duplex; + state->speed = pcs->speed; + /* SGMII AN does not negotiate flow control, but that's ok, + * since phylink already knows that, and does: + * link_state.pause |= pl->phy_state.pause; + */ + state->pause = MLO_PAUSE_NONE; + + phydev_dbg(pcs, + "mode=%s/%s/%s adv=%*pb lpa=%*pb link=%u an_enabled=%u an_complete=%u\n", + phy_modes(pcs->interface), + phy_speed_to_str(pcs->speed), + phy_duplex_to_str(pcs->duplex), + __ETHTOOL_LINK_MODE_MASK_NBITS, pcs->advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS, pcs->lp_advertising, + pcs->link, pcs->autoneg, pcs->autoneg_complete); +} + +static void vsc9959_pcs_link_state_sgmii(struct phy_device *pcs, + struct phylink_link_state *state) +{ + int err; + + err = genphy_update_link(pcs); + if (err < 0) + return; + + if (pcs->autoneg_complete) { + u16 lpa = phy_read(pcs, MII_LPA); + + mii_lpa_to_linkmode_lpa_sgmii(pcs->lp_advertising, lpa); + + phy_resolve_aneg_linkmode(pcs); + } +} + +static void vsc9959_pcs_link_state_2500basex(struct phy_device *pcs, + struct phylink_link_state *state) +{ + int err; + + err = genphy_update_link(pcs); + if (err < 0) + return; + + pcs->speed = SPEED_2500; + pcs->asym_pause = true; + pcs->pause = true; +} + +static void vsc9959_pcs_link_state_usxgmii(struct phy_device *pcs, + struct phylink_link_state *state) +{ + int status, lpa; + + status = phy_read_mmd(pcs, MDIO_MMD_VEND2, MII_BMSR); + if (status < 0) + return; + + pcs->autoneg = true; + pcs->autoneg_complete = USXGMII_BMSR_AN_CMPL(status); + pcs->link = USXGMII_BMSR_LNKS(status); + + if (!pcs->link || !pcs->autoneg_complete) + return; + + lpa = phy_read_mmd(pcs, MDIO_MMD_VEND2, MII_LPA); + if (lpa < 0) + return; + + switch (USXGMII_LPA_SPEED(lpa)) { + case USXGMII_SPEED_10: + pcs->speed = SPEED_10; + break; + case USXGMII_SPEED_100: + pcs->speed = SPEED_100; + break; + case USXGMII_SPEED_1000: + pcs->speed = SPEED_1000; + break; + case USXGMII_SPEED_2500: + pcs->speed = SPEED_2500; + break; + default: + break; + } + + if (USXGMII_LPA_DUPLEX(lpa)) + pcs->duplex = DUPLEX_FULL; + else + pcs->duplex = DUPLEX_HALF; +} + +static void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, + struct phylink_link_state *state) +{ + struct felix *felix = ocelot_to_felix(ocelot); + struct phy_device *pcs = felix->pcs[port]; + + if (!pcs) + return; + + pcs->speed = SPEED_UNKNOWN; + pcs->duplex = DUPLEX_UNKNOWN; + pcs->pause = 0; + pcs->asym_pause = 0; + + switch (pcs->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + vsc9959_pcs_link_state_sgmii(pcs, state); + break; + case PHY_INTERFACE_MODE_2500BASEX: + vsc9959_pcs_link_state_2500basex(pcs, state); + break; + case PHY_INTERFACE_MODE_USXGMII: + vsc9959_pcs_link_state_usxgmii(pcs, state); + break; + default: + return; + } + + vsc9959_pcs_link_state_resolve(pcs, state); +} + +static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port, + phy_interface_t phy_mode) +{ + switch (phy_mode) { + case PHY_INTERFACE_MODE_GMII: + /* Only supported on internal to-CPU ports */ + if (port != 4 && port != 5) + return -ENOTSUPP; + return 0; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_2500BASEX: + /* Not supported on internal to-CPU ports */ + if (port == 4 || port == 5) + return -ENOTSUPP; + return 0; + default: + return -ENOTSUPP; + } +} + static const struct ocelot_ops vsc9959_ops = { .reset = vsc9959_reset, }; +static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) +{ + struct felix *felix = ocelot_to_felix(ocelot); + struct enetc_mdio_priv *mdio_priv; + struct device *dev = ocelot->dev; + resource_size_t imdio_base; + void __iomem *imdio_regs; + struct resource *res; + struct enetc_hw *hw; + struct mii_bus *bus; + int port; + int rc; + + felix->pcs = devm_kcalloc(dev, felix->info->num_ports, + sizeof(struct phy_device *), + GFP_KERNEL); + if (!felix->pcs) { + dev_err(dev, "failed to allocate array for PCS PHYs\n"); + return -ENOMEM; + } + + imdio_base = pci_resource_start(felix->pdev, + felix->info->imdio_pci_bar); + + res = felix->info->imdio_res; + res->flags = IORESOURCE_MEM; + res->start += imdio_base; + res->end += imdio_base; + + imdio_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(imdio_regs)) { + dev_err(dev, "failed to map internal MDIO registers\n"); + return PTR_ERR(imdio_regs); + } + + hw = enetc_hw_alloc(dev, imdio_regs); + if (IS_ERR(hw)) { + dev_err(dev, "failed to allocate ENETC HW structure\n"); + return PTR_ERR(hw); + } + + bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); + if (!bus) + return -ENOMEM; + + bus->name = "VSC9959 internal MDIO bus"; + bus->read = enetc_mdio_read; + bus->write = enetc_mdio_write; + bus->parent = dev; + mdio_priv = bus->priv; + mdio_priv->hw = hw; + /* This gets added to imdio_regs, which already maps addresses + * starting with the proper offset. + */ + mdio_priv->mdio_base = 0; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev)); + + /* Needed in order to initialize the bus mutex lock */ + rc = mdiobus_register(bus); + if (rc < 0) { + dev_err(dev, "failed to register MDIO bus\n"); + return rc; + } + + felix->imdio = bus; + + for (port = 0; port < felix->info->num_ports; port++) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct phy_device *pcs; + bool is_c45 = false; + + if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_USXGMII) + is_c45 = true; + + pcs = get_phy_device(felix->imdio, port, is_c45); + if (IS_ERR(pcs)) + continue; + + pcs->interface = ocelot_port->phy_mode; + felix->pcs[port] = pcs; + + dev_info(dev, "Found PCS at internal MDIO address %d\n", port); + } + + return 0; +} + +static void vsc9959_mdio_bus_free(struct ocelot *ocelot) +{ + struct felix *felix = ocelot_to_felix(ocelot); + int port; + + for (port = 0; port < ocelot->num_phys_ports; port++) { + struct phy_device *pcs = felix->pcs[port]; + + if (!pcs) + continue; + + put_device(&pcs->mdio.dev); + } + mdiobus_unregister(felix->imdio); +} + struct felix_info felix_info_vsc9959 = { .target_io_res = vsc9959_target_io_res, .port_io_res = vsc9959_port_io_res, + .imdio_res = &vsc9959_imdio_res, .regfields = vsc9959_regfields, .map = vsc9959_regmap, .ops = &vsc9959_ops, @@ -579,5 +1091,12 @@ struct felix_info felix_info_vsc9959 = { .num_stats = ARRAY_SIZE(vsc9959_stats_layout), .shared_queue_sz = 128 * 1024, .num_ports = 6, - .pci_bar = 4, + .switch_pci_bar = 4, + .imdio_pci_bar = 0, + .mdio_bus_alloc = vsc9959_mdio_bus_alloc, + .mdio_bus_free = vsc9959_mdio_bus_free, + .pcs_init = vsc9959_pcs_init, + .pcs_an_restart = vsc9959_pcs_an_restart, + .pcs_link_state = vsc9959_pcs_link_state, + .prevalidate_phy_mode = vsc9959_prevalidate_phy_mode, }; diff --git a/drivers/net/dsa/qca/Kconfig b/drivers/net/dsa/qca/Kconfig new file mode 100644 index 000000000000..e3c8d715a18f --- /dev/null +++ b/drivers/net/dsa/qca/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_DSA_AR9331 + tristate "Qualcomm Atheros AR9331 Ethernet switch support" + depends on NET_DSA + select NET_DSA_TAG_AR9331 + select REGMAP + ---help--- + This enables support for the Qualcomm Atheros AR9331 built-in Ethernet + switch. diff --git a/drivers/net/dsa/qca/Makefile b/drivers/net/dsa/qca/Makefile new file mode 100644 index 000000000000..274022319066 --- /dev/null +++ b/drivers/net/dsa/qca/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c new file mode 100644 index 000000000000..de25f99e995a --- /dev/null +++ b/drivers/net/dsa/qca/ar9331.c @@ -0,0 +1,856 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2019 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> +/* + * +----------------------+ + * GMAC1----RGMII----|--MAC0 | + * \---MDIO1----|--REGs |----MDIO3----\ + * | | | +------+ + * | | +--| | + * | MAC1-|----RMII--M-----| PHY0 |-o P0 + * | | | | +------+ + * | | | +--| | + * | MAC2-|----RMII--------| PHY1 |-o P1 + * | | | | +------+ + * | | | +--| | + * | MAC3-|----RMII--------| PHY2 |-o P2 + * | | | | +------+ + * | | | +--| | + * | MAC4-|----RMII--------| PHY3 |-o P3 + * | | | | +------+ + * | | | +--| | + * | MAC5-|--+-RMII--M-----|-PHY4-|-o P4 + * | | | | +------+ + * +----------------------+ | \--CFG_SW_PHY_SWAP + * GMAC0---------------RMII--------------------/ \-CFG_SW_PHY_ADDR_SWAP + * \---MDIO0--NC + * + * GMAC0 and MAC5 are connected together and use same PHY. Depending on + * configuration it can be PHY4 (default) or PHY0. Only GMAC0 or MAC5 can be + * used at same time. If GMAC0 is used (default) then MAC5 should be disabled. + * + * CFG_SW_PHY_SWAP - swap connections of PHY0 and PHY4. If this bit is not set + * PHY4 is connected to GMAC0/MAC5 bundle and PHY0 is connected to MAC1. If this + * bit is set, PHY4 is connected to MAC1 and PHY0 is connected to GMAC0/MAC5 + * bundle. + * + * CFG_SW_PHY_ADDR_SWAP - swap addresses of PHY0 and PHY4 + * + * CFG_SW_PHY_SWAP and CFG_SW_PHY_ADDR_SWAP are part of SoC specific register + * set and not related to switch internal registers. + */ + +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/of_mdio.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <net/dsa.h> + +#define AR9331_SW_NAME "ar9331_switch" +#define AR9331_SW_PORTS 6 + +/* dummy reg to change page */ +#define AR9331_SW_REG_PAGE 0x40000 + +/* Global Interrupt */ +#define AR9331_SW_REG_GINT 0x10 +#define AR9331_SW_REG_GINT_MASK 0x14 +#define AR9331_SW_GINT_PHY_INT BIT(2) + +#define AR9331_SW_REG_FLOOD_MASK 0x2c +#define AR9331_SW_FLOOD_MASK_BROAD_TO_CPU BIT(26) + +#define AR9331_SW_REG_GLOBAL_CTRL 0x30 +#define AR9331_SW_GLOBAL_CTRL_MFS_M GENMASK(13, 0) + +#define AR9331_SW_REG_MDIO_CTRL 0x98 +#define AR9331_SW_MDIO_CTRL_BUSY BIT(31) +#define AR9331_SW_MDIO_CTRL_MASTER_EN BIT(30) +#define AR9331_SW_MDIO_CTRL_CMD_READ BIT(27) +#define AR9331_SW_MDIO_CTRL_PHY_ADDR_M GENMASK(25, 21) +#define AR9331_SW_MDIO_CTRL_REG_ADDR_M GENMASK(20, 16) +#define AR9331_SW_MDIO_CTRL_DATA_M GENMASK(16, 0) + +#define AR9331_SW_REG_PORT_STATUS(_port) (0x100 + (_port) * 0x100) + +/* FLOW_LINK_EN - enable mac flow control config auto-neg with phy. + * If not set, mac can be config by software. + */ +#define AR9331_SW_PORT_STATUS_FLOW_LINK_EN BIT(12) + +/* LINK_EN - If set, MAC is configured from PHY link status. + * If not set, MAC should be configured by software. + */ +#define AR9331_SW_PORT_STATUS_LINK_EN BIT(9) +#define AR9331_SW_PORT_STATUS_DUPLEX_MODE BIT(6) +#define AR9331_SW_PORT_STATUS_RX_FLOW_EN BIT(5) +#define AR9331_SW_PORT_STATUS_TX_FLOW_EN BIT(4) +#define AR9331_SW_PORT_STATUS_RXMAC BIT(3) +#define AR9331_SW_PORT_STATUS_TXMAC BIT(2) +#define AR9331_SW_PORT_STATUS_SPEED_M GENMASK(1, 0) +#define AR9331_SW_PORT_STATUS_SPEED_1000 2 +#define AR9331_SW_PORT_STATUS_SPEED_100 1 +#define AR9331_SW_PORT_STATUS_SPEED_10 0 + +#define AR9331_SW_PORT_STATUS_MAC_MASK \ + (AR9331_SW_PORT_STATUS_TXMAC | AR9331_SW_PORT_STATUS_RXMAC) + +#define AR9331_SW_PORT_STATUS_LINK_MASK \ + (AR9331_SW_PORT_STATUS_LINK_EN | AR9331_SW_PORT_STATUS_FLOW_LINK_EN | \ + AR9331_SW_PORT_STATUS_DUPLEX_MODE | \ + AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \ + AR9331_SW_PORT_STATUS_SPEED_M) + +/* Phy bypass mode + * ------------------------------------------------------------------------ + * Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 | + * + * real | start | OP | PhyAddr | Reg Addr | TA | + * atheros| start | OP | 2'b00 |PhyAdd[2:0]| Reg Addr[4:0] | TA | + * + * + * Bit: |16 |17 |18 |19 |20 |21 |22 |23 |24 |25 |26 |27 |28 |29 |30 |31 | + * real | Data | + * atheros| Data | + * + * ------------------------------------------------------------------------ + * Page address mode + * ------------------------------------------------------------------------ + * Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 | + * real | start | OP | PhyAddr | Reg Addr | TA | + * atheros| start | OP | 2'b11 | 8'b0 | TA | + * + * Bit: |16 |17 |18 |19 |20 |21 |22 |23 |24 |25 |26 |27 |28 |29 |30 |31 | + * real | Data | + * atheros| | Page [9:0] | + */ +/* In case of Page Address mode, Bit[18:9] of 32 bit register address should be + * written to bits[9:0] of mdio data register. + */ +#define AR9331_SW_ADDR_PAGE GENMASK(18, 9) + +/* ------------------------------------------------------------------------ + * Normal register access mode + * ------------------------------------------------------------------------ + * Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 | + * real | start | OP | PhyAddr | Reg Addr | TA | + * atheros| start | OP | 2'b10 | low_addr[7:0] | TA | + * + * Bit: |16 |17 |18 |19 |20 |21 |22 |23 |24 |25 |26 |27 |28 |29 |30 |31 | + * real | Data | + * atheros| Data | + * ------------------------------------------------------------------------ + */ +#define AR9331_SW_LOW_ADDR_PHY GENMASK(8, 6) +#define AR9331_SW_LOW_ADDR_REG GENMASK(5, 1) + +#define AR9331_SW_MDIO_PHY_MODE_M GENMASK(4, 3) +#define AR9331_SW_MDIO_PHY_MODE_PAGE 3 +#define AR9331_SW_MDIO_PHY_MODE_REG 2 +#define AR9331_SW_MDIO_PHY_MODE_BYPASS 0 +#define AR9331_SW_MDIO_PHY_ADDR_M GENMASK(2, 0) + +/* Empirical determined values */ +#define AR9331_SW_MDIO_POLL_SLEEP_US 1 +#define AR9331_SW_MDIO_POLL_TIMEOUT_US 20 + +struct ar9331_sw_priv { + struct device *dev; + struct dsa_switch ds; + struct dsa_switch_ops ops; + struct irq_domain *irqdomain; + struct mii_bus *mbus; /* mdio master */ + struct mii_bus *sbus; /* mdio slave */ + struct regmap *regmap; + struct reset_control *sw_reset; +}; + +/* Warning: switch reset will reset last AR9331_SW_MDIO_PHY_MODE_PAGE request + * If some kind of optimization is used, the request should be repeated. + */ +static int ar9331_sw_reset(struct ar9331_sw_priv *priv) +{ + int ret; + + ret = reset_control_assert(priv->sw_reset); + if (ret) + goto error; + + /* AR9331 doc do not provide any information about proper reset + * sequence. The AR8136 (the closes switch to the AR9331) doc says: + * reset duration should be greater than 10ms. So, let's use this value + * for now. + */ + usleep_range(10000, 15000); + ret = reset_control_deassert(priv->sw_reset); + if (ret) + goto error; + /* There is no information on how long should we wait after reset. + * AR8136 has an EEPROM and there is an Interrupt for EEPROM load + * status. AR9331 has no EEPROM support. + * For now, do not wait. In case AR8136 will be needed, the after + * reset delay can be added as well. + */ + + return 0; +error: + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); + return ret; +} + +static int ar9331_sw_mbus_write(struct mii_bus *mbus, int port, int regnum, + u16 data) +{ + struct ar9331_sw_priv *priv = mbus->priv; + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + ret = regmap_write(regmap, AR9331_SW_REG_MDIO_CTRL, + AR9331_SW_MDIO_CTRL_BUSY | + AR9331_SW_MDIO_CTRL_MASTER_EN | + FIELD_PREP(AR9331_SW_MDIO_CTRL_PHY_ADDR_M, port) | + FIELD_PREP(AR9331_SW_MDIO_CTRL_REG_ADDR_M, regnum) | + FIELD_PREP(AR9331_SW_MDIO_CTRL_DATA_M, data)); + if (ret) + goto error; + + ret = regmap_read_poll_timeout(regmap, AR9331_SW_REG_MDIO_CTRL, val, + !(val & AR9331_SW_MDIO_CTRL_BUSY), + AR9331_SW_MDIO_POLL_SLEEP_US, + AR9331_SW_MDIO_POLL_TIMEOUT_US); + if (ret) + goto error; + + return 0; +error: + dev_err_ratelimited(priv->dev, "PHY write error: %i\n", ret); + return ret; +} + +static int ar9331_sw_mbus_read(struct mii_bus *mbus, int port, int regnum) +{ + struct ar9331_sw_priv *priv = mbus->priv; + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + ret = regmap_write(regmap, AR9331_SW_REG_MDIO_CTRL, + AR9331_SW_MDIO_CTRL_BUSY | + AR9331_SW_MDIO_CTRL_MASTER_EN | + AR9331_SW_MDIO_CTRL_CMD_READ | + FIELD_PREP(AR9331_SW_MDIO_CTRL_PHY_ADDR_M, port) | + FIELD_PREP(AR9331_SW_MDIO_CTRL_REG_ADDR_M, regnum)); + if (ret) + goto error; + + ret = regmap_read_poll_timeout(regmap, AR9331_SW_REG_MDIO_CTRL, val, + !(val & AR9331_SW_MDIO_CTRL_BUSY), + AR9331_SW_MDIO_POLL_SLEEP_US, + AR9331_SW_MDIO_POLL_TIMEOUT_US); + if (ret) + goto error; + + ret = regmap_read(regmap, AR9331_SW_REG_MDIO_CTRL, &val); + if (ret) + goto error; + + return FIELD_GET(AR9331_SW_MDIO_CTRL_DATA_M, val); + +error: + dev_err_ratelimited(priv->dev, "PHY read error: %i\n", ret); + return ret; +} + +static int ar9331_sw_mbus_init(struct ar9331_sw_priv *priv) +{ + struct device *dev = priv->dev; + struct mii_bus *mbus; + struct device_node *np, *mnp; + int ret; + + np = dev->of_node; + + mbus = devm_mdiobus_alloc(dev); + if (!mbus) + return -ENOMEM; + + mbus->name = np->full_name; + snprintf(mbus->id, MII_BUS_ID_SIZE, "%pOF", np); + + mbus->read = ar9331_sw_mbus_read; + mbus->write = ar9331_sw_mbus_write; + mbus->priv = priv; + mbus->parent = dev; + + mnp = of_get_child_by_name(np, "mdio"); + if (!mnp) + return -ENODEV; + + ret = of_mdiobus_register(mbus, mnp); + of_node_put(mnp); + if (ret) + return ret; + + priv->mbus = mbus; + + return 0; +} + +static int ar9331_sw_setup(struct dsa_switch *ds) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + + ret = ar9331_sw_reset(priv); + if (ret) + return ret; + + /* Reset will set proper defaults. CPU - Port0 will be enabled and + * configured. All other ports (ports 1 - 5) are disabled + */ + ret = ar9331_sw_mbus_init(priv); + if (ret) + return ret; + + /* Do not drop broadcast frames */ + ret = regmap_write_bits(regmap, AR9331_SW_REG_FLOOD_MASK, + AR9331_SW_FLOOD_MASK_BROAD_TO_CPU, + AR9331_SW_FLOOD_MASK_BROAD_TO_CPU); + if (ret) + goto error; + + /* Set max frame size to the maximum supported value */ + ret = regmap_write_bits(regmap, AR9331_SW_REG_GLOBAL_CTRL, + AR9331_SW_GLOBAL_CTRL_MFS_M, + AR9331_SW_GLOBAL_CTRL_MFS_M); + if (ret) + goto error; + + return 0; +error: + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); + return ret; +} + +static void ar9331_sw_port_disable(struct dsa_switch *ds, int port) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_write(regmap, AR9331_SW_REG_PORT_STATUS(port), 0); + if (ret) + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); +} + +static enum dsa_tag_protocol ar9331_sw_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol m) +{ + return DSA_TAG_PROTO_AR9331; +} + +static void ar9331_sw_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + switch (port) { + case 0: + if (state->interface != PHY_INTERFACE_MODE_GMII) + goto unsupported; + + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + break; + case 1: + case 2: + case 3: + case 4: + case 5: + if (state->interface != PHY_INTERFACE_MODE_INTERNAL) + goto unsupported; + break; + default: + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, "Unsupported port: %i\n", port); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + + return; + +unsupported: + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, "Unsupported interface: %d, port: %d\n", + state->interface, port); +} + +static void ar9331_sw_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + u32 val; + + switch (state->speed) { + case SPEED_1000: + val = AR9331_SW_PORT_STATUS_SPEED_1000; + break; + case SPEED_100: + val = AR9331_SW_PORT_STATUS_SPEED_100; + break; + case SPEED_10: + val = AR9331_SW_PORT_STATUS_SPEED_10; + break; + default: + return; + } + + if (state->duplex) + val |= AR9331_SW_PORT_STATUS_DUPLEX_MODE; + + if (state->pause & MLO_PAUSE_TX) + val |= AR9331_SW_PORT_STATUS_TX_FLOW_EN; + + if (state->pause & MLO_PAUSE_RX) + val |= AR9331_SW_PORT_STATUS_RX_FLOW_EN; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), + AR9331_SW_PORT_STATUS_LINK_MASK, val); + if (ret) + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); +} + +static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), + AR9331_SW_PORT_STATUS_MAC_MASK, 0); + if (ret) + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); +} + +static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), + AR9331_SW_PORT_STATUS_MAC_MASK, + AR9331_SW_PORT_STATUS_MAC_MASK); + if (ret) + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); +} + +static const struct dsa_switch_ops ar9331_sw_ops = { + .get_tag_protocol = ar9331_sw_get_tag_protocol, + .setup = ar9331_sw_setup, + .port_disable = ar9331_sw_port_disable, + .phylink_validate = ar9331_sw_phylink_validate, + .phylink_mac_config = ar9331_sw_phylink_mac_config, + .phylink_mac_link_down = ar9331_sw_phylink_mac_link_down, + .phylink_mac_link_up = ar9331_sw_phylink_mac_link_up, +}; + +static irqreturn_t ar9331_sw_irq(int irq, void *data) +{ + struct ar9331_sw_priv *priv = data; + struct regmap *regmap = priv->regmap; + u32 stat; + int ret; + + ret = regmap_read(regmap, AR9331_SW_REG_GINT, &stat); + if (ret) { + dev_err(priv->dev, "can't read interrupt status\n"); + return IRQ_NONE; + } + + if (!stat) + return IRQ_NONE; + + if (stat & AR9331_SW_GINT_PHY_INT) { + int child_irq; + + child_irq = irq_find_mapping(priv->irqdomain, 0); + handle_nested_irq(child_irq); + } + + ret = regmap_write(regmap, AR9331_SW_REG_GINT, stat); + if (ret) { + dev_err(priv->dev, "can't write interrupt status\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static void ar9331_sw_mask_irq(struct irq_data *d) +{ + struct ar9331_sw_priv *priv = irq_data_get_irq_chip_data(d); + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_GINT_MASK, + AR9331_SW_GINT_PHY_INT, 0); + if (ret) + dev_err(priv->dev, "could not mask IRQ\n"); +} + +static void ar9331_sw_unmask_irq(struct irq_data *d) +{ + struct ar9331_sw_priv *priv = irq_data_get_irq_chip_data(d); + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_GINT_MASK, + AR9331_SW_GINT_PHY_INT, + AR9331_SW_GINT_PHY_INT); + if (ret) + dev_err(priv->dev, "could not unmask IRQ\n"); +} + +static struct irq_chip ar9331_sw_irq_chip = { + .name = AR9331_SW_NAME, + .irq_mask = ar9331_sw_mask_irq, + .irq_unmask = ar9331_sw_unmask_irq, +}; + +static int ar9331_sw_irq_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_data(irq, domain->host_data); + irq_set_chip_and_handler(irq, &ar9331_sw_irq_chip, handle_simple_irq); + irq_set_nested_thread(irq, 1); + irq_set_noprobe(irq); + + return 0; +} + +static void ar9331_sw_irq_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_nested_thread(irq, 0); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops ar9331_sw_irqdomain_ops = { + .map = ar9331_sw_irq_map, + .unmap = ar9331_sw_irq_unmap, + .xlate = irq_domain_xlate_onecell, +}; + +static int ar9331_sw_irq_init(struct ar9331_sw_priv *priv) +{ + struct device_node *np = priv->dev->of_node; + struct device *dev = priv->dev; + int ret, irq; + + irq = of_irq_get(np, 0); + if (irq <= 0) { + dev_err(dev, "failed to get parent IRQ\n"); + return irq ? irq : -EINVAL; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, ar9331_sw_irq, + IRQF_ONESHOT, AR9331_SW_NAME, priv); + if (ret) { + dev_err(dev, "unable to request irq: %d\n", ret); + return ret; + } + + priv->irqdomain = irq_domain_add_linear(np, 1, &ar9331_sw_irqdomain_ops, + priv); + if (!priv->irqdomain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -EINVAL; + } + + irq_set_parent(irq_create_mapping(priv->irqdomain, 0), irq); + + return 0; +} + +static int __ar9331_mdio_write(struct mii_bus *sbus, u8 mode, u16 reg, u16 val) +{ + u8 r, p; + + p = FIELD_PREP(AR9331_SW_MDIO_PHY_MODE_M, mode) | + FIELD_GET(AR9331_SW_LOW_ADDR_PHY, reg); + r = FIELD_GET(AR9331_SW_LOW_ADDR_REG, reg); + + return mdiobus_write(sbus, p, r, val); +} + +static int __ar9331_mdio_read(struct mii_bus *sbus, u16 reg) +{ + u8 r, p; + + p = FIELD_PREP(AR9331_SW_MDIO_PHY_MODE_M, AR9331_SW_MDIO_PHY_MODE_REG) | + FIELD_GET(AR9331_SW_LOW_ADDR_PHY, reg); + r = FIELD_GET(AR9331_SW_LOW_ADDR_REG, reg); + + return mdiobus_read(sbus, p, r); +} + +static int ar9331_mdio_read(void *ctx, const void *reg_buf, size_t reg_len, + void *val_buf, size_t val_len) +{ + struct ar9331_sw_priv *priv = ctx; + struct mii_bus *sbus = priv->sbus; + u32 reg = *(u32 *)reg_buf; + int ret; + + if (reg == AR9331_SW_REG_PAGE) { + /* We cannot read the page selector register from hardware and + * we cache its value in regmap. Return all bits set here, + * that regmap will always write the page on first use. + */ + *(u32 *)val_buf = GENMASK(9, 0); + return 0; + } + + ret = __ar9331_mdio_read(sbus, reg); + if (ret < 0) + goto error; + + *(u32 *)val_buf = ret; + ret = __ar9331_mdio_read(sbus, reg + 2); + if (ret < 0) + goto error; + + *(u32 *)val_buf |= ret << 16; + + return 0; +error: + dev_err_ratelimited(&sbus->dev, "Bus error. Failed to read register.\n"); + return ret; +} + +static int ar9331_mdio_write(void *ctx, u32 reg, u32 val) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ctx; + struct mii_bus *sbus = priv->sbus; + int ret; + + if (reg == AR9331_SW_REG_PAGE) { + ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_PAGE, + 0, val); + if (ret < 0) + goto error; + + return 0; + } + + ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_REG, reg, val); + if (ret < 0) + goto error; + + ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_REG, reg + 2, + val >> 16); + if (ret < 0) + goto error; + + return 0; +error: + dev_err_ratelimited(&sbus->dev, "Bus error. Failed to write register.\n"); + return ret; +} + +static int ar9331_sw_bus_write(void *context, const void *data, size_t count) +{ + u32 reg = *(u32 *)data; + u32 val = *((u32 *)data + 1); + + return ar9331_mdio_write(context, reg, val); +} + +static const struct regmap_range ar9331_valid_regs[] = { + regmap_reg_range(0x0, 0x0), + regmap_reg_range(0x10, 0x14), + regmap_reg_range(0x20, 0x24), + regmap_reg_range(0x2c, 0x30), + regmap_reg_range(0x40, 0x44), + regmap_reg_range(0x50, 0x78), + regmap_reg_range(0x80, 0x98), + + regmap_reg_range(0x100, 0x120), + regmap_reg_range(0x200, 0x220), + regmap_reg_range(0x300, 0x320), + regmap_reg_range(0x400, 0x420), + regmap_reg_range(0x500, 0x520), + regmap_reg_range(0x600, 0x620), + + regmap_reg_range(0x20000, 0x200a4), + regmap_reg_range(0x20100, 0x201a4), + regmap_reg_range(0x20200, 0x202a4), + regmap_reg_range(0x20300, 0x203a4), + regmap_reg_range(0x20400, 0x204a4), + regmap_reg_range(0x20500, 0x205a4), + + /* dummy page selector reg */ + regmap_reg_range(AR9331_SW_REG_PAGE, AR9331_SW_REG_PAGE), +}; + +static const struct regmap_range ar9331_nonvolatile_regs[] = { + regmap_reg_range(AR9331_SW_REG_PAGE, AR9331_SW_REG_PAGE), +}; + +static const struct regmap_range_cfg ar9331_regmap_range[] = { + { + .selector_reg = AR9331_SW_REG_PAGE, + .selector_mask = GENMASK(9, 0), + .selector_shift = 0, + + .window_start = 0, + .window_len = 512, + + .range_min = 0, + .range_max = AR9331_SW_REG_PAGE - 4, + }, +}; + +static const struct regmap_access_table ar9331_register_set = { + .yes_ranges = ar9331_valid_regs, + .n_yes_ranges = ARRAY_SIZE(ar9331_valid_regs), +}; + +static const struct regmap_access_table ar9331_volatile_set = { + .no_ranges = ar9331_nonvolatile_regs, + .n_no_ranges = ARRAY_SIZE(ar9331_nonvolatile_regs), +}; + +static const struct regmap_config ar9331_mdio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = AR9331_SW_REG_PAGE, + + .ranges = ar9331_regmap_range, + .num_ranges = ARRAY_SIZE(ar9331_regmap_range), + + .volatile_table = &ar9331_volatile_set, + .wr_table = &ar9331_register_set, + .rd_table = &ar9331_register_set, + + .cache_type = REGCACHE_RBTREE, +}; + +static struct regmap_bus ar9331_sw_bus = { + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, + .read = ar9331_mdio_read, + .write = ar9331_sw_bus_write, + .max_raw_read = 4, + .max_raw_write = 4, +}; + +static int ar9331_sw_probe(struct mdio_device *mdiodev) +{ + struct ar9331_sw_priv *priv; + struct dsa_switch *ds; + int ret; + + priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init(&mdiodev->dev, &ar9331_sw_bus, priv, + &ar9331_mdio_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&mdiodev->dev, "regmap init failed: %d\n", ret); + return ret; + } + + priv->sw_reset = devm_reset_control_get(&mdiodev->dev, "switch"); + if (IS_ERR(priv->sw_reset)) { + dev_err(&mdiodev->dev, "missing switch reset\n"); + return PTR_ERR(priv->sw_reset); + } + + priv->sbus = mdiodev->bus; + priv->dev = &mdiodev->dev; + + ret = ar9331_sw_irq_init(priv); + if (ret) + return ret; + + ds = &priv->ds; + ds->dev = &mdiodev->dev; + ds->num_ports = AR9331_SW_PORTS; + ds->priv = priv; + priv->ops = ar9331_sw_ops; + ds->ops = &priv->ops; + dev_set_drvdata(&mdiodev->dev, priv); + + ret = dsa_register_switch(ds); + if (ret) + goto err_remove_irq; + + return 0; + +err_remove_irq: + irq_domain_remove(priv->irqdomain); + + return ret; +} + +static void ar9331_sw_remove(struct mdio_device *mdiodev) +{ + struct ar9331_sw_priv *priv = dev_get_drvdata(&mdiodev->dev); + + irq_domain_remove(priv->irqdomain); + mdiobus_unregister(priv->mbus); + dsa_unregister_switch(&priv->ds); + + reset_control_assert(priv->sw_reset); +} + +static const struct of_device_id ar9331_sw_of_match[] = { + { .compatible = "qca,ar9331-switch" }, + { }, +}; + +static struct mdio_driver ar9331_sw_mdio_driver = { + .probe = ar9331_sw_probe, + .remove = ar9331_sw_remove, + .mdiodrv.driver = { + .name = AR9331_SW_NAME, + .of_match_table = ar9331_sw_of_match, + }, +}; + +mdio_module_driver(ar9331_sw_mdio_driver); + +MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>"); +MODULE_DESCRIPTION("Driver for Atheros AR9331 switch"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index e548289df31e..9f4205b4439b 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -1017,7 +1017,8 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port, } static enum dsa_tag_protocol -qca8k_get_tag_protocol(struct dsa_switch *ds, int port) +qca8k_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mp) { return DSA_TAG_PROTO_QCA; } diff --git a/drivers/net/dsa/rtl8366rb.c b/drivers/net/dsa/rtl8366rb.c index f5cc8b0a7c74..fd1977590cb4 100644 --- a/drivers/net/dsa/rtl8366rb.c +++ b/drivers/net/dsa/rtl8366rb.c @@ -964,7 +964,8 @@ static int rtl8366rb_setup(struct dsa_switch *ds) } static enum dsa_tag_protocol rtl8366_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol mp) { /* For now, the RTL switches are handled without any custom tags. * diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index bb91f3d17cf2..03ba6d25f7fe 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -426,14 +426,6 @@ static int sja1105_init_general_params(struct sja1105_private *priv) .tpid2 = ETH_P_SJA1105, }; struct sja1105_table *table; - int i, k = 0; - - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - if (dsa_is_dsa_port(priv->ds, i)) - default_general_params.casc_port = i; - else if (dsa_is_user_port(priv->ds, i)) - priv->ports[i].mgmt_slot = k++; - } table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; @@ -1542,7 +1534,8 @@ static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled) } static enum dsa_tag_protocol -sja1105_get_tag_protocol(struct dsa_switch *ds, int port) +sja1105_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mp) { return DSA_TAG_PROTO_SJA1105; } @@ -1740,6 +1733,16 @@ static int sja1105_setup(struct dsa_switch *ds) static void sja1105_teardown(struct dsa_switch *ds) { struct sja1105_private *priv = ds->priv; + int port; + + for (port = 0; port < SJA1105_NUM_PORTS; port++) { + struct sja1105_port *sp = &priv->ports[port]; + + if (!dsa_is_user_port(ds, port)) + continue; + + kthread_destroy_worker(sp->xmit_worker); + } sja1105_tas_teardown(ds); sja1105_ptp_clock_unregister(ds); @@ -1761,6 +1764,18 @@ static int sja1105_port_enable(struct dsa_switch *ds, int port, return 0; } +static void sja1105_port_disable(struct dsa_switch *ds, int port) +{ + struct sja1105_private *priv = ds->priv; + struct sja1105_port *sp = &priv->ports[port]; + + if (!dsa_is_user_port(ds, port)) + return; + + kthread_cancel_work_sync(&sp->xmit_work); + skb_queue_purge(&sp->xmit_queue); +} + static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, struct sk_buff *skb, bool takets) { @@ -1819,47 +1834,36 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, return NETDEV_TX_OK; } +#define work_to_port(work) \ + container_of((work), struct sja1105_port, xmit_work) +#define tagger_to_sja1105(t) \ + container_of((t), struct sja1105_private, tagger_data) + /* Deferred work is unfortunately necessary because setting up the management * route cannot be done from atomit context (SPI transfer takes a sleepable * lock on the bus) */ -static netdev_tx_t sja1105_port_deferred_xmit(struct dsa_switch *ds, int port, - struct sk_buff *skb) +static void sja1105_port_deferred_xmit(struct kthread_work *work) { - struct sja1105_private *priv = ds->priv; - struct sja1105_port *sp = &priv->ports[port]; - int slot = sp->mgmt_slot; - struct sk_buff *clone; - - /* The tragic fact about the switch having 4x2 slots for installing - * management routes is that all of them except one are actually - * useless. - * If 2 slots are simultaneously configured for two BPDUs sent to the - * same (multicast) DMAC but on different egress ports, the switch - * would confuse them and redirect first frame it receives on the CPU - * port towards the port configured on the numerically first slot - * (therefore wrong port), then second received frame on second slot - * (also wrong port). - * So for all practical purposes, there needs to be a lock that - * prevents that from happening. The slot used here is utterly useless - * (could have simply been 0 just as fine), but we are doing it - * nonetheless, in case a smarter idea ever comes up in the future. - */ - mutex_lock(&priv->mgmt_lock); + struct sja1105_port *sp = work_to_port(work); + struct sja1105_tagger_data *tagger_data = sp->data; + struct sja1105_private *priv = tagger_to_sja1105(tagger_data); + int port = sp - priv->ports; + struct sk_buff *skb; - /* The clone, if there, was made by dsa_skb_tx_timestamp */ - clone = DSA_SKB_CB(skb)->clone; + while ((skb = skb_dequeue(&sp->xmit_queue)) != NULL) { + struct sk_buff *clone = DSA_SKB_CB(skb)->clone; - sja1105_mgmt_xmit(ds, port, slot, skb, !!clone); + mutex_lock(&priv->mgmt_lock); - if (!clone) - goto out; + sja1105_mgmt_xmit(priv->ds, port, 0, skb, !!clone); - sja1105_ptp_txtstamp_skb(ds, port, clone); + /* The clone, if there, was made by dsa_skb_tx_timestamp */ + if (clone) + sja1105_ptp_txtstamp_skb(priv->ds, port, clone); -out: - mutex_unlock(&priv->mgmt_lock); - return NETDEV_TX_OK; + mutex_unlock(&priv->mgmt_lock); + } } /* The MAXAGE setting belongs to the L2 Forwarding Parameters table, @@ -1990,6 +1994,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .get_sset_count = sja1105_get_sset_count, .get_ts_info = sja1105_get_ts_info, .port_enable = sja1105_port_enable, + .port_disable = sja1105_port_disable, .port_fdb_dump = sja1105_fdb_dump, .port_fdb_add = sja1105_fdb_add, .port_fdb_del = sja1105_fdb_del, @@ -2003,7 +2008,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .port_mdb_prepare = sja1105_mdb_prepare, .port_mdb_add = sja1105_mdb_add, .port_mdb_del = sja1105_mdb_del, - .port_deferred_xmit = sja1105_port_deferred_xmit, .port_hwtstamp_get = sja1105_hwtstamp_get, .port_hwtstamp_set = sja1105_hwtstamp_set, .port_rxtstamp = sja1105_port_rxtstamp, @@ -2055,7 +2059,7 @@ static int sja1105_probe(struct spi_device *spi) struct device *dev = &spi->dev; struct sja1105_private *priv; struct dsa_switch *ds; - int rc, i; + int rc, port; if (!dev->of_node) { dev_err(dev, "No DTS bindings for SJA1105 driver\n"); @@ -2120,15 +2124,42 @@ static int sja1105_probe(struct spi_device *spi) return rc; /* Connections between dsa_port and sja1105_port */ - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - struct sja1105_port *sp = &priv->ports[i]; + for (port = 0; port < SJA1105_NUM_PORTS; port++) { + struct sja1105_port *sp = &priv->ports[port]; + struct dsa_port *dp = dsa_to_port(ds, port); + struct net_device *slave; - dsa_to_port(ds, i)->priv = sp; - sp->dp = dsa_to_port(ds, i); + if (!dsa_is_user_port(ds, port)) + continue; + + dp->priv = sp; + sp->dp = dp; sp->data = tagger_data; + slave = dp->slave; + kthread_init_work(&sp->xmit_work, sja1105_port_deferred_xmit); + sp->xmit_worker = kthread_create_worker(0, "%s_xmit", + slave->name); + if (IS_ERR(sp->xmit_worker)) { + rc = PTR_ERR(sp->xmit_worker); + dev_err(ds->dev, + "failed to create deferred xmit thread: %d\n", + rc); + goto out; + } + skb_queue_head_init(&sp->xmit_queue); } return 0; +out: + while (port-- > 0) { + struct sja1105_port *sp = &priv->ports[port]; + + if (!dsa_is_user_port(ds, port)) + continue; + + kthread_destroy_worker(sp->xmit_worker); + } + return rc; } static int sja1105_remove(struct spi_device *spi) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 43ab7589d0d0..a836fc38c4a4 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -83,6 +83,7 @@ static int sja1105_init_avb_params(struct sja1105_private *priv, static int sja1105_change_rxtstamping(struct sja1105_private *priv, bool on) { + struct sja1105_ptp_data *ptp_data = &priv->ptp_data; struct sja1105_general_params_entry *general_params; struct sja1105_table *table; int rc; @@ -101,6 +102,8 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv, kfree_skb(priv->tagger_data.stampable_skb); priv->tagger_data.stampable_skb = NULL; } + ptp_cancel_worker_sync(ptp_data->clock); + skb_queue_purge(&ptp_data->skb_rxtstamp_queue); return sja1105_static_config_reload(priv, SJA1105_RX_HWTSTAMPING); } @@ -367,22 +370,16 @@ static int sja1105_ptpclkval_write(struct sja1105_private *priv, u64 ticks, ptp_sts); } -#define rxtstamp_to_tagger(d) \ - container_of((d), struct sja1105_tagger_data, rxtstamp_work) -#define tagger_to_sja1105(d) \ - container_of((d), struct sja1105_private, tagger_data) - -static void sja1105_rxtstamp_work(struct work_struct *work) +static long sja1105_rxtstamp_work(struct ptp_clock_info *ptp) { - struct sja1105_tagger_data *tagger_data = rxtstamp_to_tagger(work); - struct sja1105_private *priv = tagger_to_sja1105(tagger_data); - struct sja1105_ptp_data *ptp_data = &priv->ptp_data; + struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); struct dsa_switch *ds = priv->ds; struct sk_buff *skb; mutex_lock(&ptp_data->lock); - while ((skb = skb_dequeue(&tagger_data->skb_rxtstamp_queue)) != NULL) { + while ((skb = skb_dequeue(&ptp_data->skb_rxtstamp_queue)) != NULL) { struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb); u64 ticks, ts; int rc; @@ -404,6 +401,9 @@ static void sja1105_rxtstamp_work(struct work_struct *work) } mutex_unlock(&ptp_data->lock); + + /* Don't restart */ + return -1; } /* Called from dsa_skb_defer_rx_timestamp */ @@ -411,16 +411,16 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb, unsigned int type) { struct sja1105_private *priv = ds->priv; - struct sja1105_tagger_data *tagger_data = &priv->tagger_data; + struct sja1105_ptp_data *ptp_data = &priv->ptp_data; - if (!test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state)) + if (!test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) return false; /* We need to read the full PTP clock to reconstruct the Rx * timestamp. For that we need a sleepable context. */ - skb_queue_tail(&tagger_data->skb_rxtstamp_queue, skb); - schedule_work(&tagger_data->rxtstamp_work); + skb_queue_tail(&ptp_data->skb_rxtstamp_queue, skb); + ptp_schedule_worker(ptp_data->clock, 0); return true; } @@ -628,11 +628,11 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds) .adjtime = sja1105_ptp_adjtime, .gettimex64 = sja1105_ptp_gettimex, .settime64 = sja1105_ptp_settime, + .do_aux_work = sja1105_rxtstamp_work, .max_adj = SJA1105_MAX_ADJ_PPB, }; - skb_queue_head_init(&tagger_data->skb_rxtstamp_queue); - INIT_WORK(&tagger_data->rxtstamp_work, sja1105_rxtstamp_work); + skb_queue_head_init(&ptp_data->skb_rxtstamp_queue); spin_lock_init(&tagger_data->meta_lock); ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev); @@ -653,8 +653,8 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds) if (IS_ERR_OR_NULL(ptp_data->clock)) return; - cancel_work_sync(&priv->tagger_data.rxtstamp_work); - skb_queue_purge(&priv->tagger_data.skb_rxtstamp_queue); + ptp_cancel_worker_sync(ptp_data->clock); + skb_queue_purge(&ptp_data->skb_rxtstamp_queue); ptp_clock_unregister(ptp_data->clock); ptp_data->clock = NULL; } diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.h b/drivers/net/dsa/sja1105/sja1105_ptp.h index 470f44b76318..6f4a19eec709 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.h +++ b/drivers/net/dsa/sja1105/sja1105_ptp.h @@ -30,6 +30,7 @@ struct sja1105_ptp_cmd { }; struct sja1105_ptp_data { + struct sk_buff_head skb_rxtstamp_queue; struct ptp_clock_info caps; struct ptp_clock *clock; struct sja1105_ptp_cmd cmd; diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index 42c1574d45f2..6e21a2a5cf01 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -542,7 +542,8 @@ static int vsc73xx_phy_write(struct dsa_switch *ds, int phy, int regnum, } static enum dsa_tag_protocol vsc73xx_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol mp) { /* The switch internally uses a 8 byte header with length, * source port, tag, LPA and priority. This is supposedly @@ -1111,7 +1112,9 @@ static int vsc73xx_gpio_probe(struct vsc73xx *vsc) vsc->gc.ngpio = 4; vsc->gc.owner = THIS_MODULE; vsc->gc.parent = vsc->dev; +#if IS_ENABLED(CONFIG_OF_GPIO) vsc->gc.of_node = vsc->dev->of_node; +#endif vsc->gc.base = -1; vsc->gc.get = vsc73xx_gpio_get; vsc->gc.set = vsc73xx_gpio_set; |