diff options
Diffstat (limited to 'drivers/net/dsa')
-rw-r--r-- | drivers/net/dsa/b53/Kconfig | 2 | ||||
-rw-r--r-- | drivers/net/dsa/b53/b53_common.c | 235 | ||||
-rw-r--r-- | drivers/net/dsa/b53/b53_priv.h | 146 | ||||
-rw-r--r-- | drivers/net/dsa/b53/b53_regs.h | 48 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 240 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2.h | 3 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2_cfp.c | 1130 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2_regs.h | 49 | ||||
-rw-r--r-- | drivers/net/dsa/dsa_loop.c | 11 | ||||
-rw-r--r-- | drivers/net/dsa/lan9303-core.c | 557 | ||||
-rw-r--r-- | drivers/net/dsa/lan9303.h | 21 | ||||
-rw-r--r-- | drivers/net/dsa/lan9303_i2c.c | 2 | ||||
-rw-r--r-- | drivers/net/dsa/lan9303_mdio.c | 7 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 3 | ||||
-rw-r--r-- | drivers/net/dsa/mt7530.c | 21 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6060.c | 51 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 210 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.c | 35 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.h | 2 | ||||
-rw-r--r-- | drivers/net/dsa/qca8k.c | 15 |
20 files changed, 1988 insertions, 800 deletions
diff --git a/drivers/net/dsa/b53/Kconfig b/drivers/net/dsa/b53/Kconfig index 27f32a50df57..2f988216dab9 100644 --- a/drivers/net/dsa/b53/Kconfig +++ b/drivers/net/dsa/b53/Kconfig @@ -1,6 +1,8 @@ menuconfig B53 tristate "Broadcom BCM53xx managed switch support" depends on NET_DSA + select NET_DSA_TAG_BRCM + select NET_DSA_TAG_BRCM_PREPEND help This driver adds support for Broadcom managed switch chips. It supports BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 274f3679f33d..f5a8dd96fd75 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -325,7 +325,6 @@ static void b53_get_vlan_entry(struct b53_device *dev, u16 vid, static void b53_set_forwarding(struct b53_device *dev, int enable) { - struct dsa_switch *ds = dev->ds; u8 mgmt; b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); @@ -337,14 +336,11 @@ static void b53_set_forwarding(struct b53_device *dev, int enable) b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); - /* Include IMP port in dumb forwarding mode when no tagging protocol is - * set + /* Include IMP port in dumb forwarding mode */ - if (ds->ops->get_tag_protocol(ds) == DSA_TAG_PROTO_NONE) { - b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt); - mgmt |= B53_MII_DUMB_FWDG_EN; - b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt); - } + b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt); + mgmt |= B53_MII_DUMB_FWDG_EN; + b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt); } static void b53_enable_vlan(struct b53_device *dev, bool enable) @@ -484,7 +480,7 @@ static int b53_fast_age_vlan(struct b53_device *dev, u16 vid) return b53_flush_arl(dev, FAST_AGE_VLAN); } -static void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) +void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) { struct b53_device *dev = ds->priv; unsigned int i; @@ -500,12 +496,12 @@ static void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), pvlan); } } +EXPORT_SYMBOL(b53_imp_vlan_setup); -static int b53_enable_port(struct dsa_switch *ds, int port, - struct phy_device *phy) +int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) { struct b53_device *dev = ds->priv; - unsigned int cpu_port = dev->cpu_port; + unsigned int cpu_port = ds->ports[port].cpu_dp->index; u16 pvlan; /* Clear the Rx and Tx disable bits and set to no spanning tree */ @@ -523,11 +519,15 @@ static int b53_enable_port(struct dsa_switch *ds, int port, b53_imp_vlan_setup(ds, cpu_port); + /* If EEE was enabled, restore it */ + if (dev->ports[port].eee.eee_enabled) + b53_eee_enable_set(ds, port, true); + return 0; } +EXPORT_SYMBOL(b53_enable_port); -static void b53_disable_port(struct dsa_switch *ds, int port, - struct phy_device *phy) +void b53_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy) { struct b53_device *dev = ds->priv; u8 reg; @@ -537,20 +537,80 @@ static void b53_disable_port(struct dsa_switch *ds, int port, reg |= PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE; b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg); } +EXPORT_SYMBOL(b53_disable_port); -static void b53_enable_cpu_port(struct b53_device *dev) +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; + u8 hdr_ctl, val; + u16 reg; + + /* Resolve which bit controls the Broadcom tag */ + switch (port) { + case 8: + val = BRCM_HDR_P8_EN; + break; + case 7: + val = BRCM_HDR_P7_EN; + break; + case 5: + val = BRCM_HDR_P5_EN; + break; + default: + val = 0; + break; + } + + /* Enable Broadcom tags for IMP port */ + b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, &hdr_ctl); + if (tag_en) + hdr_ctl |= val; + else + hdr_ctl &= ~val; + b53_write8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, hdr_ctl); + + /* Registers below are only accessible on newer devices */ + if (!is58xx(dev)) + return; + + /* Enable reception Broadcom tag for CPU TX (switch RX) to + * allow us to tag outgoing frames + */ + b53_read16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_RX_DIS, ®); + if (tag_en) + reg &= ~BIT(port); + else + reg |= BIT(port); + b53_write16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_RX_DIS, reg); + + /* Enable transmission of Broadcom tags from the switch (CPU RX) to + * allow delivering frames to the per-port net_devices + */ + b53_read16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_TX_DIS, ®); + if (tag_en) + reg &= ~BIT(port); + else + reg |= BIT(port); + b53_write16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_TX_DIS, reg); +} +EXPORT_SYMBOL(b53_brcm_hdr_setup); + +static void b53_enable_cpu_port(struct b53_device *dev, int port) { - unsigned int cpu_port = dev->cpu_port; u8 port_ctrl; /* BCM5325 CPU port is at 8 */ - if ((is5325(dev) || is5365(dev)) && cpu_port == B53_CPU_PORT_25) - cpu_port = B53_CPU_PORT; + if ((is5325(dev) || is5365(dev)) && port == B53_CPU_PORT_25) + port = B53_CPU_PORT; port_ctrl = PORT_CTRL_RX_BCST_EN | PORT_CTRL_RX_MCST_EN | PORT_CTRL_RX_UCST_EN; - b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(cpu_port), port_ctrl); + b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), port_ctrl); + + b53_brcm_hdr_setup(dev->ds, port); } static void b53_enable_mib(struct b53_device *dev) @@ -562,8 +622,9 @@ static void b53_enable_mib(struct b53_device *dev) b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc); } -static int b53_configure_vlan(struct b53_device *dev) +int b53_configure_vlan(struct dsa_switch *ds) { + struct b53_device *dev = ds->priv; struct b53_vlan vl = { 0 }; int i; @@ -586,6 +647,7 @@ static int b53_configure_vlan(struct b53_device *dev) return 0; } +EXPORT_SYMBOL(b53_configure_vlan); static void b53_switch_reset_gpio(struct b53_device *dev) { @@ -700,7 +762,7 @@ static int b53_apply_config(struct b53_device *priv) /* disable switching */ b53_set_forwarding(priv, 0); - b53_configure_vlan(priv); + b53_configure_vlan(priv->ds); /* enable switching */ b53_set_forwarding(priv, 1); @@ -816,12 +878,13 @@ static int b53_setup(struct dsa_switch *ds) if (ret) dev_err(ds->dev, "failed to apply configuration\n"); + /* Configure IMP/CPU port, disable unused ports. Enabled + * ports will be configured with .port_enable + */ for (port = 0; port < dev->num_ports; port++) { - if (BIT(port) & ds->enabled_port_mask) - b53_enable_port(ds, port, NULL); - else if (dsa_is_cpu_port(ds, port)) - b53_enable_cpu_port(dev); - else + if (dsa_is_cpu_port(ds, port)) + b53_enable_cpu_port(dev, port); + else if (dsa_is_unused_port(ds, port)) b53_disable_port(ds, port, NULL); } @@ -832,6 +895,7 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct b53_device *dev = ds->priv; + struct ethtool_eee *p = &dev->ports[port].eee; u8 rgmii_ctrl = 0, reg = 0, off; if (!phy_is_pseudo_fixed_link(phydev)) @@ -953,6 +1017,9 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po); } } + + /* Re-negotiate EEE if it was enabled already */ + p->eee_enabled = b53_eee_init(ds, port, phydev); } int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) @@ -986,7 +1053,6 @@ void b53_vlan_add(struct dsa_switch *ds, int port, struct b53_device *dev = ds->priv; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; - unsigned int cpu_port = dev->cpu_port; struct b53_vlan *vl; u16 vid; @@ -995,12 +1061,11 @@ void b53_vlan_add(struct dsa_switch *ds, int port, b53_get_vlan_entry(dev, vid, vl); - vl->members |= BIT(port) | BIT(cpu_port); + vl->members |= BIT(port); if (untagged) vl->untag |= BIT(port); else vl->untag &= ~BIT(port); - vl->untag &= ~BIT(cpu_port); b53_set_vlan_entry(dev, vid, vl); b53_fast_age_vlan(dev, vid); @@ -1280,7 +1345,7 @@ EXPORT_SYMBOL(b53_fdb_dump); int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) { struct b53_device *dev = ds->priv; - s8 cpu_port = ds->dst->cpu_dp->index; + s8 cpu_port = ds->ports[port].cpu_dp->index; u16 pvlan, reg; unsigned int i; @@ -1298,7 +1363,7 @@ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan); b53_for_each_port(dev, i) { - if (ds->ports[i].bridge_dev != br) + if (dsa_to_port(ds, i)->bridge_dev != br) continue; /* Add this local port to the remote port VLAN control @@ -1326,7 +1391,7 @@ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br) { struct b53_device *dev = ds->priv; struct b53_vlan *vl = &dev->vlans[0]; - s8 cpu_port = ds->dst->cpu_dp->index; + s8 cpu_port = ds->ports[port].cpu_dp->index; unsigned int i; u16 pvlan, reg, pvid; @@ -1334,7 +1399,7 @@ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br) b53_for_each_port(dev, i) { /* Don't touch the remaining ports */ - if (ds->ports[i].bridge_dev != br) + if (dsa_to_port(ds, i)->bridge_dev != br) continue; b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®); @@ -1364,8 +1429,8 @@ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br) b53_write16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, reg); } else { b53_get_vlan_entry(dev, pvid, vl); - vl->members |= BIT(port) | BIT(dev->cpu_port); - vl->untag |= BIT(port) | BIT(dev->cpu_port); + vl->members |= BIT(port) | BIT(cpu_port); + vl->untag |= BIT(port) | BIT(cpu_port); b53_set_vlan_entry(dev, pvid, vl); } } @@ -1414,9 +1479,40 @@ void b53_br_fast_age(struct dsa_switch *ds, int port) } EXPORT_SYMBOL(b53_br_fast_age); -static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds) +static bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port) +{ + /* Broadcom switches will accept enabling Broadcom tags on the + * following ports: 5, 7 and 8, any other port is not supported + */ + switch (port) { + case B53_CPU_PORT_25: + case 7: + case B53_CPU_PORT: + return true; + } + + dev_warn(ds->dev, "Port %d is not Broadcom tag capable\n", port); + return false; +} + +static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, + int port) { - return DSA_TAG_PROTO_NONE; + struct b53_device *dev = ds->priv; + + /* Older models support a different tag format that we do not + * support in net/dsa/tag_brcm.c yet. + */ + if (is5325(dev) || is5365(dev) || !b53_can_enable_brcm_tags(ds, port)) + return DSA_TAG_PROTO_NONE; + + /* 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; + + return DSA_TAG_PROTO_BRCM; } int b53_mirror_add(struct dsa_switch *ds, int port, @@ -1484,6 +1580,69 @@ void b53_mirror_del(struct dsa_switch *ds, int port, } EXPORT_SYMBOL(b53_mirror_del); +void b53_eee_enable_set(struct dsa_switch *ds, int port, bool enable) +{ + struct b53_device *dev = ds->priv; + u16 reg; + + b53_read16(dev, B53_EEE_PAGE, B53_EEE_EN_CTRL, ®); + if (enable) + reg |= BIT(port); + else + reg &= ~BIT(port); + b53_write16(dev, B53_EEE_PAGE, B53_EEE_EN_CTRL, reg); +} +EXPORT_SYMBOL(b53_eee_enable_set); + + +/* Returns 0 if EEE was not enabled, or 1 otherwise + */ +int b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) +{ + int ret; + + ret = phy_init_eee(phy, 0); + if (ret) + return 0; + + b53_eee_enable_set(ds, port, true); + + return 1; +} +EXPORT_SYMBOL(b53_eee_init); + +int b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +{ + struct b53_device *dev = ds->priv; + struct ethtool_eee *p = &dev->ports[port].eee; + u16 reg; + + if (is5325(dev) || is5365(dev)) + return -EOPNOTSUPP; + + b53_read16(dev, B53_EEE_PAGE, B53_EEE_LPI_INDICATE, ®); + e->eee_enabled = p->eee_enabled; + e->eee_active = !!(reg & BIT(port)); + + return 0; +} +EXPORT_SYMBOL(b53_get_mac_eee); + +int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +{ + struct b53_device *dev = ds->priv; + struct ethtool_eee *p = &dev->ports[port].eee; + + if (is5325(dev) || is5365(dev)) + return -EOPNOTSUPP; + + p->eee_enabled = e->eee_enabled; + b53_eee_enable_set(ds, port, e->eee_enabled); + + return 0; +} +EXPORT_SYMBOL(b53_set_mac_eee); + static const struct dsa_switch_ops b53_switch_ops = { .get_tag_protocol = b53_get_tag_protocol, .setup = b53_setup, @@ -1495,6 +1654,8 @@ static const struct dsa_switch_ops b53_switch_ops = { .adjust_link = b53_adjust_link, .port_enable = b53_enable_port, .port_disable = b53_disable_port, + .get_mac_eee = b53_get_mac_eee, + .set_mac_eee = b53_set_mac_eee, .port_bridge_join = b53_br_join, .port_bridge_leave = b53_br_leave, .port_stp_state_set = b53_br_set_stp_state, diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 01bd8cbe9a3f..daaaa1ecb996 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -70,6 +70,7 @@ enum { struct b53_port { u16 vlan_ctl_mask; + struct ethtool_eee eee; }; struct b53_vlan { @@ -186,11 +187,6 @@ static inline int is58xx(struct b53_device *dev) #define B53_CPU_PORT_25 5 #define B53_CPU_PORT 8 -static inline int is_cpu_port(struct b53_device *dev, int port) -{ - return dev->cpu_port; -} - struct b53_device *b53_switch_alloc(struct device *base, const struct b53_io_ops *ops, void *priv); @@ -204,119 +200,30 @@ static inline void b53_switch_remove(struct b53_device *dev) dsa_unregister_switch(dev->ds); } -static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->read8(dev, page, reg, val); - mutex_unlock(&dev->reg_mutex); - - return ret; +#define b53_build_op(type_op_size, val_type) \ +static inline int b53_##type_op_size(struct b53_device *dev, u8 page, \ + u8 reg, val_type val) \ +{ \ + int ret; \ + \ + mutex_lock(&dev->reg_mutex); \ + ret = dev->ops->type_op_size(dev, page, reg, val); \ + mutex_unlock(&dev->reg_mutex); \ + \ + return ret; \ } -static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->read16(dev, page, reg, val); - mutex_unlock(&dev->reg_mutex); - - return ret; -} - -static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->read32(dev, page, reg, val); - mutex_unlock(&dev->reg_mutex); - - return ret; -} - -static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->read48(dev, page, reg, val); - mutex_unlock(&dev->reg_mutex); - - return ret; -} - -static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->read64(dev, page, reg, val); - mutex_unlock(&dev->reg_mutex); - - return ret; -} +b53_build_op(read8, u8 *); +b53_build_op(read16, u16 *); +b53_build_op(read32, u32 *); +b53_build_op(read48, u64 *); +b53_build_op(read64, u64 *); -static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->write8(dev, page, reg, value); - mutex_unlock(&dev->reg_mutex); - - return ret; -} - -static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg, - u16 value) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->write16(dev, page, reg, value); - mutex_unlock(&dev->reg_mutex); - - return ret; -} - -static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg, - u32 value) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->write32(dev, page, reg, value); - mutex_unlock(&dev->reg_mutex); - - return ret; -} - -static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg, - u64 value) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->write48(dev, page, reg, value); - mutex_unlock(&dev->reg_mutex); - - return ret; -} - -static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg, - u64 value) -{ - int ret; - - mutex_lock(&dev->reg_mutex); - ret = dev->ops->write64(dev, page, reg, value); - mutex_unlock(&dev->reg_mutex); - - return ret; -} +b53_build_op(write8, u8); +b53_build_op(write16, u16); +b53_build_op(write32, u32); +b53_build_op(write48, u64); +b53_build_op(write64, u64); struct b53_arl_entry { u8 port; @@ -377,6 +284,8 @@ static inline int b53_switch_get_reset_gpio(struct b53_device *dev) #endif /* Exported functions towards other drivers */ +void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port); +int b53_configure_vlan(struct dsa_switch *ds); void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data); void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); int b53_get_sset_count(struct dsa_switch *ds); @@ -403,5 +312,12 @@ int b53_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress); 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); +void b53_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy); +void b53_brcm_hdr_setup(struct dsa_switch *ds, int port); +void b53_eee_enable_set(struct dsa_switch *ds, int port, bool enable); +int b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy); +int b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); +int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); #endif diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h index e5c86d44667a..2a9f421680aa 100644 --- a/drivers/net/dsa/b53/b53_regs.h +++ b/drivers/net/dsa/b53/b53_regs.h @@ -50,6 +50,9 @@ /* Jumbo Frame Registers */ #define B53_JUMBO_PAGE 0x40 +/* EEE Control Registers Page */ +#define B53_EEE_PAGE 0x92 + /* CFP Configuration Registers Page */ #define B53_CFP_PAGE 0xa1 @@ -210,6 +213,7 @@ #define B53_BRCM_HDR 0x03 #define BRCM_HDR_P8_EN BIT(0) /* Enable tagging on port 8 */ #define BRCM_HDR_P5_EN BIT(1) /* Enable tagging on port 5 */ +#define BRCM_HDR_P7_EN BIT(2) /* Enable tagging on port 7 */ /* Mirror capture control register (16 bit) */ #define B53_MIR_CAP_CTL 0x10 @@ -249,6 +253,12 @@ /* Revision ID register (8 bit) */ #define B53_REV_ID 0x40 +/* Broadcom header RX control (16 bit) */ +#define B53_BRCM_HDR_RX_DIS 0x60 + +/* Broadcom header TX control (16 bit) */ +#define B53_BRCM_HDR_TX_DIS 0x62 + /************************************************************************* * ARL Access Page Registers *************************************************************************/ @@ -465,6 +475,44 @@ #define JMS_MAX_SIZE 9724 /************************************************************************* + * EEE Configuration Page Registers + *************************************************************************/ + +/* EEE Enable control register (16 bit) */ +#define B53_EEE_EN_CTRL 0x00 + +/* EEE LPI assert status register (16 bit) */ +#define B53_EEE_LPI_ASSERT_STS 0x02 + +/* EEE LPI indicate status register (16 bit) */ +#define B53_EEE_LPI_INDICATE 0x4 + +/* EEE Receiving idle symbols status register (16 bit) */ +#define B53_EEE_RX_IDLE_SYM_STS 0x6 + +/* EEE Pipeline timer register (32 bit) */ +#define B53_EEE_PIP_TIMER 0xC + +/* EEE Sleep timer Gig register (32 bit) */ +#define B53_EEE_SLEEP_TIMER_GIG(i) (0x10 + 4 * (i)) + +/* EEE Sleep timer FE register (32 bit) */ +#define B53_EEE_SLEEP_TIMER_FE(i) (0x34 + 4 * (i)) + +/* EEE Minimum LP timer Gig register (32 bit) */ +#define B53_EEE_MIN_LP_TIMER_GIG(i) (0x58 + 4 * (i)) + +/* EEE Minimum LP timer FE register (32 bit) */ +#define B53_EEE_MIN_LP_TIMER_FE(i) (0x7c + 4 * (i)) + +/* EEE Wake timer Gig register (16 bit) */ +#define B53_EEE_WAKE_TIMER_GIG(i) (0xa0 + 2 * (i)) + +/* EEE Wake timer FE register (16 bit) */ +#define B53_EEE_WAKE_TIMER_FE(i) (0xb2 + 2 * (i)) + + +/************************************************************************* * CFP Configuration Page Registers *************************************************************************/ diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index d7b53d53c116..93faa1fed6f2 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -35,71 +35,12 @@ #include "b53/b53_priv.h" #include "b53/b53_regs.h" -static enum dsa_tag_protocol bcm_sf2_sw_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol bcm_sf2_sw_get_tag_protocol(struct dsa_switch *ds, + int port) { return DSA_TAG_PROTO_BRCM; } -static void bcm_sf2_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) -{ - struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - unsigned int i; - u32 reg; - - /* Enable the IMP Port to be in the same VLAN as the other ports - * on a per-port basis such that we only have Port i and IMP in - * the same VLAN. - */ - for (i = 0; i < priv->hw_params.num_ports; i++) { - if (!((1 << i) & ds->enabled_port_mask)) - continue; - - reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); - reg |= (1 << cpu_port); - core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); - } -} - -static void bcm_sf2_brcm_hdr_setup(struct bcm_sf2_priv *priv, int port) -{ - u32 reg, val; - - /* Resolve which bit controls the Broadcom tag */ - switch (port) { - case 8: - val = BRCM_HDR_EN_P8; - break; - case 7: - val = BRCM_HDR_EN_P7; - break; - case 5: - val = BRCM_HDR_EN_P5; - break; - default: - val = 0; - break; - } - - /* Enable Broadcom tags for IMP port */ - reg = core_readl(priv, CORE_BRCM_HDR_CTRL); - reg |= val; - core_writel(priv, reg, CORE_BRCM_HDR_CTRL); - - /* Enable reception Broadcom tag for CPU TX (switch RX) to - * allow us to tag outgoing frames - */ - reg = core_readl(priv, CORE_BRCM_HDR_RX_DIS); - reg &= ~(1 << port); - core_writel(priv, reg, CORE_BRCM_HDR_RX_DIS); - - /* Enable transmission of Broadcom tags from the switch (CPU RX) to - * allow delivering frames to the per-port net_devices - */ - reg = core_readl(priv, CORE_BRCM_HDR_TX_DIS); - reg &= ~(1 << port); - core_writel(priv, reg, CORE_BRCM_HDR_TX_DIS); -} - static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); @@ -138,7 +79,7 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) reg |= i << (PRT_TO_QID_SHIFT * i); core_writel(priv, reg, CORE_PORT_TC2_QOS_MAP_PORT(port)); - bcm_sf2_brcm_hdr_setup(priv, port); + b53_brcm_hdr_setup(ds, port); /* Force link status for IMP port */ reg = core_readl(priv, offset); @@ -146,19 +87,6 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) core_writel(priv, reg, offset); } -static void bcm_sf2_eee_enable_set(struct dsa_switch *ds, int port, bool enable) -{ - struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - u32 reg; - - reg = core_readl(priv, CORE_EEE_EN_CTRL); - if (enable) - reg |= 1 << port; - else - reg &= ~(1 << port); - core_writel(priv, reg, CORE_EEE_EN_CTRL); -} - static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); @@ -236,7 +164,6 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, struct phy_device *phy) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - s8 cpu_port = ds->dst->cpu_dp->index; unsigned int i; u32 reg; @@ -247,7 +174,7 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, /* Enable Broadcom tags for that port if requested */ if (priv->brcm_tag_mask & BIT(port)) - bcm_sf2_brcm_hdr_setup(priv, port); + b53_brcm_hdr_setup(ds, port); /* Configure Traffic Class to QoS mapping, allow each priority to map * to a different queue number @@ -257,9 +184,6 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, reg |= i << (PRT_TO_QID_SHIFT * i); core_writel(priv, reg, CORE_PORT_TC2_QOS_MAP_PORT(port)); - /* Clear the Rx and Tx disable bits and set to no spanning tree */ - core_writel(priv, 0, CORE_G_PCTL_PORT(port)); - /* Re-enable the GPHY and re-apply workarounds */ if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1) { bcm_sf2_gphy_enable_set(ds, true); @@ -282,23 +206,20 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, if (port == priv->moca_port) bcm_sf2_port_intr_enable(priv, port); - /* Set this port, and only this one to be in the default VLAN, - * if member of a bridge, restore its membership prior to - * bringing down this port. - */ - reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); - reg &= ~PORT_VLAN_CTRL_MASK; - reg |= (1 << port); - reg |= priv->dev->ports[port].vlan_ctl_mask; - core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port)); - - bcm_sf2_imp_vlan_setup(ds, cpu_port); - - /* If EEE was enabled, restore it */ - if (priv->port_sts[port].eee.eee_enabled) - bcm_sf2_eee_enable_set(ds, port, true); + /* Set per-queue pause threshold to 32 */ + core_writel(priv, 32, CORE_TXQ_THD_PAUSE_QN_PORT(port)); + + /* Set ACB threshold to 24 */ + for (i = 0; i < SF2_NUM_EGRESS_QUEUES; i++) { + reg = acb_readl(priv, ACB_QUEUE_CFG(port * + SF2_NUM_EGRESS_QUEUES + i)); + reg &= ~XOFF_THRESHOLD_MASK; + reg |= 24; + acb_writel(priv, reg, ACB_QUEUE_CFG(port * + SF2_NUM_EGRESS_QUEUES + i)); + } - return 0; + return b53_enable_port(ds, port, phy); } static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, @@ -321,9 +242,7 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, else off = CORE_G_PCTL_PORT(port); - reg = core_readl(priv, off); - reg |= RX_DIS | TX_DIS; - core_writel(priv, reg, off); + b53_disable_port(ds, port, phy); /* Power down the port memory */ reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL); @@ -331,47 +250,6 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL); } -/* Returns 0 if EEE was not enabled, or 1 otherwise - */ -static int bcm_sf2_eee_init(struct dsa_switch *ds, int port, - struct phy_device *phy) -{ - int ret; - - ret = phy_init_eee(phy, 0); - if (ret) - return 0; - - bcm_sf2_eee_enable_set(ds, port, true); - - return 1; -} - -static int bcm_sf2_sw_get_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) -{ - struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - struct ethtool_eee *p = &priv->port_sts[port].eee; - u32 reg; - - reg = core_readl(priv, CORE_EEE_LPI_INDICATE); - e->eee_enabled = p->eee_enabled; - e->eee_active = !!(reg & (1 << port)); - - return 0; -} - -static int bcm_sf2_sw_set_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) -{ - struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - struct ethtool_eee *p = &priv->port_sts[port].eee; - - p->eee_enabled = e->eee_enabled; - bcm_sf2_eee_enable_set(ds, port, e->eee_enabled); - - return 0; -} static int bcm_sf2_sw_indir_rw(struct bcm_sf2_priv *priv, int op, int addr, int regnum, u16 val) @@ -606,7 +484,7 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - struct ethtool_eee *p = &priv->port_sts[port].eee; + struct ethtool_eee *p = &priv->dev->ports[port].eee; u32 id_mode_dis = 0, port_mode; const char *str = NULL; u32 reg, offset; @@ -688,7 +566,7 @@ force_link: core_writel(priv, reg, offset); if (!phydev->is_pseudo_fixed_link) - p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev); + p->eee_enabled = b53_eee_init(ds, port, phydev); } static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, @@ -724,7 +602,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, * state machine and make it go in PHY_FORCING state instead. */ if (!status->link) - netif_carrier_off(ds->ports[port].netdev); + netif_carrier_off(ds->ports[port].slave); status->duplex = 1; } else { status->link = 1; @@ -749,6 +627,20 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, status->pause = 1; } +static void bcm_sf2_enable_acb(struct dsa_switch *ds) +{ + struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); + u32 reg; + + /* Enable ACB globally */ + reg = acb_readl(priv, ACB_CONTROL); + reg |= (ACB_FLUSH_MASK << ACB_FLUSH_SHIFT); + acb_writel(priv, reg, ACB_CONTROL); + reg &= ~(ACB_FLUSH_MASK << ACB_FLUSH_SHIFT); + reg |= ACB_EN | ACB_ALGORITHM; + acb_writel(priv, reg, ACB_CONTROL); +} + static int bcm_sf2_sw_suspend(struct dsa_switch *ds) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); @@ -761,8 +653,7 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds) * bcm_sf2_sw_setup */ for (port = 0; port < DSA_MAX_PORTS; port++) { - if ((1 << port) & ds->enabled_port_mask || - dsa_is_cpu_port(ds, port)) + if (dsa_is_user_port(ds, port) || dsa_is_cpu_port(ds, port)) bcm_sf2_port_disable(ds, port, NULL); } @@ -785,19 +676,21 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) bcm_sf2_gphy_enable_set(ds, true); for (port = 0; port < DSA_MAX_PORTS; port++) { - if ((1 << port) & ds->enabled_port_mask) + if (dsa_is_user_port(ds, port)) bcm_sf2_port_setup(ds, port, NULL); else if (dsa_is_cpu_port(ds, port)) bcm_sf2_imp_setup(ds, port); } + bcm_sf2_enable_acb(ds); + return 0; } static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->dst->cpu_dp->netdev; + struct net_device *p = ds->ports[port].cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_wolinfo pwol; @@ -820,9 +713,9 @@ static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->dst->cpu_dp->netdev; + struct net_device *p = ds->ports[port].cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - s8 cpu_port = ds->dst->cpu_dp->index; + s8 cpu_port = ds->ports[port].cpu_dp->index; struct ethtool_wolinfo pwol; p->ethtool_ops->get_wol(p, &pwol); @@ -846,45 +739,6 @@ static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, return p->ethtool_ops->set_wol(p, wol); } -static int bcm_sf2_vlan_op_wait(struct bcm_sf2_priv *priv) -{ - unsigned int timeout = 10; - u32 reg; - - do { - reg = core_readl(priv, CORE_ARLA_VTBL_RWCTRL); - if (!(reg & ARLA_VTBL_STDN)) - return 0; - - usleep_range(1000, 2000); - } while (timeout--); - - return -ETIMEDOUT; -} - -static int bcm_sf2_vlan_op(struct bcm_sf2_priv *priv, u8 op) -{ - core_writel(priv, ARLA_VTBL_STDN | op, CORE_ARLA_VTBL_RWCTRL); - - return bcm_sf2_vlan_op_wait(priv); -} - -static void bcm_sf2_sw_configure_vlan(struct dsa_switch *ds) -{ - struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - unsigned int port; - - /* Clear all VLANs */ - bcm_sf2_vlan_op(priv, ARLA_VTBL_CMD_CLEAR); - - for (port = 0; port < priv->hw_params.num_ports; port++) { - if (!((1 << port) & ds->enabled_port_mask)) - continue; - - core_writel(priv, 1, CORE_DEFAULT_1Q_TAG_P(port)); - } -} - static int bcm_sf2_sw_setup(struct dsa_switch *ds) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); @@ -893,7 +747,7 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) /* Enable all valid ports and disable those unused */ for (port = 0; port < priv->hw_params.num_ports; port++) { /* IMP port receives special treatment */ - if ((1 << port) & ds->enabled_port_mask) + if (dsa_is_user_port(ds, port)) bcm_sf2_port_setup(ds, port, NULL); else if (dsa_is_cpu_port(ds, port)) bcm_sf2_imp_setup(ds, port); @@ -901,7 +755,8 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) bcm_sf2_port_disable(ds, port, NULL); } - bcm_sf2_sw_configure_vlan(ds); + b53_configure_vlan(ds); + bcm_sf2_enable_acb(ds); return 0; } @@ -1020,8 +875,8 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .set_wol = bcm_sf2_sw_set_wol, .port_enable = bcm_sf2_port_setup, .port_disable = bcm_sf2_port_disable, - .get_mac_eee = bcm_sf2_sw_get_mac_eee, - .set_mac_eee = bcm_sf2_sw_set_mac_eee, + .get_mac_eee = b53_get_mac_eee, + .set_mac_eee = b53_set_mac_eee, .port_bridge_join = b53_br_join, .port_bridge_leave = b53_br_leave, .port_stp_state_set = b53_br_set_stp_state, @@ -1173,6 +1028,7 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev) * permanently used */ set_bit(0, priv->cfp.used); + set_bit(0, priv->cfp.unique); bcm_sf2_identify_ports(priv, dn->child); diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index 02c499f9c56b..cc31e986e6e3 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -48,14 +48,13 @@ struct bcm_sf2_hw_params { struct bcm_sf2_port_status { unsigned int link; - - struct ethtool_eee eee; }; struct bcm_sf2_cfp_priv { /* Mutex protecting concurrent accesses to the CFP registers */ struct mutex lock; DECLARE_BITMAP(used, CFP_NUM_RULES); + DECLARE_BITMAP(unique, CFP_NUM_RULES); unsigned int rules_cnt; }; diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index 8a1da7e67707..b721a2009b50 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -20,37 +20,102 @@ #include "bcm_sf2.h" #include "bcm_sf2_regs.h" -struct cfp_udf_layout { - u8 slices[UDF_NUM_SLICES]; +struct cfp_udf_slice_layout { + u8 slices[UDFS_PER_SLICE]; u32 mask_value; + u32 base_offset; +}; +struct cfp_udf_layout { + struct cfp_udf_slice_layout udfs[UDF_NUM_SLICES]; }; +static const u8 zero_slice[UDFS_PER_SLICE] = { }; + /* UDF slices layout for a TCPv4/UDPv4 specification */ static const struct cfp_udf_layout udf_tcpip4_layout = { - .slices = { - /* End of L2, byte offset 12, src IP[0:15] */ - CFG_UDF_EOL2 | 6, - /* End of L2, byte offset 14, src IP[16:31] */ - CFG_UDF_EOL2 | 7, - /* End of L2, byte offset 16, dst IP[0:15] */ - CFG_UDF_EOL2 | 8, - /* End of L2, byte offset 18, dst IP[16:31] */ - CFG_UDF_EOL2 | 9, - /* End of L3, byte offset 0, src port */ - CFG_UDF_EOL3 | 0, - /* End of L3, byte offset 2, dst port */ - CFG_UDF_EOL3 | 1, - 0, 0, 0 + .udfs = { + [1] = { + .slices = { + /* End of L2, byte offset 12, src IP[0:15] */ + CFG_UDF_EOL2 | 6, + /* End of L2, byte offset 14, src IP[16:31] */ + CFG_UDF_EOL2 | 7, + /* End of L2, byte offset 16, dst IP[0:15] */ + CFG_UDF_EOL2 | 8, + /* End of L2, byte offset 18, dst IP[16:31] */ + CFG_UDF_EOL2 | 9, + /* End of L3, byte offset 0, src port */ + CFG_UDF_EOL3 | 0, + /* End of L3, byte offset 2, dst port */ + CFG_UDF_EOL3 | 1, + 0, 0, 0 + }, + .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, + .base_offset = CORE_UDF_0_A_0_8_PORT_0 + UDF_SLICE_OFFSET, + }, + }, +}; + +/* UDF slices layout for a TCPv6/UDPv6 specification */ +static const struct cfp_udf_layout udf_tcpip6_layout = { + .udfs = { + [0] = { + .slices = { + /* End of L2, byte offset 8, src IP[0:15] */ + CFG_UDF_EOL2 | 4, + /* End of L2, byte offset 10, src IP[16:31] */ + CFG_UDF_EOL2 | 5, + /* End of L2, byte offset 12, src IP[32:47] */ + CFG_UDF_EOL2 | 6, + /* End of L2, byte offset 14, src IP[48:63] */ + CFG_UDF_EOL2 | 7, + /* End of L2, byte offset 16, src IP[64:79] */ + CFG_UDF_EOL2 | 8, + /* End of L2, byte offset 18, src IP[80:95] */ + CFG_UDF_EOL2 | 9, + /* End of L2, byte offset 20, src IP[96:111] */ + CFG_UDF_EOL2 | 10, + /* End of L2, byte offset 22, src IP[112:127] */ + CFG_UDF_EOL2 | 11, + /* End of L3, byte offset 0, src port */ + CFG_UDF_EOL3 | 0, + }, + .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, + .base_offset = CORE_UDF_0_B_0_8_PORT_0, + }, + [3] = { + .slices = { + /* End of L2, byte offset 24, dst IP[0:15] */ + CFG_UDF_EOL2 | 12, + /* End of L2, byte offset 26, dst IP[16:31] */ + CFG_UDF_EOL2 | 13, + /* End of L2, byte offset 28, dst IP[32:47] */ + CFG_UDF_EOL2 | 14, + /* End of L2, byte offset 30, dst IP[48:63] */ + CFG_UDF_EOL2 | 15, + /* End of L2, byte offset 32, dst IP[64:79] */ + CFG_UDF_EOL2 | 16, + /* End of L2, byte offset 34, dst IP[80:95] */ + CFG_UDF_EOL2 | 17, + /* End of L2, byte offset 36, dst IP[96:111] */ + CFG_UDF_EOL2 | 18, + /* End of L2, byte offset 38, dst IP[112:127] */ + CFG_UDF_EOL2 | 19, + /* End of L3, byte offset 2, dst port */ + CFG_UDF_EOL3 | 1, + }, + .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, + .base_offset = CORE_UDF_0_D_0_11_PORT_0, + }, }, - .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, }; static inline unsigned int bcm_sf2_get_num_udf_slices(const u8 *layout) { unsigned int i, count = 0; - for (i = 0; i < UDF_NUM_SLICES; i++) { + for (i = 0; i < UDFS_PER_SLICE; i++) { if (layout[i] != 0) count++; } @@ -58,15 +123,42 @@ static inline unsigned int bcm_sf2_get_num_udf_slices(const u8 *layout) return count; } +static inline u32 udf_upper_bits(unsigned int num_udf) +{ + return GENMASK(num_udf - 1, 0) >> (UDFS_PER_SLICE - 1); +} + +static inline u32 udf_lower_bits(unsigned int num_udf) +{ + return (u8)GENMASK(num_udf - 1, 0); +} + +static unsigned int bcm_sf2_get_slice_number(const struct cfp_udf_layout *l, + unsigned int start) +{ + const struct cfp_udf_slice_layout *slice_layout; + unsigned int slice_idx; + + for (slice_idx = start; slice_idx < UDF_NUM_SLICES; slice_idx++) { + slice_layout = &l->udfs[slice_idx]; + if (memcmp(slice_layout->slices, zero_slice, + sizeof(zero_slice))) + break; + } + + return slice_idx; +} + static void bcm_sf2_cfp_udf_set(struct bcm_sf2_priv *priv, - unsigned int slice_num, - const u8 *layout) + const struct cfp_udf_layout *layout, + unsigned int slice_num) { - u32 offset = CORE_UDF_0_A_0_8_PORT_0 + slice_num * UDF_SLICE_OFFSET; + u32 offset = layout->udfs[slice_num].base_offset; unsigned int i; - for (i = 0; i < UDF_NUM_SLICES; i++) - core_writel(priv, layout[i], offset + i * 4); + for (i = 0; i < UDFS_PER_SLICE; i++) + core_writel(priv, layout->udfs[slice_num].slices[i], + offset + i * 4); } static int bcm_sf2_cfp_op(struct bcm_sf2_priv *priv, unsigned int op) @@ -112,69 +204,177 @@ static inline unsigned int bcm_sf2_cfp_rule_size(struct bcm_sf2_priv *priv) return priv->num_cfp_rules - 1; } -static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, - struct ethtool_rx_flow_spec *fs) +static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv, + unsigned int rule_index, + unsigned int port_num, + unsigned int queue_num, + bool fwd_map_change) { - struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - struct ethtool_tcpip4_spec *v4_spec; - const struct cfp_udf_layout *layout; - unsigned int slice_num, rule_index; - unsigned int queue_num, port_num; - u8 ip_proto, ip_frag; - u8 num_udf; - u32 reg; int ret; + u32 reg; - /* Check for unsupported extensions */ - if ((fs->flow_type & FLOW_EXT) && - (fs->m_ext.vlan_etype || fs->m_ext.data[1])) - return -EINVAL; + /* Replace ARL derived destination with DST_MAP derived, define + * which port and queue this should be forwarded to. + */ + if (fwd_map_change) + reg = CHANGE_FWRD_MAP_IB_REP_ARL | + BIT(port_num + DST_MAP_IB_SHIFT) | + CHANGE_TC | queue_num << NEW_TC_SHIFT; + else + reg = 0; - if (fs->location != RX_CLS_LOC_ANY && - test_bit(fs->location, priv->cfp.used)) - return -EBUSY; + core_writel(priv, reg, CORE_ACT_POL_DATA0); - if (fs->location != RX_CLS_LOC_ANY && - fs->location > bcm_sf2_cfp_rule_size(priv)) - return -EINVAL; + /* Set classification ID that needs to be put in Broadcom tag */ + core_writel(priv, rule_index << CHAIN_ID_SHIFT, CORE_ACT_POL_DATA1); - ip_frag = be32_to_cpu(fs->m_ext.data[0]); + core_writel(priv, 0, CORE_ACT_POL_DATA2); - /* We do not support discarding packets, check that the - * destination port is enabled and that we are within the - * number of ports supported by the switch + /* Configure policer RAM now */ + ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | ACT_POL_RAM); + if (ret) { + pr_err("Policer entry at %d failed\n", rule_index); + return ret; + } + + /* Disable the policer */ + core_writel(priv, POLICER_MODE_DISABLE, CORE_RATE_METER0); + + /* Now the rate meter */ + ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | RATE_METER_RAM); + if (ret) { + pr_err("Meter entry at %d failed\n", rule_index); + return ret; + } + + return 0; +} + +static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv, + struct ethtool_tcpip4_spec *v4_spec, + unsigned int slice_num, + bool mask) +{ + u32 reg, offset; + + /* C-Tag [31:24] + * UDF_n_A8 [23:8] + * UDF_n_A7 [7:0] */ - port_num = fs->ring_cookie / 8; + reg = 0; + if (mask) + offset = CORE_CFP_MASK_PORT(4); + else + offset = CORE_CFP_DATA_PORT(4); + core_writel(priv, reg, offset); - if (fs->ring_cookie == RX_CLS_FLOW_DISC || - !(BIT(port_num) & ds->enabled_port_mask) || - port_num >= priv->hw_params.num_ports) - return -EINVAL; + /* UDF_n_A7 [31:24] + * UDF_n_A6 [23:8] + * UDF_n_A5 [7:0] + */ + reg = be16_to_cpu(v4_spec->pdst) >> 8; + if (mask) + offset = CORE_CFP_MASK_PORT(3); + else + offset = CORE_CFP_DATA_PORT(3); + core_writel(priv, reg, offset); + + /* UDF_n_A5 [31:24] + * UDF_n_A4 [23:8] + * UDF_n_A3 [7:0] + */ + reg = (be16_to_cpu(v4_spec->pdst) & 0xff) << 24 | + (u32)be16_to_cpu(v4_spec->psrc) << 8 | + (be32_to_cpu(v4_spec->ip4dst) & 0x0000ff00) >> 8; + if (mask) + offset = CORE_CFP_MASK_PORT(2); + else + offset = CORE_CFP_DATA_PORT(2); + core_writel(priv, reg, offset); + + /* UDF_n_A3 [31:24] + * UDF_n_A2 [23:8] + * UDF_n_A1 [7:0] + */ + reg = (u32)(be32_to_cpu(v4_spec->ip4dst) & 0xff) << 24 | + (u32)(be32_to_cpu(v4_spec->ip4dst) >> 16) << 8 | + (be32_to_cpu(v4_spec->ip4src) & 0x0000ff00) >> 8; + if (mask) + offset = CORE_CFP_MASK_PORT(1); + else + offset = CORE_CFP_DATA_PORT(1); + core_writel(priv, reg, offset); + + /* UDF_n_A1 [31:24] + * UDF_n_A0 [23:8] + * Reserved [7:4] + * Slice ID [3:2] + * Slice valid [1:0] + */ + reg = (u32)(be32_to_cpu(v4_spec->ip4src) & 0xff) << 24 | + (u32)(be32_to_cpu(v4_spec->ip4src) >> 16) << 8 | + SLICE_NUM(slice_num) | SLICE_VALID; + if (mask) + offset = CORE_CFP_MASK_PORT(0); + else + offset = CORE_CFP_DATA_PORT(0); + core_writel(priv, reg, offset); +} + +static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, + unsigned int port_num, + unsigned int queue_num, + struct ethtool_rx_flow_spec *fs) +{ + struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec; + const struct cfp_udf_layout *layout; + unsigned int slice_num, rule_index; + u8 ip_proto, ip_frag; + u8 num_udf; + u32 reg; + int ret; switch (fs->flow_type & ~FLOW_EXT) { case TCP_V4_FLOW: ip_proto = IPPROTO_TCP; v4_spec = &fs->h_u.tcp_ip4_spec; + v4_m_spec = &fs->m_u.tcp_ip4_spec; break; case UDP_V4_FLOW: ip_proto = IPPROTO_UDP; v4_spec = &fs->h_u.udp_ip4_spec; + v4_m_spec = &fs->m_u.udp_ip4_spec; break; default: return -EINVAL; } - /* We only use one UDF slice for now */ - slice_num = 1; + ip_frag = be32_to_cpu(fs->m_ext.data[0]); + + /* Locate the first rule available */ + if (fs->location == RX_CLS_LOC_ANY) + rule_index = find_first_zero_bit(priv->cfp.used, + bcm_sf2_cfp_rule_size(priv)); + else + rule_index = fs->location; + layout = &udf_tcpip4_layout; - num_udf = bcm_sf2_get_num_udf_slices(layout->slices); + /* We only use one UDF slice for now */ + slice_num = bcm_sf2_get_slice_number(layout, 0); + if (slice_num == UDF_NUM_SLICES) + return -EINVAL; + + num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); /* Apply the UDF layout for this filter */ - bcm_sf2_cfp_udf_set(priv, slice_num, layout->slices); + bcm_sf2_cfp_udf_set(priv, layout, slice_num); /* Apply to all packets received through this port */ core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7)); + /* Source port map match */ + core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); + /* S-Tag status [31:30] * C-Tag status [29:28] * L2 framing [27:26] @@ -189,143 +389,398 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, * Reserved [1] * UDF_Valid[8] [0] */ - core_writel(priv, v4_spec->tos << 16 | ip_proto << 8 | ip_frag << 7, + core_writel(priv, v4_spec->tos << IPTOS_SHIFT | + ip_proto << IPPROTO_SHIFT | ip_frag << IP_FRAG_SHIFT | + udf_upper_bits(num_udf), CORE_CFP_DATA_PORT(6)); + /* Mask with the specific layout for IPv4 packets */ + core_writel(priv, layout->udfs[slice_num].mask_value | + udf_upper_bits(num_udf), CORE_CFP_MASK_PORT(6)); + /* UDF_Valid[7:0] [31:24] * S-Tag [23:8] * C-Tag [7:0] */ - core_writel(priv, GENMASK(num_udf - 1, 0) << 24, CORE_CFP_DATA_PORT(5)); + core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); + + /* Mask all but valid UDFs */ + core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); + + /* Program the match and the mask */ + bcm_sf2_cfp_slice_ipv4(priv, v4_spec, slice_num, false); + bcm_sf2_cfp_slice_ipv4(priv, v4_m_spec, SLICE_NUM_MASK, true); + + /* Insert into TCAM now */ + bcm_sf2_cfp_rule_addr_set(priv, rule_index); + + ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); + if (ret) { + pr_err("TCAM entry at addr %d failed\n", rule_index); + return ret; + } + + /* Insert into Action and policer RAMs now */ + ret = bcm_sf2_cfp_act_pol_set(priv, rule_index, port_num, + queue_num, true); + if (ret) + return ret; + + /* Turn on CFP for this rule now */ + reg = core_readl(priv, CORE_CFP_CTL_REG); + reg |= BIT(port); + core_writel(priv, reg, CORE_CFP_CTL_REG); + + /* Flag the rule as being used and return it */ + set_bit(rule_index, priv->cfp.used); + set_bit(rule_index, priv->cfp.unique); + fs->location = rule_index; + + return 0; +} + +static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv, + const __be32 *ip6_addr, const __be16 port, + unsigned int slice_num, + bool mask) +{ + u32 reg, tmp, val, offset; /* C-Tag [31:24] - * UDF_n_A8 [23:8] - * UDF_n_A7 [7:0] + * UDF_n_B8 [23:8] (port) + * UDF_n_B7 (upper) [7:0] (addr[15:8]) */ - core_writel(priv, 0, CORE_CFP_DATA_PORT(4)); + reg = be32_to_cpu(ip6_addr[3]); + val = (u32)be16_to_cpu(port) << 8 | ((reg >> 8) & 0xff); + if (mask) + offset = CORE_CFP_MASK_PORT(4); + else + offset = CORE_CFP_DATA_PORT(4); + core_writel(priv, val, offset); - /* UDF_n_A7 [31:24] - * UDF_n_A6 [23:8] - * UDF_n_A5 [7:0] + /* UDF_n_B7 (lower) [31:24] (addr[7:0]) + * UDF_n_B6 [23:8] (addr[31:16]) + * UDF_n_B5 (upper) [7:0] (addr[47:40]) */ - core_writel(priv, be16_to_cpu(v4_spec->pdst) >> 8, - CORE_CFP_DATA_PORT(3)); + tmp = be32_to_cpu(ip6_addr[2]); + val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 | + ((tmp >> 8) & 0xff); + if (mask) + offset = CORE_CFP_MASK_PORT(3); + else + offset = CORE_CFP_DATA_PORT(3); + core_writel(priv, val, offset); - /* UDF_n_A5 [31:24] - * UDF_n_A4 [23:8] - * UDF_n_A3 [7:0] + /* UDF_n_B5 (lower) [31:24] (addr[39:32]) + * UDF_n_B4 [23:8] (addr[63:48]) + * UDF_n_B3 (upper) [7:0] (addr[79:72]) */ - reg = (be16_to_cpu(v4_spec->pdst) & 0xff) << 24 | - (u32)be16_to_cpu(v4_spec->psrc) << 8 | - (be32_to_cpu(v4_spec->ip4dst) & 0x0000ff00) >> 8; - core_writel(priv, reg, CORE_CFP_DATA_PORT(2)); + reg = be32_to_cpu(ip6_addr[1]); + val = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 | + ((reg >> 8) & 0xff); + if (mask) + offset = CORE_CFP_MASK_PORT(2); + else + offset = CORE_CFP_DATA_PORT(2); + core_writel(priv, val, offset); - /* UDF_n_A3 [31:24] - * UDF_n_A2 [23:8] - * UDF_n_A1 [7:0] + /* UDF_n_B3 (lower) [31:24] (addr[71:64]) + * UDF_n_B2 [23:8] (addr[95:80]) + * UDF_n_B1 (upper) [7:0] (addr[111:104]) */ - reg = (u32)(be32_to_cpu(v4_spec->ip4dst) & 0xff) << 24 | - (u32)(be32_to_cpu(v4_spec->ip4dst) >> 16) << 8 | - (be32_to_cpu(v4_spec->ip4src) & 0x0000ff00) >> 8; - core_writel(priv, reg, CORE_CFP_DATA_PORT(1)); + tmp = be32_to_cpu(ip6_addr[0]); + val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 | + ((tmp >> 8) & 0xff); + if (mask) + offset = CORE_CFP_MASK_PORT(1); + else + offset = CORE_CFP_DATA_PORT(1); + core_writel(priv, val, offset); - /* UDF_n_A1 [31:24] - * UDF_n_A0 [23:8] + /* UDF_n_B1 (lower) [31:24] (addr[103:96]) + * UDF_n_B0 [23:8] (addr[127:112]) * Reserved [7:4] * Slice ID [3:2] * Slice valid [1:0] */ - reg = (u32)(be32_to_cpu(v4_spec->ip4src) & 0xff) << 24 | - (u32)(be32_to_cpu(v4_spec->ip4src) >> 16) << 8 | - SLICE_NUM(slice_num) | SLICE_VALID; - core_writel(priv, reg, CORE_CFP_DATA_PORT(0)); + reg = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 | + SLICE_NUM(slice_num) | SLICE_VALID; + if (mask) + offset = CORE_CFP_MASK_PORT(0); + else + offset = CORE_CFP_DATA_PORT(0); + core_writel(priv, reg, offset); +} - /* Source port map match */ - core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); +static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, + unsigned int port_num, + unsigned int queue_num, + struct ethtool_rx_flow_spec *fs) +{ + struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec; + unsigned int slice_num, rule_index[2]; + const struct cfp_udf_layout *layout; + u8 ip_proto, ip_frag; + int ret = 0; + u8 num_udf; + u32 reg; - /* Mask with the specific layout for IPv4 packets */ - core_writel(priv, layout->mask_value, CORE_CFP_MASK_PORT(6)); + switch (fs->flow_type & ~FLOW_EXT) { + case TCP_V6_FLOW: + ip_proto = IPPROTO_TCP; + v6_spec = &fs->h_u.tcp_ip6_spec; + v6_m_spec = &fs->m_u.tcp_ip6_spec; + break; + case UDP_V6_FLOW: + ip_proto = IPPROTO_UDP; + v6_spec = &fs->h_u.udp_ip6_spec; + v6_m_spec = &fs->m_u.udp_ip6_spec; + break; + default: + return -EINVAL; + } - /* Mask all but valid UDFs */ - core_writel(priv, GENMASK(num_udf - 1, 0) << 24, CORE_CFP_MASK_PORT(5)); + ip_frag = be32_to_cpu(fs->m_ext.data[0]); - /* Mask all */ - core_writel(priv, 0, CORE_CFP_MASK_PORT(4)); + layout = &udf_tcpip6_layout; + slice_num = bcm_sf2_get_slice_number(layout, 0); + if (slice_num == UDF_NUM_SLICES) + return -EINVAL; - /* All other UDFs should be matched with the filter */ - core_writel(priv, 0xff, CORE_CFP_MASK_PORT(3)); - core_writel(priv, 0xffffffff, CORE_CFP_MASK_PORT(2)); - core_writel(priv, 0xffffffff, CORE_CFP_MASK_PORT(1)); - core_writel(priv, 0xffffff0f, CORE_CFP_MASK_PORT(0)); + num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); - /* Locate the first rule available */ + /* Negotiate two indexes, one for the second half which we are chained + * from, which is what we will return to user-space, and a second one + * which is used to store its first half. That first half does not + * allow any choice of placement, so it just needs to find the next + * available bit. We return the second half as fs->location because + * that helps with the rule lookup later on since the second half is + * chained from its first half, we can easily identify IPv6 CFP rules + * by looking whether they carry a CHAIN_ID. + * + * We also want the second half to have a lower rule_index than its + * first half because the HW search is by incrementing addresses. + */ if (fs->location == RX_CLS_LOC_ANY) - rule_index = find_first_zero_bit(priv->cfp.used, - bcm_sf2_cfp_rule_size(priv)); + rule_index[0] = find_first_zero_bit(priv->cfp.used, + bcm_sf2_cfp_rule_size(priv)); else - rule_index = fs->location; + rule_index[0] = fs->location; - /* Insert into TCAM now */ - bcm_sf2_cfp_rule_addr_set(priv, rule_index); + /* Flag it as used (cleared on error path) such that we can immediately + * obtain a second one to chain from. + */ + set_bit(rule_index[0], priv->cfp.used); - ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); - if (ret) { - pr_err("TCAM entry at addr %d failed\n", rule_index); - return ret; + rule_index[1] = find_first_zero_bit(priv->cfp.used, + bcm_sf2_cfp_rule_size(priv)); + if (rule_index[1] > bcm_sf2_cfp_rule_size(priv)) { + ret = -ENOSPC; + goto out_err; } - /* Replace ARL derived destination with DST_MAP derived, define - * which port and queue this should be forwarded to. - * - * We have a small oddity where Port 6 just does not have a - * valid bit here (so we subtract by one). + /* Apply the UDF layout for this filter */ + bcm_sf2_cfp_udf_set(priv, layout, slice_num); + + /* Apply to all packets received through this port */ + core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7)); + + /* Source port map match */ + core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); + + /* S-Tag status [31:30] + * C-Tag status [29:28] + * L2 framing [27:26] + * L3 framing [25:24] + * IP ToS [23:16] + * IP proto [15:08] + * IP Fragm [7] + * Non 1st frag [6] + * IP Authen [5] + * TTL range [4:3] + * PPPoE session [2] + * Reserved [1] + * UDF_Valid[8] [0] */ - queue_num = fs->ring_cookie % 8; - if (port_num >= 7) - port_num -= 1; + reg = 1 << L3_FRAMING_SHIFT | ip_proto << IPPROTO_SHIFT | + ip_frag << IP_FRAG_SHIFT | udf_upper_bits(num_udf); + core_writel(priv, reg, CORE_CFP_DATA_PORT(6)); - reg = CHANGE_FWRD_MAP_IB_REP_ARL | BIT(port_num + DST_MAP_IB_SHIFT) | - CHANGE_TC | queue_num << NEW_TC_SHIFT; + /* Mask with the specific layout for IPv6 packets including + * UDF_Valid[8] + */ + reg = layout->udfs[slice_num].mask_value | udf_upper_bits(num_udf); + core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); - core_writel(priv, reg, CORE_ACT_POL_DATA0); + /* UDF_Valid[7:0] [31:24] + * S-Tag [23:8] + * C-Tag [7:0] + */ + core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); - /* Set classification ID that needs to be put in Broadcom tag */ - core_writel(priv, rule_index << CHAIN_ID_SHIFT, - CORE_ACT_POL_DATA1); + /* Mask all but valid UDFs */ + core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); - core_writel(priv, 0, CORE_ACT_POL_DATA2); + /* Slice the IPv6 source address and port */ + bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6src, v6_spec->psrc, + slice_num, false); + bcm_sf2_cfp_slice_ipv6(priv, v6_m_spec->ip6src, v6_m_spec->psrc, + slice_num, true); - /* Configure policer RAM now */ - ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | ACT_POL_RAM); + /* Insert into TCAM now because we need to insert a second rule */ + bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]); + + ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); if (ret) { - pr_err("Policer entry at %d failed\n", rule_index); - return ret; + pr_err("TCAM entry at addr %d failed\n", rule_index[0]); + goto out_err; } - /* Disable the policer */ - core_writel(priv, POLICER_MODE_DISABLE, CORE_RATE_METER0); + /* Insert into Action and policer RAMs now */ + ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port_num, + queue_num, false); + if (ret) + goto out_err; - /* Now the rate meter */ - ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | RATE_METER_RAM); + /* Now deal with the second slice to chain this rule */ + slice_num = bcm_sf2_get_slice_number(layout, slice_num + 1); + if (slice_num == UDF_NUM_SLICES) { + ret = -EINVAL; + goto out_err; + } + + num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); + + /* Apply the UDF layout for this filter */ + bcm_sf2_cfp_udf_set(priv, layout, slice_num); + + /* Chained rule, source port match is coming from the rule we are + * chained from. + */ + core_writel(priv, 0, CORE_CFP_DATA_PORT(7)); + core_writel(priv, 0, CORE_CFP_MASK_PORT(7)); + + /* + * CHAIN ID [31:24] chain to previous slice + * Reserved [23:20] + * UDF_Valid[11:8] [19:16] + * UDF_Valid[7:0] [15:8] + * UDF_n_D11 [7:0] + */ + reg = rule_index[0] << 24 | udf_upper_bits(num_udf) << 16 | + udf_lower_bits(num_udf) << 8; + core_writel(priv, reg, CORE_CFP_DATA_PORT(6)); + + /* Mask all except chain ID, UDF Valid[8] and UDF Valid[7:0] */ + reg = XCESS_ADDR_MASK << 24 | udf_upper_bits(num_udf) << 16 | + udf_lower_bits(num_udf) << 8; + core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); + + /* Don't care */ + core_writel(priv, 0, CORE_CFP_DATA_PORT(5)); + + /* Mask all */ + core_writel(priv, 0, CORE_CFP_MASK_PORT(5)); + + bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6dst, v6_spec->pdst, slice_num, + false); + bcm_sf2_cfp_slice_ipv6(priv, v6_m_spec->ip6dst, v6_m_spec->pdst, + SLICE_NUM_MASK, true); + + /* Insert into TCAM now */ + bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]); + + ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); if (ret) { - pr_err("Meter entry at %d failed\n", rule_index); - return ret; + pr_err("TCAM entry at addr %d failed\n", rule_index[1]); + goto out_err; } + /* Insert into Action and policer RAMs now, set chain ID to + * the one we are chained to + */ + ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port_num, + queue_num, true); + if (ret) + goto out_err; + /* Turn on CFP for this rule now */ reg = core_readl(priv, CORE_CFP_CTL_REG); reg |= BIT(port); core_writel(priv, reg, CORE_CFP_CTL_REG); - /* Flag the rule as being used and return it */ - set_bit(rule_index, priv->cfp.used); - fs->location = rule_index; + /* Flag the second half rule as being used now, return it as the + * location, and flag it as unique while dumping rules + */ + set_bit(rule_index[1], priv->cfp.used); + set_bit(rule_index[1], priv->cfp.unique); + fs->location = rule_index[1]; - return 0; + return ret; + +out_err: + clear_bit(rule_index[0], priv->cfp.used); + return ret; } -static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, - u32 loc) +static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, + struct ethtool_rx_flow_spec *fs) +{ + struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); + unsigned int queue_num, port_num; + int ret = -EINVAL; + + /* Check for unsupported extensions */ + if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype || + fs->m_ext.data[1])) + return -EINVAL; + + if (fs->location != RX_CLS_LOC_ANY && + test_bit(fs->location, priv->cfp.used)) + return -EBUSY; + + if (fs->location != RX_CLS_LOC_ANY && + fs->location > bcm_sf2_cfp_rule_size(priv)) + return -EINVAL; + + /* We do not support discarding packets, check that the + * destination port is enabled and that we are within the + * number of ports supported by the switch + */ + port_num = fs->ring_cookie / SF2_NUM_EGRESS_QUEUES; + + if (fs->ring_cookie == RX_CLS_FLOW_DISC || + !dsa_is_user_port(ds, port_num) || + port_num >= priv->hw_params.num_ports) + return -EINVAL; + /* + * We have a small oddity where Port 6 just does not have a + * valid bit here (so we substract by one). + */ + queue_num = fs->ring_cookie % SF2_NUM_EGRESS_QUEUES; + if (port_num >= 7) + port_num -= 1; + + switch (fs->flow_type & ~FLOW_EXT) { + case TCP_V4_FLOW: + case UDP_V4_FLOW: + ret = bcm_sf2_cfp_ipv4_rule_set(priv, port, port_num, + queue_num, fs); + break; + case TCP_V6_FLOW: + case UDP_V6_FLOW: + ret = bcm_sf2_cfp_ipv6_rule_set(priv, port, port_num, + queue_num, fs); + break; + default: + break; + } + + return ret; +} + +static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port, + u32 loc, u32 *next_loc) { int ret; u32 reg; @@ -341,6 +796,14 @@ static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, if (ret) return ret; + /* Check if this is possibly an IPv6 rule that would + * indicate we need to delete its companion rule + * as well + */ + reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); + if (next_loc) + *next_loc = (reg >> 24) & CHAIN_ID_MASK; + /* Clear its valid bits */ reg = core_readl(priv, CORE_CFP_DATA_PORT(0)); reg &= ~SLICE_VALID; @@ -352,10 +815,28 @@ static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, return ret; clear_bit(loc, priv->cfp.used); + clear_bit(loc, priv->cfp.unique); return 0; } +static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, + u32 loc) +{ + u32 next_loc = 0; + int ret; + + ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc); + if (ret) + return ret; + + /* If this was an IPv6 rule, delete is companion rule too */ + if (next_loc) + ret = bcm_sf2_cfp_rule_del_one(priv, port, next_loc, NULL); + + return ret; +} + static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow) { unsigned int i; @@ -369,93 +850,63 @@ static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow) flow->m_ext.data[1] ^= cpu_to_be32(~0); } -static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port, - struct ethtool_rxnfc *nfc, bool search) +static int bcm_sf2_cfp_unslice_ipv4(struct bcm_sf2_priv *priv, + struct ethtool_tcpip4_spec *v4_spec, + bool mask) { - struct ethtool_tcpip4_spec *v4_spec; - unsigned int queue_num; + u32 reg, offset, ipv4; u16 src_dst_port; - u32 reg, ipv4; - int ret; - - if (!search) { - bcm_sf2_cfp_rule_addr_set(priv, nfc->fs.location); - - ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | ACT_POL_RAM); - if (ret) - return ret; - - reg = core_readl(priv, CORE_ACT_POL_DATA0); - ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); - if (ret) - return ret; - } else { - reg = core_readl(priv, CORE_ACT_POL_DATA0); - } - - /* Extract the destination port */ - nfc->fs.ring_cookie = fls((reg >> DST_MAP_IB_SHIFT) & - DST_MAP_IB_MASK) - 1; - - /* There is no Port 6, so we compensate for that here */ - if (nfc->fs.ring_cookie >= 6) - nfc->fs.ring_cookie++; - nfc->fs.ring_cookie *= 8; - - /* Extract the destination queue */ - queue_num = (reg >> NEW_TC_SHIFT) & NEW_TC_MASK; - nfc->fs.ring_cookie += queue_num; - - /* Extract the IP protocol */ - reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); - switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) { - case IPPROTO_TCP: - nfc->fs.flow_type = TCP_V4_FLOW; - v4_spec = &nfc->fs.h_u.tcp_ip4_spec; - break; - case IPPROTO_UDP: - nfc->fs.flow_type = UDP_V4_FLOW; - v4_spec = &nfc->fs.h_u.udp_ip4_spec; - break; - default: - /* Clear to exit the search process */ - if (search) - core_readl(priv, CORE_CFP_DATA_PORT(7)); - return -EINVAL; - } - - v4_spec->tos = (reg >> 16) & IPPROTO_MASK; - nfc->fs.m_ext.data[0] = cpu_to_be32((reg >> 7) & 1); + if (mask) + offset = CORE_CFP_MASK_PORT(3); + else + offset = CORE_CFP_DATA_PORT(3); - reg = core_readl(priv, CORE_CFP_DATA_PORT(3)); + reg = core_readl(priv, offset); /* src port [15:8] */ src_dst_port = reg << 8; - reg = core_readl(priv, CORE_CFP_DATA_PORT(2)); + if (mask) + offset = CORE_CFP_MASK_PORT(2); + else + offset = CORE_CFP_DATA_PORT(2); + + reg = core_readl(priv, offset); /* src port [7:0] */ src_dst_port |= (reg >> 24); v4_spec->pdst = cpu_to_be16(src_dst_port); - nfc->fs.m_u.tcp_ip4_spec.pdst = cpu_to_be16(~0); v4_spec->psrc = cpu_to_be16((u16)(reg >> 8)); - nfc->fs.m_u.tcp_ip4_spec.psrc = cpu_to_be16(~0); /* IPv4 dst [15:8] */ ipv4 = (reg & 0xff) << 8; - reg = core_readl(priv, CORE_CFP_DATA_PORT(1)); + + if (mask) + offset = CORE_CFP_MASK_PORT(1); + else + offset = CORE_CFP_DATA_PORT(1); + + reg = core_readl(priv, offset); /* IPv4 dst [31:16] */ ipv4 |= ((reg >> 8) & 0xffff) << 16; /* IPv4 dst [7:0] */ ipv4 |= (reg >> 24) & 0xff; v4_spec->ip4dst = cpu_to_be32(ipv4); - nfc->fs.m_u.tcp_ip4_spec.ip4dst = cpu_to_be32(~0); /* IPv4 src [15:8] */ ipv4 = (reg & 0xff) << 8; - reg = core_readl(priv, CORE_CFP_DATA_PORT(0)); - if (!(reg & SLICE_VALID)) + if (mask) + offset = CORE_CFP_MASK_PORT(0); + else + offset = CORE_CFP_DATA_PORT(0); + reg = core_readl(priv, offset); + + /* Once the TCAM is programmed, the mask reflects the slice number + * being matched, don't bother checking it when reading back the + * mask spec + */ + if (!mask && !(reg & SLICE_VALID)) return -EINVAL; /* IPv4 src [7:0] */ @@ -463,7 +914,233 @@ static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port, /* IPv4 src [31:16] */ ipv4 |= ((reg >> 8) & 0xffff) << 16; v4_spec->ip4src = cpu_to_be32(ipv4); - nfc->fs.m_u.tcp_ip4_spec.ip4src = cpu_to_be32(~0); + + return 0; +} + +static int bcm_sf2_cfp_ipv4_rule_get(struct bcm_sf2_priv *priv, int port, + struct ethtool_rx_flow_spec *fs) +{ + struct ethtool_tcpip4_spec *v4_spec = NULL, *v4_m_spec = NULL; + u32 reg; + int ret; + + reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); + + switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) { + case IPPROTO_TCP: + fs->flow_type = TCP_V4_FLOW; + v4_spec = &fs->h_u.tcp_ip4_spec; + v4_m_spec = &fs->m_u.tcp_ip4_spec; + break; + case IPPROTO_UDP: + fs->flow_type = UDP_V4_FLOW; + v4_spec = &fs->h_u.udp_ip4_spec; + v4_m_spec = &fs->m_u.udp_ip4_spec; + break; + default: + return -EINVAL; + } + + fs->m_ext.data[0] = cpu_to_be32((reg >> IP_FRAG_SHIFT) & 1); + v4_spec->tos = (reg >> IPTOS_SHIFT) & IPTOS_MASK; + + ret = bcm_sf2_cfp_unslice_ipv4(priv, v4_spec, false); + if (ret) + return ret; + + return bcm_sf2_cfp_unslice_ipv4(priv, v4_m_spec, true); +} + +static int bcm_sf2_cfp_unslice_ipv6(struct bcm_sf2_priv *priv, + __be32 *ip6_addr, __be16 *port, + bool mask) +{ + u32 reg, tmp, offset; + + /* C-Tag [31:24] + * UDF_n_B8 [23:8] (port) + * UDF_n_B7 (upper) [7:0] (addr[15:8]) + */ + if (mask) + offset = CORE_CFP_MASK_PORT(4); + else + offset = CORE_CFP_DATA_PORT(4); + reg = core_readl(priv, offset); + *port = cpu_to_be32(reg) >> 8; + tmp = (u32)(reg & 0xff) << 8; + + /* UDF_n_B7 (lower) [31:24] (addr[7:0]) + * UDF_n_B6 [23:8] (addr[31:16]) + * UDF_n_B5 (upper) [7:0] (addr[47:40]) + */ + if (mask) + offset = CORE_CFP_MASK_PORT(3); + else + offset = CORE_CFP_DATA_PORT(3); + reg = core_readl(priv, offset); + tmp |= (reg >> 24) & 0xff; + tmp |= (u32)((reg >> 8) << 16); + ip6_addr[3] = cpu_to_be32(tmp); + tmp = (u32)(reg & 0xff) << 8; + + /* UDF_n_B5 (lower) [31:24] (addr[39:32]) + * UDF_n_B4 [23:8] (addr[63:48]) + * UDF_n_B3 (upper) [7:0] (addr[79:72]) + */ + if (mask) + offset = CORE_CFP_MASK_PORT(2); + else + offset = CORE_CFP_DATA_PORT(2); + reg = core_readl(priv, offset); + tmp |= (reg >> 24) & 0xff; + tmp |= (u32)((reg >> 8) << 16); + ip6_addr[2] = cpu_to_be32(tmp); + tmp = (u32)(reg & 0xff) << 8; + + /* UDF_n_B3 (lower) [31:24] (addr[71:64]) + * UDF_n_B2 [23:8] (addr[95:80]) + * UDF_n_B1 (upper) [7:0] (addr[111:104]) + */ + if (mask) + offset = CORE_CFP_MASK_PORT(1); + else + offset = CORE_CFP_DATA_PORT(1); + reg = core_readl(priv, offset); + tmp |= (reg >> 24) & 0xff; + tmp |= (u32)((reg >> 8) << 16); + ip6_addr[1] = cpu_to_be32(tmp); + tmp = (u32)(reg & 0xff) << 8; + + /* UDF_n_B1 (lower) [31:24] (addr[103:96]) + * UDF_n_B0 [23:8] (addr[127:112]) + * Reserved [7:4] + * Slice ID [3:2] + * Slice valid [1:0] + */ + if (mask) + offset = CORE_CFP_MASK_PORT(0); + else + offset = CORE_CFP_DATA_PORT(0); + reg = core_readl(priv, offset); + tmp |= (reg >> 24) & 0xff; + tmp |= (u32)((reg >> 8) << 16); + ip6_addr[0] = cpu_to_be32(tmp); + + if (!mask && !(reg & SLICE_VALID)) + return -EINVAL; + + return 0; +} + +static int bcm_sf2_cfp_ipv6_rule_get(struct bcm_sf2_priv *priv, int port, + struct ethtool_rx_flow_spec *fs, + u32 next_loc) +{ + struct ethtool_tcpip6_spec *v6_spec = NULL, *v6_m_spec = NULL; + u32 reg; + int ret; + + /* UDPv6 and TCPv6 both use ethtool_tcpip6_spec so we are fine + * assuming tcp_ip6_spec here being an union. + */ + v6_spec = &fs->h_u.tcp_ip6_spec; + v6_m_spec = &fs->m_u.tcp_ip6_spec; + + /* Read the second half first */ + ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_spec->ip6dst, &v6_spec->pdst, + false); + if (ret) + return ret; + + ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_m_spec->ip6dst, + &v6_m_spec->pdst, true); + if (ret) + return ret; + + /* Read last to avoid next entry clobbering the results during search + * operations. We would not have the port enabled for this rule, so + * don't bother checking it. + */ + (void)core_readl(priv, CORE_CFP_DATA_PORT(7)); + + /* The slice number is valid, so read the rule we are chained from now + * which is our first half. + */ + bcm_sf2_cfp_rule_addr_set(priv, next_loc); + ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); + if (ret) + return ret; + + reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); + + switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) { + case IPPROTO_TCP: + fs->flow_type = TCP_V6_FLOW; + break; + case IPPROTO_UDP: + fs->flow_type = UDP_V6_FLOW; + break; + default: + return -EINVAL; + } + + ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_spec->ip6src, &v6_spec->psrc, + false); + if (ret) + return ret; + + return bcm_sf2_cfp_unslice_ipv6(priv, v6_m_spec->ip6src, + &v6_m_spec->psrc, true); +} + +static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port, + struct ethtool_rxnfc *nfc) +{ + u32 reg, ipv4_or_chain_id; + unsigned int queue_num; + int ret; + + bcm_sf2_cfp_rule_addr_set(priv, nfc->fs.location); + + ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | ACT_POL_RAM); + if (ret) + return ret; + + reg = core_readl(priv, CORE_ACT_POL_DATA0); + + ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); + if (ret) + return ret; + + /* Extract the destination port */ + nfc->fs.ring_cookie = fls((reg >> DST_MAP_IB_SHIFT) & + DST_MAP_IB_MASK) - 1; + + /* There is no Port 6, so we compensate for that here */ + if (nfc->fs.ring_cookie >= 6) + nfc->fs.ring_cookie++; + nfc->fs.ring_cookie *= SF2_NUM_EGRESS_QUEUES; + + /* Extract the destination queue */ + queue_num = (reg >> NEW_TC_SHIFT) & NEW_TC_MASK; + nfc->fs.ring_cookie += queue_num; + + /* Extract the L3_FRAMING or CHAIN_ID */ + reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); + + /* With IPv6 rules this would contain a non-zero chain ID since + * we reserve entry 0 and it cannot be used. So if we read 0 here + * this means an IPv4 rule. + */ + ipv4_or_chain_id = (reg >> L3_FRAMING_SHIFT) & 0xff; + if (ipv4_or_chain_id == 0) + ret = bcm_sf2_cfp_ipv4_rule_get(priv, port, &nfc->fs); + else + ret = bcm_sf2_cfp_ipv6_rule_get(priv, port, &nfc->fs, + ipv4_or_chain_id); + if (ret) + return ret; /* Read last to avoid next entry clobbering the results during search * operations @@ -486,44 +1163,11 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv, u32 *rule_locs) { unsigned int index = 1, rules_cnt = 0; - int ret; - u32 reg; - /* Do not poll on OP_STR_DONE to be self-clearing for search - * operations, we cannot use bcm_sf2_cfp_op here because it completes - * on clearing OP_STR_DONE which won't clear until the entire search - * operation is over. - */ - reg = core_readl(priv, CORE_CFP_ACC); - reg &= ~(XCESS_ADDR_MASK << XCESS_ADDR_SHIFT); - reg |= index << XCESS_ADDR_SHIFT; - reg &= ~(OP_SEL_MASK | RAM_SEL_MASK); - reg |= OP_SEL_SEARCH | TCAM_SEL | OP_STR_DONE; - core_writel(priv, reg, CORE_CFP_ACC); - - do { - /* Wait for results to be ready */ - reg = core_readl(priv, CORE_CFP_ACC); - - /* Extract the address we are searching */ - index = reg >> XCESS_ADDR_SHIFT; - index &= XCESS_ADDR_MASK; - - /* We have a valid search result, so flag it accordingly */ - if (reg & SEARCH_STS) { - ret = bcm_sf2_cfp_rule_get(priv, port, nfc, true); - if (ret) - continue; - - rule_locs[rules_cnt] = index; - rules_cnt++; - } - - /* Search is over break out */ - if (!(reg & OP_STR_DONE)) - break; - - } while (index < priv->num_cfp_rules); + for_each_set_bit_from(index, priv->cfp.unique, priv->num_cfp_rules) { + rule_locs[rules_cnt] = index; + rules_cnt++; + } /* Put the TCAM size here */ nfc->data = bcm_sf2_cfp_rule_size(priv); @@ -543,13 +1187,13 @@ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, switch (nfc->cmd) { case ETHTOOL_GRXCLSRLCNT: /* Subtract the default, unusable rule */ - nfc->rule_cnt = bitmap_weight(priv->cfp.used, + nfc->rule_cnt = bitmap_weight(priv->cfp.unique, priv->num_cfp_rules) - 1; /* We support specifying rule locations */ nfc->data |= RX_CLS_LOC_SPECIAL; break; case ETHTOOL_GRXCLSRULE: - ret = bcm_sf2_cfp_rule_get(priv, port, nfc, false); + ret = bcm_sf2_cfp_rule_get(priv, port, nfc); break; case ETHTOOL_GRXCLSRLALL: ret = bcm_sf2_cfp_rule_get_all(priv, port, nfc, rule_locs); diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index 49695fcc2ea8..3ccd5a865dcb 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h @@ -115,6 +115,24 @@ enum bcm_sf2_reg_offs { #define P7_IRQ_OFF 0 #define P_IRQ_OFF(x) ((6 - (x)) * P_NUM_IRQ) +/* Register set relative to 'ACB' */ +#define ACB_CONTROL 0x00 +#define ACB_EN (1 << 0) +#define ACB_ALGORITHM (1 << 1) +#define ACB_FLUSH_SHIFT 2 +#define ACB_FLUSH_MASK 0x3 + +#define ACB_QUEUE_0_CFG 0x08 +#define XOFF_THRESHOLD_MASK 0x7ff +#define XON_EN (1 << 11) +#define TOTAL_XOFF_THRESHOLD_SHIFT 12 +#define TOTAL_XOFF_THRESHOLD_MASK 0x7ff +#define TOTAL_XOFF_EN (1 << 23) +#define TOTAL_XON_EN (1 << 24) +#define PKTLEN_SHIFT 25 +#define PKTLEN_MASK 0x3f +#define ACB_QUEUE_CFG(x) (ACB_QUEUE_0_CFG + ((x) * 0x4)) + /* Register set relative to 'CORE' */ #define CORE_G_PCTL_PORT0 0x00000 #define CORE_G_PCTL_PORT(x) (CORE_G_PCTL_PORT0 + (x * 0x4)) @@ -205,16 +223,8 @@ enum bcm_sf2_reg_offs { #define CORE_IMP0_PRT_ID 0x0804 -#define CORE_BRCM_HDR_CTRL 0x0080c -#define BRCM_HDR_EN_P8 (1 << 0) -#define BRCM_HDR_EN_P5 (1 << 1) -#define BRCM_HDR_EN_P7 (1 << 2) - #define CORE_RST_MIB_CNT_EN 0x0950 -#define CORE_BRCM_HDR_RX_DIS 0x0980 -#define CORE_BRCM_HDR_TX_DIS 0x0988 - #define CORE_ARLA_VTBL_RWCTRL 0x1600 #define ARLA_VTBL_CMD_WRITE 0 #define ARLA_VTBL_CMD_READ 1 @@ -245,6 +255,11 @@ enum bcm_sf2_reg_offs { #define CORE_PORT_VLAN_CTL_PORT(x) (0xc400 + ((x) * 0x8)) #define PORT_VLAN_CTRL_MASK 0x1ff +#define CORE_TXQ_THD_PAUSE_QN_PORT_0 0x2c80 +#define TXQ_PAUSE_THD_MASK 0x7ff +#define CORE_TXQ_THD_PAUSE_QN_PORT(x) (CORE_TXQ_THD_PAUSE_QN_PORT_0 + \ + (x) * 0x8) + #define CORE_DEFAULT_1Q_TAG_P(x) (0xd040 + ((x) * 8)) #define CFI_SHIFT 12 #define PRI_SHIFT 13 @@ -252,9 +267,6 @@ enum bcm_sf2_reg_offs { #define CORE_JOIN_ALL_VLAN_EN 0xd140 -#define CORE_EEE_EN_CTRL 0x24800 -#define CORE_EEE_LPI_INDICATE 0x24810 - #define CORE_CFP_ACC 0x28000 #define OP_STR_DONE (1 << 0) #define OP_SEL_SHIFT 1 @@ -290,14 +302,18 @@ enum bcm_sf2_reg_offs { /* UDF_DATA7 */ #define L3_FRAMING_SHIFT 24 #define L3_FRAMING_MASK (0x3 << L3_FRAMING_SHIFT) +#define IPTOS_SHIFT 16 +#define IPTOS_MASK 0xff #define IPPROTO_SHIFT 8 #define IPPROTO_MASK (0xff << IPPROTO_SHIFT) -#define IP_FRAG (1 << 7) +#define IP_FRAG_SHIFT 7 +#define IP_FRAG (1 << IP_FRAG_SHIFT) /* UDF_DATA0 */ #define SLICE_VALID 3 #define SLICE_NUM_SHIFT 2 #define SLICE_NUM(x) ((x) << SLICE_NUM_SHIFT) +#define SLICE_NUM_MASK 0x3 #define CORE_CFP_MASK_PORT_0 0x280c0 @@ -393,8 +409,15 @@ enum bcm_sf2_reg_offs { #define CFG_UDF_EOL2 (2 << CFG_UDF_OFFSET_BASE_SHIFT) #define CFG_UDF_EOL3 (3 << CFG_UDF_OFFSET_BASE_SHIFT) +/* IPv6 slices */ +#define CORE_UDF_0_B_0_8_PORT_0 0x28500 + +/* IPv6 chained slices */ +#define CORE_UDF_0_D_0_11_PORT_0 0x28680 + /* Number of slices for IPv4, IPv6 and non-IP */ -#define UDF_NUM_SLICES 9 +#define UDF_NUM_SLICES 4 +#define UDFS_PER_SLICE 9 /* Spacing between different slices */ #define UDF_SLICE_OFFSET 0x40 diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index d55051abf4ed..bb71d3d6f65b 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -64,7 +64,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) +static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds, + int port) { dev_dbg(ds->dev, "%s\n", __func__); @@ -110,13 +111,6 @@ static void dsa_loop_get_ethtool_stats(struct dsa_switch *ds, int port, data[i] = ps->ports[port].mib[i].val; } -static int dsa_loop_set_addr(struct dsa_switch *ds, u8 *addr) -{ - dev_dbg(ds->dev, "%s\n", __func__); - - return 0; -} - static int dsa_loop_phy_read(struct dsa_switch *ds, int port, int regnum) { struct dsa_loop_priv *ps = ds->priv; @@ -263,7 +257,6 @@ static const struct dsa_switch_ops dsa_loop_driver = { .get_strings = dsa_loop_get_strings, .get_ethtool_stats = dsa_loop_get_ethtool_stats, .get_sset_count = dsa_loop_get_sset_count, - .set_addr = dsa_loop_set_addr, .phy_read = dsa_loop_phy_read, .phy_write = dsa_loop_phy_write, .port_bridge_join = dsa_loop_port_bridge_join, diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index b471413d3df9..b24566bb74d2 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -17,6 +17,9 @@ #include <linux/regmap.h> #include <linux/mutex.h> #include <linux/mii.h> +#include <linux/phy.h> +#include <linux/if_bridge.h> +#include <linux/etherdevice.h> #include "lan9303.h" @@ -57,6 +60,7 @@ #define LAN9303_SWITCH_CSR_CMD_LANES (BIT(19) | BIT(18) | BIT(17) | BIT(16)) #define LAN9303_VIRT_PHY_BASE 0x70 #define LAN9303_VIRT_SPECIAL_CTRL 0x77 +#define LAN9303_VIRT_SPECIAL_TURBO BIT(10) /*Turbo MII Enable*/ /*13.4 Switch Fabric Control and Status Registers * Accessed indirectly via SWITCH_CSR_CMD, SWITCH_CSR_DATA. @@ -121,6 +125,21 @@ #define LAN9303_MAC_RX_CFG_2 0x0c01 #define LAN9303_MAC_TX_CFG_2 0x0c40 #define LAN9303_SWE_ALR_CMD 0x1800 +# define LAN9303_ALR_CMD_MAKE_ENTRY BIT(2) +# define LAN9303_ALR_CMD_GET_FIRST BIT(1) +# define LAN9303_ALR_CMD_GET_NEXT BIT(0) +#define LAN9303_SWE_ALR_WR_DAT_0 0x1801 +#define LAN9303_SWE_ALR_WR_DAT_1 0x1802 +# define LAN9303_ALR_DAT1_VALID BIT(26) +# define LAN9303_ALR_DAT1_END_OF_TABL BIT(25) +# define LAN9303_ALR_DAT1_AGE_OVERRID BIT(25) +# define LAN9303_ALR_DAT1_STATIC BIT(24) +# define LAN9303_ALR_DAT1_PORT_BITOFFS 16 +# define LAN9303_ALR_DAT1_PORT_MASK (7 << LAN9303_ALR_DAT1_PORT_BITOFFS) +#define LAN9303_SWE_ALR_RD_DAT_0 0x1805 +#define LAN9303_SWE_ALR_RD_DAT_1 0x1806 +#define LAN9303_SWE_ALR_CMD_STS 0x1808 +# define ALR_STS_MAKE_PEND BIT(0) #define LAN9303_SWE_VLAN_CMD 0x180b # define LAN9303_SWE_VLAN_CMD_RNW BIT(5) # define LAN9303_SWE_VLAN_CMD_PVIDNVLAN BIT(4) @@ -134,6 +153,8 @@ # define LAN9303_SWE_VLAN_UNTAG_PORT0 BIT(12) #define LAN9303_SWE_VLAN_CMD_STS 0x1810 #define LAN9303_SWE_GLB_INGRESS_CFG 0x1840 +# define LAN9303_SWE_GLB_INGR_IGMP_TRAP BIT(7) +# define LAN9303_SWE_GLB_INGR_IGMP_PORT(p) BIT(10 + p) #define LAN9303_SWE_PORT_STATE 0x1843 # define LAN9303_SWE_PORT_STATE_FORWARDING_PORT2 (0) # define LAN9303_SWE_PORT_STATE_LEARNING_PORT2 BIT(5) @@ -144,6 +165,7 @@ # define LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 (0) # define LAN9303_SWE_PORT_STATE_LEARNING_PORT0 BIT(1) # define LAN9303_SWE_PORT_STATE_BLOCKING_PORT0 BIT(0) +# define LAN9303_SWE_PORT_STATE_DISABLED_PORT0 (3) #define LAN9303_SWE_PORT_MIRROR 0x1846 # define LAN9303_SWE_PORT_MIRROR_SNIFF_ALL BIT(8) # define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT2 BIT(7) @@ -154,7 +176,9 @@ # define LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT0 BIT(2) # define LAN9303_SWE_PORT_MIRROR_ENABLE_RX_MIRRORING BIT(1) # define LAN9303_SWE_PORT_MIRROR_ENABLE_TX_MIRRORING BIT(0) +# define LAN9303_SWE_PORT_MIRROR_DISABLED 0 #define LAN9303_SWE_INGRESS_PORT_TYPE 0x1847 +#define LAN9303_SWE_INGRESS_PORT_TYPE_VLAN 3 #define LAN9303_BM_CFG 0x1c00 #define LAN9303_BM_EGRSS_PORT_TYPE 0x1c0c # define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT2 (BIT(17) | BIT(16)) @@ -262,7 +286,7 @@ static int lan9303_indirect_phy_wait_for_completion(struct lan9303 *chip) } if (!(reg & LAN9303_PMI_ACCESS_MII_BUSY)) return 0; - msleep(1); + usleep_range(1000, 2000); } return -EIO; @@ -354,7 +378,7 @@ static int lan9303_switch_wait_for_completion(struct lan9303 *chip) } if (!(reg & LAN9303_SWITCH_CSR_CMD_BUSY)) return 0; - msleep(1); + usleep_range(1000, 2000); } return -EIO; @@ -428,6 +452,21 @@ on_error: return ret; } +static int lan9303_write_switch_reg_mask(struct lan9303 *chip, u16 regnum, + u32 val, u32 mask) +{ + int ret; + u32 reg; + + ret = lan9303_read_switch_reg(chip, regnum, ®); + if (ret) + return ret; + + reg = (reg & ~mask) | val; + + return lan9303_write_switch_reg(chip, regnum, reg); +} + static int lan9303_write_switch_port(struct lan9303 *chip, int port, u16 regnum, u32 val) { @@ -472,6 +511,220 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip) return 0; } +/* Map ALR-port bits to port bitmap, and back */ +static const int alrport_2_portmap[] = {1, 2, 4, 0, 3, 5, 6, 7 }; +static const int portmap_2_alrport[] = {3, 0, 1, 4, 2, 5, 6, 7 }; + +/* Return pointer to first free ALR cache entry, return NULL if none */ +static struct lan9303_alr_cache_entry * +lan9303_alr_cache_find_free(struct lan9303 *chip) +{ + int i; + struct lan9303_alr_cache_entry *entr = chip->alr_cache; + + for (i = 0; i < LAN9303_NUM_ALR_RECORDS; i++, entr++) + if (entr->port_map == 0) + return entr; + + return NULL; +} + +/* Return pointer to ALR cache entry matching MAC address */ +static struct lan9303_alr_cache_entry * +lan9303_alr_cache_find_mac(struct lan9303 *chip, const u8 *mac_addr) +{ + int i; + struct lan9303_alr_cache_entry *entr = chip->alr_cache; + + BUILD_BUG_ON_MSG(sizeof(struct lan9303_alr_cache_entry) & 1, + "ether_addr_equal require u16 alignment"); + + for (i = 0; i < LAN9303_NUM_ALR_RECORDS; i++, entr++) + if (ether_addr_equal(entr->mac_addr, mac_addr)) + return entr; + + return NULL; +} + +/* Wait a while until mask & reg == value. Otherwise return timeout. */ +static int lan9303_csr_reg_wait(struct lan9303 *chip, int regno, + int mask, char value) +{ + int i; + + for (i = 0; i < 0x1000; i++) { + u32 reg; + + lan9303_read_switch_reg(chip, regno, ®); + if ((reg & mask) == value) + return 0; + usleep_range(1000, 2000); + } + return -ETIMEDOUT; +} + +static int lan9303_alr_make_entry_raw(struct lan9303 *chip, u32 dat0, u32 dat1) +{ + lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_WR_DAT_0, dat0); + lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_WR_DAT_1, dat1); + lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, + LAN9303_ALR_CMD_MAKE_ENTRY); + lan9303_csr_reg_wait(chip, LAN9303_SWE_ALR_CMD_STS, ALR_STS_MAKE_PEND, + 0); + lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0); + + return 0; +} + +typedef void alr_loop_cb_t(struct lan9303 *chip, u32 dat0, u32 dat1, + int portmap, void *ctx); + +static void lan9303_alr_loop(struct lan9303 *chip, alr_loop_cb_t *cb, void *ctx) +{ + int i; + + lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, + LAN9303_ALR_CMD_GET_FIRST); + lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0); + + for (i = 1; i < LAN9303_NUM_ALR_RECORDS; i++) { + u32 dat0, dat1; + int alrport, portmap; + + lan9303_read_switch_reg(chip, LAN9303_SWE_ALR_RD_DAT_0, &dat0); + lan9303_read_switch_reg(chip, LAN9303_SWE_ALR_RD_DAT_1, &dat1); + if (dat1 & LAN9303_ALR_DAT1_END_OF_TABL) + break; + + alrport = (dat1 & LAN9303_ALR_DAT1_PORT_MASK) >> + LAN9303_ALR_DAT1_PORT_BITOFFS; + portmap = alrport_2_portmap[alrport]; + + cb(chip, dat0, dat1, portmap, ctx); + + lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, + LAN9303_ALR_CMD_GET_NEXT); + lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0); + } +} + +static void alr_reg_to_mac(u32 dat0, u32 dat1, u8 mac[6]) +{ + mac[0] = (dat0 >> 0) & 0xff; + mac[1] = (dat0 >> 8) & 0xff; + mac[2] = (dat0 >> 16) & 0xff; + mac[3] = (dat0 >> 24) & 0xff; + mac[4] = (dat1 >> 0) & 0xff; + mac[5] = (dat1 >> 8) & 0xff; +} + +struct del_port_learned_ctx { + int port; +}; + +/* Clear learned (non-static) entry on given port */ +static void alr_loop_cb_del_port_learned(struct lan9303 *chip, u32 dat0, + u32 dat1, int portmap, void *ctx) +{ + struct del_port_learned_ctx *del_ctx = ctx; + int port = del_ctx->port; + + if (((BIT(port) & portmap) == 0) || (dat1 & LAN9303_ALR_DAT1_STATIC)) + return; + + /* learned entries has only one port, we can just delete */ + dat1 &= ~LAN9303_ALR_DAT1_VALID; /* delete entry */ + lan9303_alr_make_entry_raw(chip, dat0, dat1); +} + +struct port_fdb_dump_ctx { + int port; + void *data; + dsa_fdb_dump_cb_t *cb; +}; + +static void alr_loop_cb_fdb_port_dump(struct lan9303 *chip, u32 dat0, + u32 dat1, int portmap, void *ctx) +{ + struct port_fdb_dump_ctx *dump_ctx = ctx; + u8 mac[ETH_ALEN]; + bool is_static; + + if ((BIT(dump_ctx->port) & portmap) == 0) + return; + + alr_reg_to_mac(dat0, dat1, mac); + is_static = !!(dat1 & LAN9303_ALR_DAT1_STATIC); + dump_ctx->cb(mac, 0, is_static, dump_ctx->data); +} + +/* Set a static ALR entry. Delete entry if port_map is zero */ +static void lan9303_alr_set_entry(struct lan9303 *chip, const u8 *mac, + u8 port_map, bool stp_override) +{ + u32 dat0, dat1, alr_port; + + dev_dbg(chip->dev, "%s(%pM, %d)\n", __func__, mac, port_map); + dat1 = LAN9303_ALR_DAT1_STATIC; + if (port_map) + dat1 |= LAN9303_ALR_DAT1_VALID; + /* otherwise no ports: delete entry */ + if (stp_override) + dat1 |= LAN9303_ALR_DAT1_AGE_OVERRID; + + alr_port = portmap_2_alrport[port_map & 7]; + dat1 &= ~LAN9303_ALR_DAT1_PORT_MASK; + dat1 |= alr_port << LAN9303_ALR_DAT1_PORT_BITOFFS; + + dat0 = 0; + dat0 |= (mac[0] << 0); + dat0 |= (mac[1] << 8); + dat0 |= (mac[2] << 16); + dat0 |= (mac[3] << 24); + + dat1 |= (mac[4] << 0); + dat1 |= (mac[5] << 8); + + lan9303_alr_make_entry_raw(chip, dat0, dat1); +} + +/* Add port to static ALR entry, create new static entry if needed */ +static int lan9303_alr_add_port(struct lan9303 *chip, const u8 *mac, int port, + bool stp_override) +{ + struct lan9303_alr_cache_entry *entr; + + entr = lan9303_alr_cache_find_mac(chip, mac); + if (!entr) { /*New entry */ + entr = lan9303_alr_cache_find_free(chip); + if (!entr) + return -ENOSPC; + ether_addr_copy(entr->mac_addr, mac); + } + entr->port_map |= BIT(port); + entr->stp_override = stp_override; + lan9303_alr_set_entry(chip, mac, entr->port_map, stp_override); + + return 0; +} + +/* Delete static port from ALR entry, delete entry if last port */ +static int lan9303_alr_del_port(struct lan9303 *chip, const u8 *mac, int port) +{ + struct lan9303_alr_cache_entry *entr; + + entr = lan9303_alr_cache_find_mac(chip, mac); + if (!entr) + return 0; /* no static entry found */ + + entr->port_map &= ~BIT(port); + if (entr->port_map == 0) /* zero means its free again */ + eth_zero_addr(entr->mac_addr); + lan9303_alr_set_entry(chip, mac, entr->port_map, entr->stp_override); + + return 0; +} + static int lan9303_disable_processing_port(struct lan9303 *chip, unsigned int port) { @@ -508,16 +761,36 @@ static int lan9303_enable_processing_port(struct lan9303 *chip, LAN9303_MAC_TX_CFG_X_TX_ENABLE); } +/* forward special tagged packets from port 0 to port 1 *or* port 2 */ +static int lan9303_setup_tagging(struct lan9303 *chip) +{ + int ret; + u32 val; + /* enable defining the destination port via special VLAN tagging + * for port 0 + */ + ret = lan9303_write_switch_reg(chip, LAN9303_SWE_INGRESS_PORT_TYPE, + LAN9303_SWE_INGRESS_PORT_TYPE_VLAN); + if (ret) + return ret; + + /* tag incoming packets at port 1 and 2 on their way to port 0 to be + * able to discover their source port + */ + val = LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0; + return lan9303_write_switch_reg(chip, LAN9303_BM_EGRSS_PORT_TYPE, val); +} + /* We want a special working switch: * - do not forward packets between port 1 and 2 * - forward everything from port 1 to port 0 * - forward everything from port 2 to port 0 - * - forward special tagged packets from port 0 to port 1 *or* port 2 */ static int lan9303_separate_ports(struct lan9303 *chip) { int ret; + lan9303_alr_del_port(chip, eth_stp_addr, 0); ret = lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_MIRROR, LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT0 | LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT1 | @@ -527,22 +800,6 @@ static int lan9303_separate_ports(struct lan9303 *chip) if (ret) return ret; - /* enable defining the destination port via special VLAN tagging - * for port 0 - */ - ret = lan9303_write_switch_reg(chip, LAN9303_SWE_INGRESS_PORT_TYPE, - 0x03); - if (ret) - return ret; - - /* tag incoming packets at port 1 and 2 on their way to port 0 to be - * able to discover their source port - */ - ret = lan9303_write_switch_reg(chip, LAN9303_BM_EGRSS_PORT_TYPE, - LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0); - if (ret) - return ret; - /* prevent port 1 and 2 from forwarding packets by their own */ return lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE, LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 | @@ -550,6 +807,17 @@ static int lan9303_separate_ports(struct lan9303 *chip) LAN9303_SWE_PORT_STATE_BLOCKING_PORT2); } +static void lan9303_bridge_ports(struct lan9303 *chip) +{ + /* ports bridged: remove mirroring */ + lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_MIRROR, + LAN9303_SWE_PORT_MIRROR_DISABLED); + + lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE, + chip->swe_port_state); + lan9303_alr_add_port(chip, eth_stp_addr, 0, true); +} + static int lan9303_handle_reset(struct lan9303 *chip) { if (!chip->reset_gpio) @@ -569,7 +837,7 @@ static int lan9303_disable_processing(struct lan9303 *chip) { int p; - for (p = 0; p < LAN9303_NUM_PORTS; p++) { + for (p = 1; p < LAN9303_NUM_PORTS; p++) { int ret = lan9303_disable_processing_port(chip, p); if (ret) @@ -626,7 +894,8 @@ static int lan9303_check_device(struct lan9303 *chip) /* ---------------------------- DSA -----------------------------------*/ -static enum dsa_tag_protocol lan9303_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol lan9303_get_tag_protocol(struct dsa_switch *ds, + int port) { return DSA_TAG_PROTO_LAN9303; } @@ -642,6 +911,10 @@ static int lan9303_setup(struct dsa_switch *ds) return -EINVAL; } + ret = lan9303_setup_tagging(chip); + if (ret) + dev_err(chip->dev, "failed to setup port tagging %d\n", ret); + ret = lan9303_separate_ports(chip); if (ret) dev_err(chip->dev, "failed to separate ports %d\n", ret); @@ -650,6 +923,15 @@ static int lan9303_setup(struct dsa_switch *ds) if (ret) dev_err(chip->dev, "failed to re-enable switching %d\n", ret); + /* Trap IGMP to port 0 */ + ret = lan9303_write_switch_reg_mask(chip, LAN9303_SWE_GLB_INGRESS_CFG, + LAN9303_SWE_GLB_INGR_IGMP_TRAP | + LAN9303_SWE_GLB_INGR_IGMP_PORT(0), + LAN9303_SWE_GLB_INGR_IGMP_PORT(1) | + LAN9303_SWE_GLB_INGR_IGMP_PORT(2)); + if (ret) + dev_err(chip->dev, "failed to setup IGMP trap %d\n", ret); + return 0; } @@ -760,22 +1042,49 @@ static int lan9303_phy_write(struct dsa_switch *ds, int phy, int regnum, return chip->ops->phy_write(chip, phy, regnum, val); } -static int lan9303_port_enable(struct dsa_switch *ds, int port, - struct phy_device *phy) +static void lan9303_adjust_link(struct dsa_switch *ds, int port, + struct phy_device *phydev) { struct lan9303 *chip = ds->priv; + int ctl, res; - /* enable internal packet processing */ - switch (port) { - case 1: - case 2: - return lan9303_enable_processing_port(chip, port); - default: - dev_dbg(chip->dev, - "Error: request to power up invalid port %d\n", port); + if (!phy_is_pseudo_fixed_link(phydev)) + return; + + ctl = lan9303_phy_read(ds, port, MII_BMCR); + + ctl &= ~BMCR_ANENABLE; + + if (phydev->speed == SPEED_100) + ctl |= BMCR_SPEED100; + else if (phydev->speed == SPEED_10) + ctl &= ~BMCR_SPEED100; + else + dev_err(ds->dev, "unsupported speed: %d\n", phydev->speed); + + if (phydev->duplex == DUPLEX_FULL) + ctl |= BMCR_FULLDPLX; + else + ctl &= ~BMCR_FULLDPLX; + + res = lan9303_phy_write(ds, port, MII_BMCR, ctl); + + if (port == chip->phy_addr_sel_strap) { + /* Virtual Phy: Remove Turbo 200Mbit mode */ + lan9303_read(chip->regmap, LAN9303_VIRT_SPECIAL_CTRL, &ctl); + + ctl &= ~LAN9303_VIRT_SPECIAL_TURBO; + res = regmap_write(chip->regmap, + LAN9303_VIRT_SPECIAL_CTRL, ctl); } +} - return -ENODEV; +static int lan9303_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct lan9303 *chip = ds->priv; + + return lan9303_enable_processing_port(chip, port); } static void lan9303_port_disable(struct dsa_switch *ds, int port, @@ -783,18 +1092,171 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port, { struct lan9303 *chip = ds->priv; - /* disable internal packet processing */ - switch (port) { - case 1: - case 2: - lan9303_disable_processing_port(chip, port); - lan9303_phy_write(ds, chip->phy_addr_sel_strap + port, - MII_BMCR, BMCR_PDOWN); + lan9303_disable_processing_port(chip, port); + lan9303_phy_write(ds, chip->phy_addr_sel_strap + port, + MII_BMCR, BMCR_PDOWN); +} + +static int lan9303_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *br) +{ + struct lan9303 *chip = ds->priv; + + dev_dbg(chip->dev, "%s(port %d)\n", __func__, port); + if (dsa_to_port(ds, 1)->bridge_dev == dsa_to_port(ds, 2)->bridge_dev) { + lan9303_bridge_ports(chip); + chip->is_bridged = true; /* unleash stp_state_set() */ + } + + return 0; +} + +static void lan9303_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *br) +{ + struct lan9303 *chip = ds->priv; + + dev_dbg(chip->dev, "%s(port %d)\n", __func__, port); + if (chip->is_bridged) { + lan9303_separate_ports(chip); + chip->is_bridged = false; + } +} + +static void lan9303_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) +{ + int portmask, portstate; + struct lan9303 *chip = ds->priv; + + dev_dbg(chip->dev, "%s(port %d, state %d)\n", + __func__, port, state); + + switch (state) { + case BR_STATE_DISABLED: + portstate = LAN9303_SWE_PORT_STATE_DISABLED_PORT0; + break; + case BR_STATE_BLOCKING: + case BR_STATE_LISTENING: + portstate = LAN9303_SWE_PORT_STATE_BLOCKING_PORT0; + break; + case BR_STATE_LEARNING: + portstate = LAN9303_SWE_PORT_STATE_LEARNING_PORT0; + break; + case BR_STATE_FORWARDING: + portstate = LAN9303_SWE_PORT_STATE_FORWARDING_PORT0; break; default: - dev_dbg(chip->dev, - "Error: request to power down invalid port %d\n", port); + portstate = LAN9303_SWE_PORT_STATE_DISABLED_PORT0; + dev_err(chip->dev, "unknown stp state: port %d, state %d\n", + port, state); } + + portmask = 0x3 << (port * 2); + portstate <<= (port * 2); + + chip->swe_port_state = (chip->swe_port_state & ~portmask) | portstate; + + if (chip->is_bridged) + lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE, + chip->swe_port_state); + /* else: touching SWE_PORT_STATE would break port separation */ +} + +static void lan9303_port_fast_age(struct dsa_switch *ds, int port) +{ + struct lan9303 *chip = ds->priv; + struct del_port_learned_ctx del_ctx = { + .port = port, + }; + + dev_dbg(chip->dev, "%s(%d)\n", __func__, port); + lan9303_alr_loop(chip, alr_loop_cb_del_port_learned, &del_ctx); +} + +static int lan9303_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct lan9303 *chip = ds->priv; + + dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, addr, vid); + if (vid) + return -EOPNOTSUPP; + + return lan9303_alr_add_port(chip, addr, port, false); +} + +static int lan9303_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) + +{ + struct lan9303 *chip = ds->priv; + + dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, addr, vid); + if (vid) + return -EOPNOTSUPP; + lan9303_alr_del_port(chip, addr, port); + + return 0; +} + +static int lan9303_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct lan9303 *chip = ds->priv; + struct port_fdb_dump_ctx dump_ctx = { + .port = port, + .data = data, + .cb = cb, + }; + + dev_dbg(chip->dev, "%s(%d)\n", __func__, port); + lan9303_alr_loop(chip, alr_loop_cb_fdb_port_dump, &dump_ctx); + + return 0; +} + +static int lan9303_port_mdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct switchdev_trans *trans) +{ + struct lan9303 *chip = ds->priv; + + dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, mdb->addr, + mdb->vid); + if (mdb->vid) + return -EOPNOTSUPP; + if (lan9303_alr_cache_find_mac(chip, mdb->addr)) + return 0; + if (!lan9303_alr_cache_find_free(chip)) + return -ENOSPC; + + return 0; +} + +static void lan9303_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct switchdev_trans *trans) +{ + struct lan9303 *chip = ds->priv; + + dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, mdb->addr, + mdb->vid); + lan9303_alr_add_port(chip, mdb->addr, port, false); +} + +static int lan9303_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct lan9303 *chip = ds->priv; + + dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, mdb->addr, + mdb->vid); + if (mdb->vid) + return -EOPNOTSUPP; + lan9303_alr_del_port(chip, mdb->addr, port); + + return 0; } static const struct dsa_switch_ops lan9303_switch_ops = { @@ -803,10 +1265,21 @@ static const struct dsa_switch_ops lan9303_switch_ops = { .get_strings = lan9303_get_strings, .phy_read = lan9303_phy_read, .phy_write = lan9303_phy_write, + .adjust_link = lan9303_adjust_link, .get_ethtool_stats = lan9303_get_ethtool_stats, .get_sset_count = lan9303_get_sset_count, .port_enable = lan9303_port_enable, .port_disable = lan9303_port_disable, + .port_bridge_join = lan9303_port_bridge_join, + .port_bridge_leave = lan9303_port_bridge_leave, + .port_stp_state_set = lan9303_port_stp_state_set, + .port_fast_age = lan9303_port_fast_age, + .port_fdb_add = lan9303_port_fdb_add, + .port_fdb_del = lan9303_port_fdb_del, + .port_fdb_dump = lan9303_port_fdb_dump, + .port_mdb_prepare = lan9303_port_mdb_prepare, + .port_mdb_add = lan9303_port_mdb_add, + .port_mdb_del = lan9303_port_mdb_del, }; static int lan9303_register_switch(struct lan9303 *chip) @@ -828,7 +1301,7 @@ static void lan9303_probe_reset_gpio(struct lan9303 *chip, chip->reset_gpio = devm_gpiod_get_optional(chip->dev, "reset", GPIOD_OUT_LOW); - if (!chip->reset_gpio) { + if (IS_ERR(chip->reset_gpio)) { dev_dbg(chip->dev, "No reset GPIO defined\n"); return; } diff --git a/drivers/net/dsa/lan9303.h b/drivers/net/dsa/lan9303.h index 7df2ac77fc8f..11f590b64701 100644 --- a/drivers/net/dsa/lan9303.h +++ b/drivers/net/dsa/lan9303.h @@ -3,26 +3,7 @@ #include <linux/device.h> #include <net/dsa.h> -struct lan9303; - -struct lan9303_phy_ops { - /* PHY 1 and 2 access*/ - int (*phy_read)(struct lan9303 *chip, int port, int regnum); - int (*phy_write)(struct lan9303 *chip, int port, - int regnum, u16 val); -}; - -struct lan9303 { - struct device *dev; - struct regmap *regmap; - struct regmap_irq_chip_data *irq_data; - struct gpio_desc *reset_gpio; - u32 reset_duration; /* in [ms] */ - bool phy_addr_sel_strap; - struct dsa_switch *ds; - struct mutex indirect_mutex; /* protect indexed register access */ - const struct lan9303_phy_ops *ops; -}; +#include <linux/dsa/lan9303.h> extern const struct regmap_access_table lan9303_register_set; extern const struct lan9303_phy_ops lan9303_indirect_phy_ops; diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index 24ec20f7f444..909a7e864246 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -50,7 +50,7 @@ static int lan9303_i2c_probe(struct i2c_client *client, return -ENOMEM; sw_dev->chip.regmap = devm_regmap_init_i2c(client, - &lan9303_i2c_regmap_config); + &lan9303_i2c_regmap_config); if (IS_ERR(sw_dev->chip.regmap)) { ret = PTR_ERR(sw_dev->chip.regmap); dev_err(&client->dev, "Failed to allocate register map: %d\n", diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c index fc16668a487f..cc9c2ea1c4fe 100644 --- a/drivers/net/dsa/lan9303_mdio.c +++ b/drivers/net/dsa/lan9303_mdio.c @@ -67,14 +67,15 @@ static int lan9303_mdio_read(void *ctx, uint32_t reg, uint32_t *val) return 0; } -int lan9303_mdio_phy_write(struct lan9303 *chip, int phy, int reg, u16 val) +static int lan9303_mdio_phy_write(struct lan9303 *chip, int phy, int reg, + u16 val) { struct lan9303_mdio *sw_dev = dev_get_drvdata(chip->dev); return mdiobus_write_nested(sw_dev->device->bus, phy, reg, val); } -int lan9303_mdio_phy_read(struct lan9303 *chip, int phy, int reg) +static int lan9303_mdio_phy_read(struct lan9303 *chip, int phy, int reg) { struct lan9303_mdio *sw_dev = dev_get_drvdata(chip->dev); @@ -115,7 +116,7 @@ static int lan9303_mdio_probe(struct mdio_device *mdiodev) return -ENOMEM; sw_dev->chip.regmap = devm_regmap_init(&mdiodev->dev, NULL, sw_dev, - &lan9303_mdio_regmap_config); + &lan9303_mdio_regmap_config); if (IS_ERR(sw_dev->chip.regmap)) { ret = PTR_ERR(sw_dev->chip.regmap); dev_err(&mdiodev->dev, "regmap init failed: %d\n", ret); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 56cd6d365352..b5be93a1e0df 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -394,7 +394,8 @@ static int ksz_setup(struct dsa_switch *ds) return 0; } -static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, + int port) { return DSA_TAG_PROTO_KSZ; } diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index c142b97add2c..2820d69810b3 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -564,7 +564,8 @@ static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum) return mdiobus_read_nested(priv->bus, port, regnum); } -int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) +static int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum, + u16 val) { struct mt7530_priv *priv = ds->priv; @@ -687,7 +688,7 @@ mt7530_cpu_port_enable(struct mt7530_priv *priv, * the switch */ mt7530_write(priv, MT7530_PCR_P(port), - PCR_MATRIX(priv->ds->enabled_port_mask)); + PCR_MATRIX(dsa_user_ports(priv->ds))); return 0; } @@ -780,8 +781,8 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port, * same bridge. If the port is disabled, port matrix is kept * and not being setup until the port becomes enabled. */ - if (ds->enabled_port_mask & BIT(i) && i != port) { - if (ds->ports[i].bridge_dev != bridge) + if (dsa_is_user_port(ds, i) && i != port) { + if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) mt7530_set(priv, MT7530_PCR_P(i), @@ -817,8 +818,8 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, * in the same bridge. If the port is disabled, port matrix * is kept and not being setup until the port becomes enabled. */ - if (ds->enabled_port_mask & BIT(i) && i != port) { - if (ds->ports[i].bridge_dev != bridge) + if (dsa_is_user_port(ds, i) && i != port) { + if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) mt7530_clear(priv, MT7530_PCR_P(i), @@ -906,11 +907,11 @@ err: } static enum dsa_tag_protocol -mtk_get_tag_protocol(struct dsa_switch *ds) +mtk_get_tag_protocol(struct dsa_switch *ds, int port) { struct mt7530_priv *priv = ds->priv; - if (!dsa_is_cpu_port(ds, MT7530_CPU_PORT)) { + if (port != MT7530_CPU_PORT) { dev_warn(priv->dev, "port not matched with tagging CPU port\n"); return DSA_TAG_PROTO_NONE; @@ -928,11 +929,11 @@ mt7530_setup(struct dsa_switch *ds) struct device_node *dn; struct mt7530_dummy_poll p; - /* The parent node of cpu_dp->netdev which holds the common system + /* The parent node of master netdev which holds the common system * controller also is the container for two GMACs nodes representing * as two netdev instances. */ - dn = ds->dst->cpu_dp->netdev->dev.of_node->parent; + dn = ds->ports[MT7530_CPU_PORT].master->dev.of_node->parent; priv->ethernet = syscon_node_to_regmap(dn); if (IS_ERR(priv->ethernet)) return PTR_ERR(priv->ethernet); diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index f123ed57630d..65f10fec25b3 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -9,6 +9,7 @@ */ #include <linux/delay.h> +#include <linux/etherdevice.h> #include <linux/jiffies.h> #include <linux/list.h> #include <linux/module.h> @@ -69,7 +70,8 @@ static const char *mv88e6060_get_name(struct mii_bus *bus, int sw_addr) return NULL; } -static enum dsa_tag_protocol mv88e6060_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol mv88e6060_get_tag_protocol(struct dsa_switch *ds, + int port) { return DSA_TAG_PROTO_TRAILER; } @@ -174,9 +176,8 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, PORT_VLAN_MAP, ((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) | - (dsa_is_cpu_port(ds, p) ? - ds->enabled_port_mask : - BIT(ds->dst->cpu_dp->index))); + (dsa_is_cpu_port(ds, p) ? dsa_user_ports(ds) : + BIT(dsa_to_port(ds, p)->cpu_dp->index))); /* Port Association Vector: when learning source addresses * of packets, add the address to the address database using @@ -188,6 +189,27 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) return 0; } +static int mv88e6060_setup_addr(struct dsa_switch *ds) +{ + u8 addr[ETH_ALEN]; + u16 val; + + eth_random_addr(addr); + + val = addr[0] << 8 | addr[1]; + + /* The multicast bit is always transmitted as a zero, so the switch uses + * bit 8 for "DiffAddr", where 0 means all ports transmit the same SA. + */ + val &= 0xfeff; + + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, val); + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]); + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]); + + return 0; +} + static int mv88e6060_setup(struct dsa_switch *ds) { int ret; @@ -203,6 +225,10 @@ static int mv88e6060_setup(struct dsa_switch *ds) if (ret < 0) return ret; + ret = mv88e6060_setup_addr(ds); + if (ret < 0) + return ret; + for (i = 0; i < MV88E6060_PORTS; i++) { ret = mv88e6060_setup_port(ds, i); if (ret < 0) @@ -212,22 +238,6 @@ static int mv88e6060_setup(struct dsa_switch *ds) return 0; } -static int mv88e6060_set_addr(struct dsa_switch *ds, u8 *addr) -{ - u16 val = addr[0] << 8 | addr[1]; - - /* The multicast bit is always transmitted as a zero, so the switch uses - * bit 8 for "DiffAddr", where 0 means all ports transmit the same SA. - */ - val &= 0xfeff; - - REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, val); - REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]); - REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]); - - return 0; -} - static int mv88e6060_port_to_phy_addr(int port) { if (port >= 0 && port < MV88E6060_PORTS) @@ -262,7 +272,6 @@ static const struct dsa_switch_ops mv88e6060_switch_ops = { .get_tag_protocol = mv88e6060_get_tag_protocol, .probe = mv88e6060_drv_probe, .setup = mv88e6060_setup, - .set_addr = mv88e6060_set_addr, .phy_read = mv88e6060_phy_read, .phy_write = mv88e6060_phy_write, }; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index d74c7335c512..8171055fde7a 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -851,7 +851,7 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) if (dsa_is_cpu_port(chip->ds, i) || dsa_is_dsa_port(chip->ds, i) || - (br && chip->ds->ports[i].bridge_dev == br)) + (br && dsa_to_port(chip->ds, i)->bridge_dev == br)) pvlan |= BIT(i); return pvlan; @@ -932,6 +932,19 @@ static int mv88e6xxx_irl_setup(struct mv88e6xxx_chip *chip) return 0; } +static int mv88e6xxx_mac_setup(struct mv88e6xxx_chip *chip) +{ + if (chip->info->ops->set_switch_mac) { + u8 addr[ETH_ALEN]; + + eth_random_addr(addr); + + return chip->info->ops->set_switch_mac(chip, addr); + } + + return 0; +} + static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) { u16 pvlan = 0; @@ -1124,23 +1137,23 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) continue; - if (!ds->ports[port].netdev) + if (!ds->ports[i].slave) continue; if (vlan.member[i] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) continue; - if (ds->ports[i].bridge_dev == + if (dsa_to_port(ds, i)->bridge_dev == ds->ports[port].bridge_dev) break; /* same bridge, check next VLAN */ - if (!ds->ports[i].bridge_dev) + if (!dsa_to_port(ds, i)->bridge_dev) continue; - dev_err(ds->dev, "p%d: hw VLAN %d already used by %s\n", - port, vlan.vid, - netdev_name(ds->ports[i].bridge_dev)); + dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n", + port, vlan.vid, i, + netdev_name(dsa_to_port(ds, i)->bridge_dev)); err = -EOPNOTSUPP; goto unlock; } @@ -1195,6 +1208,73 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, return 0; } +static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, + const unsigned char *addr, u16 vid, + u8 state) +{ + struct mv88e6xxx_vtu_entry vlan; + struct mv88e6xxx_atu_entry entry; + int err; + + /* Null VLAN ID corresponds to the port private database */ + if (vid == 0) + err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid); + else + err = mv88e6xxx_vtu_get(chip, vid, &vlan, false); + if (err) + return err; + + entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; + ether_addr_copy(entry.mac, addr); + eth_addr_dec(entry.mac); + + err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry); + if (err) + return err; + + /* Initialize a fresh ATU entry if it isn't found */ + if (entry.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED || + !ether_addr_equal(entry.mac, addr)) { + memset(&entry, 0, sizeof(entry)); + ether_addr_copy(entry.mac, addr); + } + + /* Purge the ATU entry only if no port is using it anymore */ + if (state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { + entry.portvec &= ~BIT(port); + if (!entry.portvec) + entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; + } else { + entry.portvec |= BIT(port); + entry.state = state; + } + + return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry); +} + +static int mv88e6xxx_port_add_broadcast(struct mv88e6xxx_chip *chip, int port, + u16 vid) +{ + const char broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + u8 state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC; + + return mv88e6xxx_port_db_load_purge(chip, port, broadcast, vid, state); +} + +static int mv88e6xxx_broadcast_setup(struct mv88e6xxx_chip *chip, u16 vid) +{ + int port; + int err; + + for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { + err = mv88e6xxx_port_add_broadcast(chip, port, vid); + if (err) + return err; + } + + return 0; +} + static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, u16 vid, u8 member) { @@ -1207,7 +1287,11 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, vlan.member[port] = member; - return mv88e6xxx_vtu_loadpurge(chip, &vlan); + err = mv88e6xxx_vtu_loadpurge(chip, &vlan); + if (err) + return err; + + return mv88e6xxx_broadcast_setup(chip, vid); } static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, @@ -1311,50 +1395,6 @@ unlock: return err; } -static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, - const unsigned char *addr, u16 vid, - u8 state) -{ - struct mv88e6xxx_vtu_entry vlan; - struct mv88e6xxx_atu_entry entry; - int err; - - /* Null VLAN ID corresponds to the port private database */ - if (vid == 0) - err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid); - else - err = mv88e6xxx_vtu_get(chip, vid, &vlan, false); - if (err) - return err; - - entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; - ether_addr_copy(entry.mac, addr); - eth_addr_dec(entry.mac); - - err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry); - if (err) - return err; - - /* Initialize a fresh ATU entry if it isn't found */ - if (entry.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED || - !ether_addr_equal(entry.mac, addr)) { - memset(&entry, 0, sizeof(entry)); - ether_addr_copy(entry.mac, addr); - } - - /* Purge the ATU entry only if no port is using it anymore */ - if (state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { - entry.portvec &= ~BIT(port); - if (!entry.portvec) - entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; - } else { - entry.portvec |= BIT(port); - entry.state = state; - } - - return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry); -} - static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { @@ -1663,7 +1703,7 @@ static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port) if (dsa_is_dsa_port(chip->ds, port)) return mv88e6xxx_set_port_mode_dsa(chip, port); - if (dsa_is_normal_port(chip->ds, port)) + if (dsa_is_user_port(chip->ds, port)) return mv88e6xxx_set_port_mode_normal(chip, port); /* Setup CPU port mode depending on its supported tag format */ @@ -1964,19 +2004,7 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) if (err) return err; - /* Clear the statistics counters for all ports */ - err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP, - MV88E6XXX_G1_STATS_OP_BUSY | - MV88E6XXX_G1_STATS_OP_FLUSH_ALL); - if (err) - return err; - - /* Wait for the flush to complete. */ - err = mv88e6xxx_g1_stats_wait(chip); - if (err) - return err; - - return 0; + return mv88e6xxx_g1_stats_clear(chip); } static int mv88e6xxx_setup(struct dsa_switch *ds) @@ -1992,6 +2020,9 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) /* Setup Switch Port Registers */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { + if (dsa_is_unused_port(ds, i)) + continue; + err = mv88e6xxx_setup_port(chip, i); if (err) goto unlock; @@ -2013,6 +2044,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) if (err) goto unlock; + err = mv88e6xxx_mac_setup(chip); + if (err) + goto unlock; + err = mv88e6xxx_phy_setup(chip); if (err) goto unlock; @@ -2029,6 +2064,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) if (err) goto unlock; + err = mv88e6xxx_broadcast_setup(chip, 0); + if (err) + goto unlock; + err = mv88e6xxx_pot_setup(chip); if (err) goto unlock; @@ -2043,21 +2082,6 @@ unlock: return err; } -static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - if (!chip->info->ops->set_switch_mac) - return -EOPNOTSUPP; - - mutex_lock(&chip->reg_lock); - err = chip->info->ops->set_switch_mac(chip, addr); - mutex_unlock(&chip->reg_lock); - - return err; -} - static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg) { struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; @@ -2263,6 +2287,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2290,6 +2315,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_set_upstream_port = mv88e6095_port_set_upstream_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2320,6 +2346,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2347,6 +2374,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2377,6 +2405,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6097_port_pause_limit, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2413,6 +2442,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, @@ -2445,6 +2475,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2470,6 +2501,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2503,6 +2535,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2538,6 +2571,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2572,6 +2606,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2607,6 +2642,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2634,6 +2670,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_set_upstream_port = mv88e6095_port_set_upstream_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2775,6 +2812,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2846,6 +2884,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6320_stats_get_stats, @@ -2879,6 +2918,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6320_stats_get_stats, @@ -2911,6 +2951,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, @@ -2944,6 +2985,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -2977,6 +3019,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -3012,6 +3055,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, @@ -3687,7 +3731,8 @@ static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, return 0; } -static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, + int port) { struct mv88e6xxx_chip *chip = ds->priv; @@ -3785,7 +3830,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .probe = mv88e6xxx_drv_probe, .get_tag_protocol = mv88e6xxx_get_tag_protocol, .setup = mv88e6xxx_setup, - .set_addr = mv88e6xxx_set_addr, .adjust_link = mv88e6xxx_adjust_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index d76d7c7ea819..b43bd6476632 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -374,6 +374,22 @@ int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) MV88E6XXX_G1_STATS_OP_BUSY); } +int mv88e6095_g1_stats_set_histogram(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STATS_OP, &val); + if (err) + return err; + + val |= MV88E6XXX_G1_STATS_OP_HIST_RX_TX; + + err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP, val); + + return err; +} + int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) { int err; @@ -444,3 +460,22 @@ void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val) *val = value | reg; } + +int mv88e6xxx_g1_stats_clear(struct mv88e6xxx_chip *chip) +{ + int err; + u16 val; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STATS_OP, &val); + if (err) + return err; + + val |= MV88E6XXX_G1_STATS_OP_BUSY | MV88E6XXX_G1_STATS_OP_FLUSH_ALL; + + err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP, val); + if (err) + return err; + + /* Wait for the flush to complete. */ + return mv88e6xxx_g1_stats_wait(chip); +} diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 950b914f9251..b0dc7518b47f 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -235,8 +235,10 @@ int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); +int mv88e6095_g1_stats_set_histogram(struct mv88e6xxx_chip *chip); int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip); void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val); +int mv88e6xxx_g1_stats_clear(struct mv88e6xxx_chip *chip); int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 5ada7a41449c..9df22ebee822 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -506,7 +506,7 @@ qca8k_setup(struct dsa_switch *ds) pr_warn("regmap initialization failed"); /* Initialize CPU port pad mode (xMII type, delays...) */ - phy_mode = of_get_phy_mode(ds->dst->cpu_dp->dn); + phy_mode = of_get_phy_mode(ds->ports[QCA8K_CPU_PORT].dn); if (phy_mode < 0) { pr_err("Can't find phy-mode for master device\n"); return phy_mode; @@ -536,7 +536,7 @@ qca8k_setup(struct dsa_switch *ds) /* Disable MAC by default on all user ports */ for (i = 1; i < QCA8K_NUM_PORTS; i++) - if (ds->enabled_port_mask & BIT(i)) + if (dsa_is_user_port(ds, i)) qca8k_port_set_status(priv, i, 0); /* Forward all unknown frames to CPU port for Linux processing */ @@ -551,12 +551,11 @@ qca8k_setup(struct dsa_switch *ds) /* CPU port gets connected to all user ports of the switch */ if (dsa_is_cpu_port(ds, i)) { qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), - QCA8K_PORT_LOOKUP_MEMBER, - ds->enabled_port_mask); + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); } /* Invividual user ports get connected to CPU port only */ - if (ds->enabled_port_mask & BIT(i)) { + if (dsa_is_user_port(ds, i)) { int shift = 16 * (i % 2); qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), @@ -700,7 +699,7 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) int i; for (i = 1; i < QCA8K_NUM_PORTS; i++) { - if (ds->ports[i].bridge_dev != br) + if (dsa_to_port(ds, i)->bridge_dev != br) continue; /* Add this port to the portvlan mask of the other ports * in the bridge @@ -725,7 +724,7 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) int i; for (i = 1; i < QCA8K_NUM_PORTS; i++) { - if (ds->ports[i].bridge_dev != br) + if (dsa_to_port(ds, i)->bridge_dev != br) continue; /* Remove this port to the portvlan mask of the other ports * in the bridge @@ -824,7 +823,7 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port, } static enum dsa_tag_protocol -qca8k_get_tag_protocol(struct dsa_switch *ds) +qca8k_get_tag_protocol(struct dsa_switch *ds, int port) { return DSA_TAG_PROTO_QCA; } |