aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/dsa
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa')
-rw-r--r--drivers/net/dsa/b53/Kconfig2
-rw-r--r--drivers/net/dsa/b53/b53_common.c235
-rw-r--r--drivers/net/dsa/b53/b53_priv.h146
-rw-r--r--drivers/net/dsa/b53/b53_regs.h48
-rw-r--r--drivers/net/dsa/bcm_sf2.c240
-rw-r--r--drivers/net/dsa/bcm_sf2.h3
-rw-r--r--drivers/net/dsa/bcm_sf2_cfp.c1130
-rw-r--r--drivers/net/dsa/bcm_sf2_regs.h49
-rw-r--r--drivers/net/dsa/dsa_loop.c11
-rw-r--r--drivers/net/dsa/lan9303-core.c557
-rw-r--r--drivers/net/dsa/lan9303.h21
-rw-r--r--drivers/net/dsa/lan9303_i2c.c2
-rw-r--r--drivers/net/dsa/lan9303_mdio.c7
-rw-r--r--drivers/net/dsa/microchip/ksz_common.c3
-rw-r--r--drivers/net/dsa/mt7530.c21
-rw-r--r--drivers/net/dsa/mv88e6060.c51
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c210
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1.c35
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1.h2
-rw-r--r--drivers/net/dsa/qca8k.c15
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, &reg);
+ 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, &reg);
+ 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), &reg);
@@ -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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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;
}