aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/dsa
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa')
-rw-r--r--drivers/net/dsa/Kconfig60
-rw-r--r--drivers/net/dsa/Makefile10
-rw-r--r--drivers/net/dsa/b53/b53_common.c231
-rw-r--r--drivers/net/dsa/b53/b53_mdio.c13
-rw-r--r--drivers/net/dsa/b53/b53_priv.h66
-rw-r--r--drivers/net/dsa/b53/b53_regs.h37
-rw-r--r--drivers/net/dsa/b53/b53_srab.c2
-rw-r--r--drivers/net/dsa/bcm_sf2.c272
-rw-r--r--drivers/net/dsa/bcm_sf2.h58
-rw-r--r--drivers/net/dsa/bcm_sf2_cfp.c614
-rw-r--r--drivers/net/dsa/bcm_sf2_regs.h197
-rw-r--r--drivers/net/dsa/dsa_loop.c400
-rw-r--r--drivers/net/dsa/dsa_loop.h19
-rw-r--r--drivers/net/dsa/dsa_loop_bdinfo.c34
-rw-r--r--drivers/net/dsa/lan9303-core.c879
-rw-r--r--drivers/net/dsa/lan9303.h19
-rw-r--r--drivers/net/dsa/lan9303_i2c.c113
-rw-r--r--drivers/net/dsa/lan9303_mdio.c148
-rw-r--r--drivers/net/dsa/microchip/Kconfig12
-rw-r--r--drivers/net/dsa/microchip/Makefile2
-rw-r--r--drivers/net/dsa/microchip/ksz_9477_reg.h1676
-rw-r--r--drivers/net/dsa/microchip/ksz_common.c1279
-rw-r--r--drivers/net/dsa/microchip/ksz_priv.h210
-rw-r--r--drivers/net/dsa/microchip/ksz_spi.c216
-rw-r--r--drivers/net/dsa/mt7530.c1125
-rw-r--r--drivers/net/dsa/mt7530.h402
-rw-r--r--drivers/net/dsa/mv88e6060.c12
-rw-r--r--drivers/net/dsa/mv88e6xxx/Makefile4
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c2672
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h518
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1.c173
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1.h234
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1_atu.c309
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1_vtu.c515
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2.c622
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2.h269
-rw-r--r--drivers/net/dsa/mv88e6xxx/mv88e6xxx.h890
-rw-r--r--drivers/net/dsa/mv88e6xxx/phy.c249
-rw-r--r--drivers/net/dsa/mv88e6xxx/phy.h43
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.c530
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.h257
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.c229
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.h48
-rw-r--r--drivers/net/dsa/qca8k.c26
-rw-r--r--drivers/net/dsa/qca8k.h1
45 files changed, 12671 insertions, 3024 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 065984670ff1..83a9bc892a3b 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -1,17 +1,11 @@
menu "Distributed Switch Architecture drivers"
depends on HAVE_NET_DSA
-config NET_DSA_MV88E6060
- tristate "Marvell 88E6060 ethernet switch chip support"
- depends on NET_DSA
- select NET_DSA_TAG_TRAILER
- ---help---
- This enables support for the Marvell 88E6060 ethernet switch
- chip.
+source "drivers/net/dsa/b53/Kconfig"
config NET_DSA_BCM_SF2
tristate "Broadcom Starfighter 2 Ethernet switch support"
- depends on HAS_IOMEM && NET_DSA
+ depends on HAS_IOMEM && NET_DSA && OF_MDIO
select NET_DSA_TAG_BRCM
select FIXED_PHY
select BCM7XXX_PHY
@@ -21,7 +15,31 @@ config NET_DSA_BCM_SF2
This enables support for the Broadcom Starfighter 2 Ethernet
switch chips.
-source "drivers/net/dsa/b53/Kconfig"
+config NET_DSA_LOOP
+ tristate "DSA mock-up Ethernet switch chip support"
+ depends on NET_DSA
+ select FIXED_PHY
+ ---help---
+ This enables support for a fake mock-up switch chip which
+ exercises the DSA APIs.
+
+config NET_DSA_MT7530
+ tristate "Mediatek MT7530 Ethernet switch support"
+ depends on NET_DSA
+ select NET_DSA_TAG_MTK
+ ---help---
+ This enables support for the Mediatek MT7530 Ethernet switch
+ chip.
+
+config NET_DSA_MV88E6060
+ tristate "Marvell 88E6060 ethernet switch chip support"
+ depends on NET_DSA
+ select NET_DSA_TAG_TRAILER
+ ---help---
+ This enables support for the Marvell 88E6060 ethernet switch
+ chip.
+
+source "drivers/net/dsa/microchip/Kconfig"
source "drivers/net/dsa/mv88e6xxx/Kconfig"
@@ -34,4 +52,28 @@ config NET_DSA_QCA8K
This enables support for the Qualcomm Atheros QCA8K Ethernet
switch chips.
+config NET_DSA_SMSC_LAN9303
+ tristate
+ select NET_DSA_TAG_LAN9303
+ ---help---
+ This enables support for the SMSC/Microchip LAN9303 3 port ethernet
+ switch chips.
+
+config NET_DSA_SMSC_LAN9303_I2C
+ tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in I2C managed mode"
+ depends on NET_DSA && I2C
+ select NET_DSA_SMSC_LAN9303
+ select REGMAP_I2C
+ ---help---
+ Enable access functions if the SMSC/Microchip LAN9303 is configured
+ for I2C managed mode.
+
+config NET_DSA_SMSC_LAN9303_MDIO
+ tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in MDIO managed mode"
+ depends on NET_DSA
+ select NET_DSA_SMSC_LAN9303
+ ---help---
+ Enable access functions if the SMSC/Microchip LAN9303 is configured
+ for MDIO managed mode.
+
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 8346e4f9737a..4a5b5bd297ee 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -1,6 +1,12 @@
+obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o
+bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o
+obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o
+obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
-obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o
obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
-
+obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
+obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o
+obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
obj-y += b53/
+obj-y += microchip/
obj-y += mv88e6xxx/
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 947adda3397d..7f36d3e3c98b 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -29,7 +29,6 @@
#include <linux/etherdevice.h>
#include <linux/if_bridge.h>
#include <net/dsa.h>
-#include <net/switchdev.h>
#include "b53_regs.h"
#include "b53_priv.h"
@@ -326,6 +325,7 @@ 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);
@@ -336,6 +336,15 @@ static void b53_set_forwarding(struct b53_device *dev, int enable)
mgmt &= ~SM_SW_FWD_EN;
b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+
+ /* Include IMP port in dumb forwarding mode when no tagging protocol is
+ * set
+ */
+ 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);
+ }
}
static void b53_enable_vlan(struct b53_device *dev, bool enable)
@@ -598,7 +607,8 @@ static void b53_switch_reset_gpio(struct b53_device *dev)
static int b53_switch_reset(struct b53_device *dev)
{
- u8 mgmt;
+ unsigned int timeout = 1000;
+ u8 mgmt, reg;
b53_switch_reset_gpio(dev);
@@ -607,6 +617,28 @@ static int b53_switch_reset(struct b53_device *dev)
b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
}
+ /* This is specific to 58xx devices here, do not use is58xx() which
+ * covers the larger Starfigther 2 family, including 7445/7278 which
+ * still use this driver as a library and need to perform the reset
+ * earlier.
+ */
+ if (dev->chip_id == BCM58XX_DEVICE_ID) {
+ b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, &reg);
+ reg |= SW_RST | EN_SW_RST | EN_CH_RST;
+ b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, reg);
+
+ do {
+ b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, &reg);
+ if (!(reg & SW_RST))
+ break;
+
+ usleep_range(1000, 2000);
+ } while (timeout-- > 0);
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+ }
+
b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
if (!(mgmt & SM_SW_FWD_EN)) {
@@ -712,7 +744,7 @@ static unsigned int b53_get_mib_size(struct b53_device *dev)
return B53_MIBS_SIZE;
}
-static void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
{
struct b53_device *dev = ds->priv;
const struct b53_mib_desc *mibs = b53_get_mib(dev);
@@ -723,9 +755,9 @@ static void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
memcpy(data + i * ETH_GSTRING_LEN,
mibs[i].name, ETH_GSTRING_LEN);
}
+EXPORT_SYMBOL(b53_get_strings);
-static void b53_get_ethtool_stats(struct dsa_switch *ds, int port,
- uint64_t *data)
+void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
{
struct b53_device *dev = ds->priv;
const struct b53_mib_desc *mibs = b53_get_mib(dev);
@@ -756,13 +788,15 @@ static void b53_get_ethtool_stats(struct dsa_switch *ds, int port,
mutex_unlock(&dev->stats_mutex);
}
+EXPORT_SYMBOL(b53_get_ethtool_stats);
-static int b53_get_sset_count(struct dsa_switch *ds)
+int b53_get_sset_count(struct dsa_switch *ds)
{
struct b53_device *dev = ds->priv;
return b53_get_mib_size(dev);
}
+EXPORT_SYMBOL(b53_get_sset_count);
static int b53_setup(struct dsa_switch *ds)
{
@@ -921,15 +955,15 @@ static void b53_adjust_link(struct dsa_switch *ds, int port,
}
}
-static int b53_vlan_filtering(struct dsa_switch *ds, int port,
- bool vlan_filtering)
+int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
{
return 0;
}
+EXPORT_SYMBOL(b53_vlan_filtering);
-static int b53_vlan_prepare(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct switchdev_trans *trans)
+int b53_vlan_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
{
struct b53_device *dev = ds->priv;
@@ -943,10 +977,11 @@ static int b53_vlan_prepare(struct dsa_switch *ds, int port,
return 0;
}
+EXPORT_SYMBOL(b53_vlan_prepare);
-static void b53_vlan_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct switchdev_trans *trans)
+void b53_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
{
struct b53_device *dev = ds->priv;
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
@@ -977,9 +1012,10 @@ static void b53_vlan_add(struct dsa_switch *ds, int port,
b53_fast_age_vlan(dev, vid);
}
}
+EXPORT_SYMBOL(b53_vlan_add);
-static int b53_vlan_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan)
+int b53_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
{
struct b53_device *dev = ds->priv;
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
@@ -1015,10 +1051,11 @@ static int b53_vlan_del(struct dsa_switch *ds, int port,
return 0;
}
+EXPORT_SYMBOL(b53_vlan_del);
-static int b53_vlan_dump(struct dsa_switch *ds, int port,
- struct switchdev_obj_port_vlan *vlan,
- int (*cb)(struct switchdev_obj *obj))
+int b53_vlan_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_vlan *vlan,
+ switchdev_obj_dump_cb_t *cb)
{
struct b53_device *dev = ds->priv;
u16 vid, vid_start = 0, pvid;
@@ -1057,6 +1094,7 @@ static int b53_vlan_dump(struct dsa_switch *ds, int port,
return err;
}
+EXPORT_SYMBOL(b53_vlan_dump);
/* Address Resolution Logic routines */
static int b53_arl_op_wait(struct b53_device *dev)
@@ -1137,7 +1175,7 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
int ret;
/* Convert the array into a 64-bit MAC */
- mac = b53_mac_to_u64(addr);
+ mac = ether_addr_to_u64(addr);
/* Perform a read for the given MAC and VID */
b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, mac);
@@ -1175,9 +1213,9 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
return b53_arl_rw_op(dev, 0);
}
-static int b53_fdb_prepare(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb,
- struct switchdev_trans *trans)
+int b53_fdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
{
struct b53_device *priv = ds->priv;
@@ -1189,24 +1227,27 @@ static int b53_fdb_prepare(struct dsa_switch *ds, int port,
return 0;
}
+EXPORT_SYMBOL(b53_fdb_prepare);
-static void b53_fdb_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb,
- struct switchdev_trans *trans)
+void b53_fdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
{
struct b53_device *priv = ds->priv;
if (b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, true))
pr_err("%s: failed to add MAC address\n", __func__);
}
+EXPORT_SYMBOL(b53_fdb_add);
-static int b53_fdb_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb)
+int b53_fdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb)
{
struct b53_device *priv = ds->priv;
return b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, false);
}
+EXPORT_SYMBOL(b53_fdb_del);
static int b53_arl_search_wait(struct b53_device *dev)
{
@@ -1240,10 +1281,9 @@ static void b53_arl_search_rd(struct b53_device *dev, u8 idx,
b53_arl_to_entry(ent, mac_vid, fwd_entry);
}
-static int b53_fdb_copy(struct net_device *dev, int port,
- const struct b53_arl_entry *ent,
+static int b53_fdb_copy(int port, const struct b53_arl_entry *ent,
struct switchdev_obj_port_fdb *fdb,
- int (*cb)(struct switchdev_obj *obj))
+ switchdev_obj_dump_cb_t *cb)
{
if (!ent->is_valid)
return 0;
@@ -1258,12 +1298,11 @@ static int b53_fdb_copy(struct net_device *dev, int port,
return cb(&fdb->obj);
}
-static int b53_fdb_dump(struct dsa_switch *ds, int port,
- struct switchdev_obj_port_fdb *fdb,
- int (*cb)(struct switchdev_obj *obj))
+int b53_fdb_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_fdb *fdb,
+ switchdev_obj_dump_cb_t *cb)
{
struct b53_device *priv = ds->priv;
- struct net_device *dev = ds->ports[port].netdev;
struct b53_arl_entry results[2];
unsigned int count = 0;
int ret;
@@ -1279,13 +1318,13 @@ static int b53_fdb_dump(struct dsa_switch *ds, int port,
return ret;
b53_arl_search_rd(priv, 0, &results[0]);
- ret = b53_fdb_copy(dev, port, &results[0], fdb, cb);
+ ret = b53_fdb_copy(port, &results[0], fdb, cb);
if (ret)
return ret;
if (priv->num_arl_entries > 2) {
b53_arl_search_rd(priv, 1, &results[1]);
- ret = b53_fdb_copy(dev, port, &results[1], fdb, cb);
+ ret = b53_fdb_copy(port, &results[1], fdb, cb);
if (ret)
return ret;
@@ -1297,12 +1336,12 @@ static int b53_fdb_dump(struct dsa_switch *ds, int port,
return 0;
}
+EXPORT_SYMBOL(b53_fdb_dump);
-static int b53_br_join(struct dsa_switch *ds, int port,
- struct net_device *bridge)
+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_port;
+ s8 cpu_port = ds->dst->cpu_dp->index;
u16 pvlan, reg;
unsigned int i;
@@ -1317,11 +1356,10 @@ static int b53_br_join(struct dsa_switch *ds, int port,
b53_write16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, reg);
}
- dev->ports[port].bridge_dev = bridge;
b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan);
b53_for_each_port(dev, i) {
- if (dev->ports[i].bridge_dev != bridge)
+ if (ds->ports[i].bridge_dev != br)
continue;
/* Add this local port to the remote port VLAN control
@@ -1343,13 +1381,13 @@ static int b53_br_join(struct dsa_switch *ds, int port,
return 0;
}
+EXPORT_SYMBOL(b53_br_join);
-static void b53_br_leave(struct dsa_switch *ds, int port)
+void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br)
{
struct b53_device *dev = ds->priv;
- struct net_device *bridge = dev->ports[port].bridge_dev;
struct b53_vlan *vl = &dev->vlans[0];
- s8 cpu_port = ds->dst->cpu_port;
+ s8 cpu_port = ds->dst->cpu_dp->index;
unsigned int i;
u16 pvlan, reg, pvid;
@@ -1357,7 +1395,7 @@ static void b53_br_leave(struct dsa_switch *ds, int port)
b53_for_each_port(dev, i) {
/* Don't touch the remaining ports */
- if (dev->ports[i].bridge_dev != bridge)
+ if (ds->ports[i].bridge_dev != br)
continue;
b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), &reg);
@@ -1372,7 +1410,6 @@ static void b53_br_leave(struct dsa_switch *ds, int port)
b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan);
dev->ports[port].vlan_ctl_mask = pvlan;
- dev->ports[port].bridge_dev = NULL;
if (is5325(dev) || is5365(dev))
pvid = 1;
@@ -1393,8 +1430,9 @@ static void b53_br_leave(struct dsa_switch *ds, int port)
b53_set_vlan_entry(dev, pvid, vl);
}
}
+EXPORT_SYMBOL(b53_br_leave);
-static void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state)
+void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state)
{
struct b53_device *dev = ds->priv;
u8 hw_state;
@@ -1426,21 +1464,88 @@ static void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state)
reg |= hw_state;
b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg);
}
+EXPORT_SYMBOL(b53_br_set_stp_state);
-static void b53_br_fast_age(struct dsa_switch *ds, int port)
+void b53_br_fast_age(struct dsa_switch *ds, int port)
{
struct b53_device *dev = ds->priv;
if (b53_fast_age_port(dev, port))
dev_err(ds->dev, "fast ageing failed\n");
}
+EXPORT_SYMBOL(b53_br_fast_age);
static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds)
{
return DSA_TAG_PROTO_NONE;
}
-static struct dsa_switch_ops b53_switch_ops = {
+int b53_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror, bool ingress)
+{
+ struct b53_device *dev = ds->priv;
+ u16 reg, loc;
+
+ if (ingress)
+ loc = B53_IG_MIR_CTL;
+ else
+ loc = B53_EG_MIR_CTL;
+
+ b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
+ reg &= ~MIRROR_MASK;
+ reg |= BIT(port);
+ b53_write16(dev, B53_MGMT_PAGE, loc, reg);
+
+ b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
+ reg &= ~CAP_PORT_MASK;
+ reg |= mirror->to_local_port;
+ reg |= MIRROR_EN;
+ b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL(b53_mirror_add);
+
+void b53_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror)
+{
+ struct b53_device *dev = ds->priv;
+ bool loc_disable = false, other_loc_disable = false;
+ u16 reg, loc;
+
+ if (mirror->ingress)
+ loc = B53_IG_MIR_CTL;
+ else
+ loc = B53_EG_MIR_CTL;
+
+ /* Update the desired ingress/egress register */
+ b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
+ reg &= ~BIT(port);
+ if (!(reg & MIRROR_MASK))
+ loc_disable = true;
+ b53_write16(dev, B53_MGMT_PAGE, loc, reg);
+
+ /* Now look at the other one to know if we can disable mirroring
+ * entirely
+ */
+ if (mirror->ingress)
+ b53_read16(dev, B53_MGMT_PAGE, B53_EG_MIR_CTL, &reg);
+ else
+ b53_read16(dev, B53_MGMT_PAGE, B53_IG_MIR_CTL, &reg);
+ if (!(reg & MIRROR_MASK))
+ other_loc_disable = true;
+
+ b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
+ /* Both no longer have ports, let's disable mirroring */
+ if (loc_disable && other_loc_disable) {
+ reg &= ~MIRROR_EN;
+ reg &= ~mirror->to_local_port;
+ }
+ b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
+}
+EXPORT_SYMBOL(b53_mirror_del);
+
+static const struct dsa_switch_ops b53_switch_ops = {
.get_tag_protocol = b53_get_tag_protocol,
.setup = b53_setup,
.get_strings = b53_get_strings,
@@ -1464,6 +1569,8 @@ static struct dsa_switch_ops b53_switch_ops = {
.port_fdb_dump = b53_fdb_dump,
.port_fdb_add = b53_fdb_add,
.port_fdb_del = b53_fdb_del,
+ .port_mirror_add = b53_mirror_add,
+ .port_mirror_del = b53_mirror_del,
};
struct b53_chip_data {
@@ -1558,6 +1665,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM53125",
.vlans = 4096,
.enabled_ports = 0xff,
+ .arl_entries = 4,
.cpu_port = B53_CPU_PORT,
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
@@ -1654,7 +1762,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.vlans = 4096,
.enabled_ports = 0x1ff,
.arl_entries = 4,
- .cpu_port = B53_CPU_PORT_25,
+ .cpu_port = B53_CPU_PORT,
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
@@ -1672,6 +1780,18 @@ static const struct b53_chip_data b53_switch_chips[] = {
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
},
+ {
+ .chip_id = BCM7278_DEVICE_ID,
+ .dev_name = "BCM7278",
+ .vlans = 4096,
+ .enabled_ports = 0x1ff,
+ .arl_entries= 4,
+ .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ },
};
static int b53_switch_init(struct b53_device *dev)
@@ -1765,14 +1885,15 @@ struct b53_device *b53_switch_alloc(struct device *base,
struct dsa_switch *ds;
struct b53_device *dev;
- ds = devm_kzalloc(base, sizeof(*ds) + sizeof(*dev), GFP_KERNEL);
+ ds = dsa_switch_alloc(base, DSA_MAX_PORTS);
if (!ds)
return NULL;
- dev = (struct b53_device *)(ds + 1);
+ dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
ds->priv = dev;
- ds->dev = base;
dev->dev = base;
dev->ds = ds;
@@ -1869,7 +1990,7 @@ int b53_switch_register(struct b53_device *dev)
pr_info("found switch: %s, rev %i\n", dev->name, dev->core_rev);
- return dsa_register_switch(dev->ds, dev->ds->dev->of_node);
+ return dsa_register_switch(dev->ds);
}
EXPORT_SYMBOL(b53_switch_register);
diff --git a/drivers/net/dsa/b53/b53_mdio.c b/drivers/net/dsa/b53/b53_mdio.c
index 477a16b5660a..fa7556f5d4fb 100644
--- a/drivers/net/dsa/b53/b53_mdio.c
+++ b/drivers/net/dsa/b53/b53_mdio.c
@@ -375,18 +375,7 @@ static struct mdio_driver b53_mdio_driver = {
.of_match_table = b53_of_match,
},
};
-
-static int __init b53_mdio_driver_register(void)
-{
- return mdio_driver_register(&b53_mdio_driver);
-}
-module_init(b53_mdio_driver_register);
-
-static void __exit b53_mdio_driver_unregister(void)
-{
- mdio_driver_unregister(&b53_mdio_driver);
-}
-module_exit(b53_mdio_driver_unregister);
+mdio_module_driver(b53_mdio_driver);
MODULE_DESCRIPTION("B53 MDIO access driver");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index f192a673caba..155a9c48c317 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/phy.h>
+#include <linux/etherdevice.h>
#include <net/dsa.h>
#include "b53_regs.h"
@@ -61,6 +62,7 @@ enum {
BCM53019_DEVICE_ID = 0x53019,
BCM58XX_DEVICE_ID = 0x5800,
BCM7445_DEVICE_ID = 0x7445,
+ BCM7278_DEVICE_ID = 0x7278,
};
#define B53_N_PORTS 9
@@ -68,7 +70,6 @@ enum {
struct b53_port {
u16 vlan_ctl_mask;
- struct net_device *bridge_dev;
};
struct b53_vlan {
@@ -178,7 +179,8 @@ static inline int is5301x(struct b53_device *dev)
static inline int is58xx(struct b53_device *dev)
{
return dev->chip_id == BCM58XX_DEVICE_ID ||
- dev->chip_id == BCM7445_DEVICE_ID;
+ dev->chip_id == BCM7445_DEVICE_ID ||
+ dev->chip_id == BCM7278_DEVICE_ID;
}
#define B53_CPU_PORT_25 5
@@ -325,25 +327,6 @@ struct b53_arl_entry {
u8 is_static:1;
};
-static inline void b53_mac_from_u64(u64 src, u8 *dst)
-{
- unsigned int i;
-
- for (i = 0; i < ETH_ALEN; i++)
- dst[ETH_ALEN - 1 - i] = (src >> (8 * i)) & 0xff;
-}
-
-static inline u64 b53_mac_to_u64(const u8 *src)
-{
- unsigned int i;
- u64 dst = 0;
-
- for (i = 0; i < ETH_ALEN; i++)
- dst |= (u64)src[ETH_ALEN - 1 - i] << (8 * i);
-
- return dst;
-}
-
static inline void b53_arl_to_entry(struct b53_arl_entry *ent,
u64 mac_vid, u32 fwd_entry)
{
@@ -352,14 +335,14 @@ static inline void b53_arl_to_entry(struct b53_arl_entry *ent,
ent->is_valid = !!(fwd_entry & ARLTBL_VALID);
ent->is_age = !!(fwd_entry & ARLTBL_AGE);
ent->is_static = !!(fwd_entry & ARLTBL_STATIC);
- b53_mac_from_u64(mac_vid, ent->mac);
+ u64_to_ether_addr(mac_vid, ent->mac);
ent->vid = mac_vid >> ARLTBL_VID_S;
}
static inline void b53_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
const struct b53_arl_entry *ent)
{
- *mac_vid = b53_mac_to_u64(ent->mac);
+ *mac_vid = ether_addr_to_u64(ent->mac);
*mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK) << ARLTBL_VID_S;
*fwd_entry = ent->port & ARLTBL_DATA_PORT_ID_MASK;
if (ent->is_valid)
@@ -392,4 +375,41 @@ static inline int b53_switch_get_reset_gpio(struct b53_device *dev)
return -ENOENT;
}
#endif
+
+/* Exported functions towards other drivers */
+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);
+int b53_br_join(struct dsa_switch *ds, int port, struct net_device *bridge);
+void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *bridge);
+void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state);
+void b53_br_fast_age(struct dsa_switch *ds, int port);
+int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering);
+int b53_vlan_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans);
+void b53_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans);
+int b53_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan);
+int b53_vlan_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_vlan *vlan,
+ switchdev_obj_dump_cb_t *cb);
+int b53_fdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans);
+void b53_fdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans);
+int b53_fdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb);
+int b53_fdb_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_fdb *fdb,
+ switchdev_obj_dump_cb_t *cb);
+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);
+
#endif
diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h
index dac0af4e2cd0..e5c86d44667a 100644
--- a/drivers/net/dsa/b53/b53_regs.h
+++ b/drivers/net/dsa/b53/b53_regs.h
@@ -104,6 +104,10 @@
#define B53_UC_FWD_EN BIT(6)
#define B53_MC_FWD_EN BIT(7)
+/* Switch control (8 bit) */
+#define B53_SWITCH_CTRL 0x22
+#define B53_MII_DUMB_FWDG_EN BIT(6)
+
/* (16 bit) */
#define B53_UC_FLOOD_MASK 0x32
#define B53_MC_FLOOD_MASK 0x34
@@ -139,6 +143,7 @@
/* Software reset register (8 bit) */
#define B53_SOFTRESET 0x79
#define SW_RST BIT(7)
+#define EN_CH_RST BIT(6)
#define EN_SW_RST BIT(4)
/* Fast Aging Control register (8 bit) */
@@ -206,6 +211,38 @@
#define BRCM_HDR_P8_EN BIT(0) /* Enable tagging on port 8 */
#define BRCM_HDR_P5_EN BIT(1) /* Enable tagging on port 5 */
+/* Mirror capture control register (16 bit) */
+#define B53_MIR_CAP_CTL 0x10
+#define CAP_PORT_MASK 0xf
+#define BLK_NOT_MIR BIT(14)
+#define MIRROR_EN BIT(15)
+
+/* Ingress mirror control register (16 bit) */
+#define B53_IG_MIR_CTL 0x12
+#define MIRROR_MASK 0x1ff
+#define DIV_EN BIT(13)
+#define MIRROR_FILTER_MASK 0x3
+#define MIRROR_FILTER_SHIFT 14
+#define MIRROR_ALL 0
+#define MIRROR_DA 1
+#define MIRROR_SA 2
+
+/* Ingress mirror divider register (16 bit) */
+#define B53_IG_MIR_DIV 0x14
+#define IN_MIRROR_DIV_MASK 0x3ff
+
+/* Ingress mirror MAC address register (48 bit) */
+#define B53_IG_MIR_MAC 0x16
+
+/* Egress mirror control register (16 bit) */
+#define B53_EG_MIR_CTL 0x1C
+
+/* Egress mirror divider register (16 bit) */
+#define B53_EG_MIR_DIV 0x1E
+
+/* Egress mirror MAC address register (48 bit) */
+#define B53_EG_MIR_MAC 0x20
+
/* Device ID register (8 or 32 bit) */
#define B53_DEVICE_ID 0x30
diff --git a/drivers/net/dsa/b53/b53_srab.c b/drivers/net/dsa/b53/b53_srab.c
index 8a62b6a69703..c37ffd1b6833 100644
--- a/drivers/net/dsa/b53/b53_srab.c
+++ b/drivers/net/dsa/b53/b53_srab.c
@@ -364,6 +364,7 @@ static const struct of_device_id b53_srab_of_match[] = {
{ .compatible = "brcm,bcm53018-srab" },
{ .compatible = "brcm,bcm53019-srab" },
{ .compatible = "brcm,bcm5301x-srab" },
+ { .compatible = "brcm,bcm11360-srab", .data = (void *)BCM58XX_DEVICE_ID },
{ .compatible = "brcm,bcm58522-srab", .data = (void *)BCM58XX_DEVICE_ID },
{ .compatible = "brcm,bcm58525-srab", .data = (void *)BCM58XX_DEVICE_ID },
{ .compatible = "brcm,bcm58535-srab", .data = (void *)BCM58XX_DEVICE_ID },
@@ -371,6 +372,7 @@ static const struct of_device_id b53_srab_of_match[] = {
{ .compatible = "brcm,bcm58623-srab", .data = (void *)BCM58XX_DEVICE_ID },
{ .compatible = "brcm,bcm58625-srab", .data = (void *)BCM58XX_DEVICE_ID },
{ .compatible = "brcm,bcm88312-srab", .data = (void *)BCM58XX_DEVICE_ID },
+ { .compatible = "brcm,cygnus-srab", .data = (void *)BCM58XX_DEVICE_ID },
{ .compatible = "brcm,nsp-srab", .data = (void *)BCM58XX_DEVICE_ID },
{ /* sentinel */ },
};
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 2ce7ae97ac91..648f91b58d1e 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -28,7 +28,6 @@
#include <linux/if_bridge.h>
#include <linux/brcmphy.h>
#include <linux/etherdevice.h>
-#include <net/switchdev.h>
#include <linux/platform_data/b53.h>
#include "bcm_sf2.h"
@@ -61,30 +60,10 @@ static void bcm_sf2_imp_vlan_setup(struct dsa_switch *ds, int cpu_port)
}
}
-static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)
+static void bcm_sf2_brcm_hdr_setup(struct bcm_sf2_priv *priv, int port)
{
- struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
u32 reg, val;
- /* Enable the port memories */
- reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
- reg &= ~P_TXQ_PSM_VDD(port);
- core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
-
- /* Enable Broadcast, Multicast, Unicast forwarding to IMP port */
- reg = core_readl(priv, CORE_IMP_CTL);
- reg |= (RX_BCST_EN | RX_MCST_EN | RX_UCST_EN);
- reg &= ~(RX_DIS | TX_DIS);
- core_writel(priv, reg, CORE_IMP_CTL);
-
- /* Enable forwarding */
- core_writel(priv, SW_FWDG_EN, CORE_SWMODE);
-
- /* Enable IMP port in dumb mode */
- reg = core_readl(priv, CORE_SWITCH_CTRL);
- reg |= MII_DUMB_FWDG_EN;
- core_writel(priv, reg, CORE_SWITCH_CTRL);
-
/* Resolve which bit controls the Broadcom tag */
switch (port) {
case 8:
@@ -119,11 +98,43 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)
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);
+ u32 reg, offset;
+
+ if (priv->type == BCM7445_DEVICE_ID)
+ offset = CORE_STS_OVERRIDE_IMP;
+ else
+ offset = CORE_STS_OVERRIDE_IMP2;
+
+ /* Enable the port memories */
+ reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
+ reg &= ~P_TXQ_PSM_VDD(port);
+ core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
+
+ /* Enable Broadcast, Multicast, Unicast forwarding to IMP port */
+ reg = core_readl(priv, CORE_IMP_CTL);
+ reg |= (RX_BCST_EN | RX_MCST_EN | RX_UCST_EN);
+ reg &= ~(RX_DIS | TX_DIS);
+ core_writel(priv, reg, CORE_IMP_CTL);
+
+ /* Enable forwarding */
+ core_writel(priv, SW_FWDG_EN, CORE_SWMODE);
+
+ /* Enable IMP port in dumb mode */
+ reg = core_readl(priv, CORE_SWITCH_CTRL);
+ reg |= MII_DUMB_FWDG_EN;
+ core_writel(priv, reg, CORE_SWITCH_CTRL);
+
+ bcm_sf2_brcm_hdr_setup(priv, port);
/* Force link status for IMP port */
- reg = core_readl(priv, CORE_STS_OVERRIDE_IMP);
+ reg = core_readl(priv, offset);
reg |= (MII_SW_OR | LINK_STS);
- core_writel(priv, reg, CORE_STS_OVERRIDE_IMP);
+ core_writel(priv, reg, offset);
}
static void bcm_sf2_eee_enable_set(struct dsa_switch *ds, int port, bool enable)
@@ -216,7 +227,8 @@ 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[ds->index].cpu_port;
+ s8 cpu_port = ds->dst->cpu_dp->index;
+ unsigned int i;
u32 reg;
/* Clear the memory power down */
@@ -224,6 +236,18 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
reg &= ~P_TXQ_PSM_VDD(port);
core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
+ /* Enable Broadcom tags for that port if requested */
+ if (priv->brcm_tag_mask & BIT(port))
+ bcm_sf2_brcm_hdr_setup(priv, port);
+
+ /* Configure Traffic Class to QoS mapping, allow each priority to map
+ * to a different queue number
+ */
+ reg = core_readl(priv, CORE_PORT_TC2_QOS_MAP_PORT(port));
+ for (i = 0; i < 8; i++)
+ 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));
@@ -474,10 +498,8 @@ static void bcm_sf2_identify_ports(struct bcm_sf2_priv *priv,
struct device_node *dn)
{
struct device_node *port;
- const char *phy_mode_str;
int mode;
unsigned int port_num;
- int ret;
priv->moca_port = -1;
@@ -491,18 +513,17 @@ static void bcm_sf2_identify_ports(struct bcm_sf2_priv *priv,
* time
*/
mode = of_get_phy_mode(port);
- if (mode < 0) {
- ret = of_property_read_string(port, "phy-mode",
- &phy_mode_str);
- if (ret < 0)
- continue;
-
- if (!strcasecmp(phy_mode_str, "internal"))
- priv->int_phy_mask |= 1 << port_num;
- }
+ if (mode < 0)
+ continue;
+
+ if (mode == PHY_INTERFACE_MODE_INTERNAL)
+ priv->int_phy_mask |= 1 << port_num;
if (mode == PHY_INTERFACE_MODE_MOCA)
priv->moca_port = port_num;
+
+ if (of_property_read_bool(port, "brcm,use-bcm-hdr"))
+ priv->brcm_tag_mask |= 1 << port_num;
}
}
@@ -591,7 +612,12 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
struct ethtool_eee *p = &priv->port_sts[port].eee;
u32 id_mode_dis = 0, port_mode;
const char *str = NULL;
- u32 reg;
+ u32 reg, offset;
+
+ if (priv->type == BCM7445_DEVICE_ID)
+ offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
+ else
+ offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
switch (phydev->interface) {
case PHY_INTERFACE_MODE_RGMII:
@@ -662,7 +688,7 @@ force_link:
if (phydev->duplex == DUPLEX_FULL)
reg |= DUPLX_MODE;
- core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port));
+ core_writel(priv, reg, offset);
if (!phydev->is_pseudo_fixed_link)
p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev);
@@ -672,9 +698,14 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
struct fixed_phy_status *status)
{
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
- u32 duplex, pause;
+ u32 duplex, pause, offset;
u32 reg;
+ if (priv->type == BCM7445_DEVICE_ID)
+ offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
+ else
+ offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
+
duplex = core_readl(priv, CORE_DUPSTS);
pause = core_readl(priv, CORE_PAUSESTS);
@@ -703,13 +734,13 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
status->duplex = !!(duplex & (1 << port));
}
- reg = core_readl(priv, CORE_STS_OVERRIDE_GMIIP_PORT(port));
+ reg = core_readl(priv, offset);
reg |= SW_OVERRIDE;
if (status->link)
reg |= LINK_STS;
else
reg &= ~LINK_STS;
- core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port));
+ core_writel(priv, reg, offset);
if ((pause & (1 << port)) &&
(pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) {
@@ -769,7 +800,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds)
static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port,
struct ethtool_wolinfo *wol)
{
- struct net_device *p = ds->dst[ds->index].master_netdev;
+ struct net_device *p = ds->dst[ds->index].cpu_dp->netdev;
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
struct ethtool_wolinfo pwol;
@@ -792,9 +823,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[ds->index].master_netdev;
+ struct net_device *p = ds->dst[ds->index].cpu_dp->netdev;
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
- s8 cpu_port = ds->dst[ds->index].cpu_port;
+ s8 cpu_port = ds->dst->cpu_dp->index;
struct ethtool_wolinfo pwol;
p->ethtool_ops->get_wol(p, &pwol);
@@ -977,10 +1008,110 @@ static struct b53_io_ops bcm_sf2_io_ops = {
.write64 = bcm_sf2_core_write64,
};
+static const struct dsa_switch_ops bcm_sf2_ops = {
+ .get_tag_protocol = bcm_sf2_sw_get_tag_protocol,
+ .setup = bcm_sf2_sw_setup,
+ .get_strings = b53_get_strings,
+ .get_ethtool_stats = b53_get_ethtool_stats,
+ .get_sset_count = b53_get_sset_count,
+ .get_phy_flags = bcm_sf2_sw_get_phy_flags,
+ .adjust_link = bcm_sf2_sw_adjust_link,
+ .fixed_link_update = bcm_sf2_sw_fixed_link_update,
+ .suspend = bcm_sf2_sw_suspend,
+ .resume = bcm_sf2_sw_resume,
+ .get_wol = bcm_sf2_sw_get_wol,
+ .set_wol = bcm_sf2_sw_set_wol,
+ .port_enable = bcm_sf2_port_setup,
+ .port_disable = bcm_sf2_port_disable,
+ .get_eee = bcm_sf2_sw_get_eee,
+ .set_eee = bcm_sf2_sw_set_eee,
+ .port_bridge_join = b53_br_join,
+ .port_bridge_leave = b53_br_leave,
+ .port_stp_state_set = b53_br_set_stp_state,
+ .port_fast_age = b53_br_fast_age,
+ .port_vlan_filtering = b53_vlan_filtering,
+ .port_vlan_prepare = b53_vlan_prepare,
+ .port_vlan_add = b53_vlan_add,
+ .port_vlan_del = b53_vlan_del,
+ .port_vlan_dump = b53_vlan_dump,
+ .port_fdb_prepare = b53_fdb_prepare,
+ .port_fdb_dump = b53_fdb_dump,
+ .port_fdb_add = b53_fdb_add,
+ .port_fdb_del = b53_fdb_del,
+ .get_rxnfc = bcm_sf2_get_rxnfc,
+ .set_rxnfc = bcm_sf2_set_rxnfc,
+ .port_mirror_add = b53_mirror_add,
+ .port_mirror_del = b53_mirror_del,
+};
+
+struct bcm_sf2_of_data {
+ u32 type;
+ const u16 *reg_offsets;
+ unsigned int core_reg_align;
+};
+
+/* Register offsets for the SWITCH_REG_* block */
+static const u16 bcm_sf2_7445_reg_offsets[] = {
+ [REG_SWITCH_CNTRL] = 0x00,
+ [REG_SWITCH_STATUS] = 0x04,
+ [REG_DIR_DATA_WRITE] = 0x08,
+ [REG_DIR_DATA_READ] = 0x0C,
+ [REG_SWITCH_REVISION] = 0x18,
+ [REG_PHY_REVISION] = 0x1C,
+ [REG_SPHY_CNTRL] = 0x2C,
+ [REG_RGMII_0_CNTRL] = 0x34,
+ [REG_RGMII_1_CNTRL] = 0x40,
+ [REG_RGMII_2_CNTRL] = 0x4c,
+ [REG_LED_0_CNTRL] = 0x90,
+ [REG_LED_1_CNTRL] = 0x94,
+ [REG_LED_2_CNTRL] = 0x98,
+};
+
+static const struct bcm_sf2_of_data bcm_sf2_7445_data = {
+ .type = BCM7445_DEVICE_ID,
+ .core_reg_align = 0,
+ .reg_offsets = bcm_sf2_7445_reg_offsets,
+};
+
+static const u16 bcm_sf2_7278_reg_offsets[] = {
+ [REG_SWITCH_CNTRL] = 0x00,
+ [REG_SWITCH_STATUS] = 0x04,
+ [REG_DIR_DATA_WRITE] = 0x08,
+ [REG_DIR_DATA_READ] = 0x0c,
+ [REG_SWITCH_REVISION] = 0x10,
+ [REG_PHY_REVISION] = 0x14,
+ [REG_SPHY_CNTRL] = 0x24,
+ [REG_RGMII_0_CNTRL] = 0xe0,
+ [REG_RGMII_1_CNTRL] = 0xec,
+ [REG_RGMII_2_CNTRL] = 0xf8,
+ [REG_LED_0_CNTRL] = 0x40,
+ [REG_LED_1_CNTRL] = 0x4c,
+ [REG_LED_2_CNTRL] = 0x58,
+};
+
+static const struct bcm_sf2_of_data bcm_sf2_7278_data = {
+ .type = BCM7278_DEVICE_ID,
+ .core_reg_align = 1,
+ .reg_offsets = bcm_sf2_7278_reg_offsets,
+};
+
+static const struct of_device_id bcm_sf2_of_match[] = {
+ { .compatible = "brcm,bcm7445-switch-v4.0",
+ .data = &bcm_sf2_7445_data
+ },
+ { .compatible = "brcm,bcm7278-switch-v4.0",
+ .data = &bcm_sf2_7278_data
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, bcm_sf2_of_match);
+
static int bcm_sf2_sw_probe(struct platform_device *pdev)
{
const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
struct device_node *dn = pdev->dev.of_node;
+ const struct of_device_id *of_id = NULL;
+ const struct bcm_sf2_of_data *data;
struct b53_platform_data *pdata;
struct dsa_switch_ops *ops;
struct bcm_sf2_priv *priv;
@@ -1008,42 +1139,38 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
if (!pdata)
return -ENOMEM;
+ of_id = of_match_node(bcm_sf2_of_match, dn);
+ if (!of_id || !of_id->data)
+ return -EINVAL;
+
+ data = of_id->data;
+
+ /* Set SWITCH_REG register offsets and SWITCH_CORE align factor */
+ priv->type = data->type;
+ priv->reg_offsets = data->reg_offsets;
+ priv->core_reg_align = data->core_reg_align;
+
/* Auto-detection using standard registers will not work, so
* provide an indication of what kind of device we are for
* b53_common to work with
*/
- pdata->chip_id = BCM7445_DEVICE_ID;
+ pdata->chip_id = priv->type;
dev->pdata = pdata;
priv->dev = dev;
ds = dev->ds;
-
- /* Override the parts that are non-standard wrt. normal b53 devices */
- memcpy(ops, ds->ops, sizeof(*ops));
- ds->ops = ops;
- ds->ops->get_tag_protocol = bcm_sf2_sw_get_tag_protocol;
- ds->ops->setup = bcm_sf2_sw_setup;
- ds->ops->get_phy_flags = bcm_sf2_sw_get_phy_flags;
- ds->ops->adjust_link = bcm_sf2_sw_adjust_link;
- ds->ops->fixed_link_update = bcm_sf2_sw_fixed_link_update;
- ds->ops->suspend = bcm_sf2_sw_suspend;
- ds->ops->resume = bcm_sf2_sw_resume;
- ds->ops->get_wol = bcm_sf2_sw_get_wol;
- ds->ops->set_wol = bcm_sf2_sw_set_wol;
- ds->ops->port_enable = bcm_sf2_port_setup;
- ds->ops->port_disable = bcm_sf2_port_disable;
- ds->ops->get_eee = bcm_sf2_sw_get_eee;
- ds->ops->set_eee = bcm_sf2_sw_set_eee;
-
- /* Avoid having DSA free our slave MDIO bus (checking for
- * ds->slave_mii_bus and ds->ops->phy_read being non-NULL)
- */
- ds->ops->phy_read = NULL;
+ ds->ops = &bcm_sf2_ops;
dev_set_drvdata(&pdev->dev, priv);
spin_lock_init(&priv->indir_lock);
mutex_init(&priv->stats_mutex);
+ mutex_init(&priv->cfp.lock);
+
+ /* CFP rule #0 cannot be used for specific classifications, flag it as
+ * permanently used
+ */
+ set_bit(0, priv->cfp.used);
bcm_sf2_identify_ports(priv, dn->child);
@@ -1073,6 +1200,12 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
return ret;
}
+ ret = bcm_sf2_cfp_rst(priv);
+ if (ret) {
+ pr_err("failed to reset CFP\n");
+ goto out_mdio;
+ }
+
/* Disable all interrupts and request them */
bcm_sf2_intr_disable(priv);
@@ -1179,11 +1312,6 @@ static int bcm_sf2_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(bcm_sf2_pm_ops,
bcm_sf2_suspend, bcm_sf2_resume);
-static const struct of_device_id bcm_sf2_of_match[] = {
- { .compatible = "brcm,bcm7445-switch-v4.0" },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, bcm_sf2_of_match);
static struct platform_driver bcm_sf2_driver = {
.probe = bcm_sf2_sw_probe,
diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h
index 44692673e1d5..7d3030e04f11 100644
--- a/drivers/net/dsa/bcm_sf2.h
+++ b/drivers/net/dsa/bcm_sf2.h
@@ -52,6 +52,13 @@ struct bcm_sf2_port_status {
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);
+ unsigned int rules_cnt;
+};
+
struct bcm_sf2_priv {
/* Base registers, keep those in order with BCM_SF2_REGS_NAME */
void __iomem *core;
@@ -61,6 +68,11 @@ struct bcm_sf2_priv {
void __iomem *fcb;
void __iomem *acb;
+ /* Register offsets indirection tables */
+ u32 type;
+ const u16 *reg_offsets;
+ unsigned int core_reg_align;
+
/* spinlock protecting access to the indirect registers */
spinlock_t indir_lock;
@@ -95,6 +107,12 @@ struct bcm_sf2_priv {
struct device_node *master_mii_dn;
struct mii_bus *slave_mii_bus;
struct mii_bus *master_mii_bus;
+
+ /* Bitmask of ports needing BRCM tags */
+ unsigned int brcm_tag_mask;
+
+ /* CFP rules context */
+ struct bcm_sf2_cfp_priv cfp;
};
static inline struct bcm_sf2_priv *bcm_sf2_to_priv(struct dsa_switch *ds)
@@ -104,6 +122,11 @@ static inline struct bcm_sf2_priv *bcm_sf2_to_priv(struct dsa_switch *ds)
return dev->priv;
}
+static inline u32 bcm_sf2_mangle_addr(struct bcm_sf2_priv *priv, u32 off)
+{
+ return off << priv->core_reg_align;
+}
+
#define SF2_IO_MACRO(name) \
static inline u32 name##_readl(struct bcm_sf2_priv *priv, u32 off) \
{ \
@@ -125,7 +148,7 @@ static inline u64 name##_readq(struct bcm_sf2_priv *priv, u32 off) \
{ \
u32 indir, dir; \
spin_lock(&priv->indir_lock); \
- dir = __raw_readl(priv->name + off); \
+ dir = name##_readl(priv, off); \
indir = reg_readl(priv, REG_DIR_DATA_READ); \
spin_unlock(&priv->indir_lock); \
return (u64)indir << 32 | dir; \
@@ -135,7 +158,7 @@ static inline void name##_writeq(struct bcm_sf2_priv *priv, u64 val, \
{ \
spin_lock(&priv->indir_lock); \
reg_writel(priv, upper_32_bits(val), REG_DIR_DATA_WRITE); \
- __raw_writel(lower_32_bits(val), priv->name + off); \
+ name##_writel(priv, lower_32_bits(val), off); \
spin_unlock(&priv->indir_lock); \
}
@@ -153,8 +176,28 @@ static inline void intrl2_##which##_mask_set(struct bcm_sf2_priv *priv, \
priv->irq##which##_mask |= (mask); \
} \
-SF2_IO_MACRO(core);
-SF2_IO_MACRO(reg);
+static inline u32 core_readl(struct bcm_sf2_priv *priv, u32 off)
+{
+ u32 tmp = bcm_sf2_mangle_addr(priv, off);
+ return __raw_readl(priv->core + tmp);
+}
+
+static inline void core_writel(struct bcm_sf2_priv *priv, u32 val, u32 off)
+{
+ u32 tmp = bcm_sf2_mangle_addr(priv, off);
+ __raw_writel(val, priv->core + tmp);
+}
+
+static inline u32 reg_readl(struct bcm_sf2_priv *priv, u16 off)
+{
+ return __raw_readl(priv->reg + priv->reg_offsets[off]);
+}
+
+static inline void reg_writel(struct bcm_sf2_priv *priv, u32 val, u16 off)
+{
+ __raw_writel(val, priv->reg + priv->reg_offsets[off]);
+}
+
SF2_IO64_MACRO(core);
SF2_IO_MACRO(intrl2_0);
SF2_IO_MACRO(intrl2_1);
@@ -164,4 +207,11 @@ SF2_IO_MACRO(acb);
SWITCH_INTR_L2(0);
SWITCH_INTR_L2(1);
+/* RXNFC */
+int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
+ struct ethtool_rxnfc *nfc, u32 *rule_locs);
+int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
+ struct ethtool_rxnfc *nfc);
+int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv);
+
#endif /* __BCM_SF2_H */
diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c
new file mode 100644
index 000000000000..2fb32d67065f
--- /dev/null
+++ b/drivers/net/dsa/bcm_sf2_cfp.c
@@ -0,0 +1,614 @@
+/*
+ * Broadcom Starfighter 2 DSA switch CFP support
+ *
+ * Copyright (C) 2016, Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/list.h>
+#include <linux/ethtool.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/netdevice.h>
+#include <net/dsa.h>
+#include <linux/bitmap.h>
+
+#include "bcm_sf2.h"
+#include "bcm_sf2_regs.h"
+
+struct cfp_udf_layout {
+ u8 slices[UDF_NUM_SLICES];
+ u32 mask_value;
+
+};
+
+/* 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
+ },
+ .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++) {
+ if (layout[i] != 0)
+ count++;
+ }
+
+ return count;
+}
+
+static void bcm_sf2_cfp_udf_set(struct bcm_sf2_priv *priv,
+ unsigned int slice_num,
+ const u8 *layout)
+{
+ u32 offset = CORE_UDF_0_A_0_8_PORT_0 + slice_num * UDF_SLICE_OFFSET;
+ unsigned int i;
+
+ for (i = 0; i < UDF_NUM_SLICES; i++)
+ core_writel(priv, layout[i], offset + i * 4);
+}
+
+static int bcm_sf2_cfp_op(struct bcm_sf2_priv *priv, unsigned int op)
+{
+ unsigned int timeout = 1000;
+ u32 reg;
+
+ reg = core_readl(priv, CORE_CFP_ACC);
+ reg &= ~(OP_SEL_MASK | RAM_SEL_MASK);
+ reg |= OP_STR_DONE | op;
+ core_writel(priv, reg, CORE_CFP_ACC);
+
+ do {
+ reg = core_readl(priv, CORE_CFP_ACC);
+ if (!(reg & OP_STR_DONE))
+ break;
+
+ cpu_relax();
+ } while (timeout--);
+
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static inline void bcm_sf2_cfp_rule_addr_set(struct bcm_sf2_priv *priv,
+ unsigned int addr)
+{
+ u32 reg;
+
+ WARN_ON(addr >= CFP_NUM_RULES);
+
+ reg = core_readl(priv, CORE_CFP_ACC);
+ reg &= ~(XCESS_ADDR_MASK << XCESS_ADDR_SHIFT);
+ reg |= addr << XCESS_ADDR_SHIFT;
+ core_writel(priv, reg, CORE_CFP_ACC);
+}
+
+static inline unsigned int bcm_sf2_cfp_rule_size(struct bcm_sf2_priv *priv)
+{
+ /* Entry #0 is reserved */
+ return CFP_NUM_RULES - 1;
+}
+
+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);
+ 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;
+
+ /* 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;
+
+ ip_frag = be32_to_cpu(fs->m_ext.data[0]);
+
+ /* 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 / 8;
+
+ if (fs->ring_cookie == RX_CLS_FLOW_DISC ||
+ !(BIT(port_num) & ds->enabled_port_mask) ||
+ port_num >= priv->hw_params.num_ports)
+ return -EINVAL;
+
+ switch (fs->flow_type & ~FLOW_EXT) {
+ case TCP_V4_FLOW:
+ ip_proto = IPPROTO_TCP;
+ v4_spec = &fs->h_u.tcp_ip4_spec;
+ break;
+ case UDP_V4_FLOW:
+ ip_proto = IPPROTO_UDP;
+ v4_spec = &fs->h_u.udp_ip4_spec;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* We only use one UDF slice for now */
+ slice_num = 1;
+ layout = &udf_tcpip4_layout;
+ num_udf = bcm_sf2_get_num_udf_slices(layout->slices);
+
+ /* Apply the UDF layout for this filter */
+ bcm_sf2_cfp_udf_set(priv, slice_num, layout->slices);
+
+ /* Apply to all packets received through this port */
+ core_writel(priv, BIT(port), CORE_CFP_DATA_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]
+ */
+ core_writel(priv, v4_spec->tos << 16 | ip_proto << 8 | ip_frag << 7,
+ CORE_CFP_DATA_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));
+
+ /* C-Tag [31:24]
+ * UDF_n_A8 [23:8]
+ * UDF_n_A7 [7:0]
+ */
+ core_writel(priv, 0, CORE_CFP_DATA_PORT(4));
+
+ /* UDF_n_A7 [31:24]
+ * UDF_n_A6 [23:8]
+ * UDF_n_A5 [7:0]
+ */
+ core_writel(priv, be16_to_cpu(v4_spec->pdst) >> 8,
+ CORE_CFP_DATA_PORT(3));
+
+ /* 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;
+ core_writel(priv, reg, CORE_CFP_DATA_PORT(2));
+
+ /* 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;
+ core_writel(priv, reg, CORE_CFP_DATA_PORT(1));
+
+ /* 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;
+ core_writel(priv, reg, CORE_CFP_DATA_PORT(0));
+
+ /* Source port map match */
+ core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7));
+
+ /* Mask with the specific layout for IPv4 packets */
+ core_writel(priv, layout->mask_value, CORE_CFP_MASK_PORT(6));
+
+ /* Mask all but valid UDFs */
+ core_writel(priv, GENMASK(num_udf - 1, 0) << 24, CORE_CFP_MASK_PORT(5));
+
+ /* Mask all */
+ core_writel(priv, 0, CORE_CFP_MASK_PORT(4));
+
+ /* 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));
+
+ /* 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;
+
+ /* 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;
+ }
+
+ /* 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).
+ */
+ queue_num = fs->ring_cookie % 8;
+ if (port_num >= 7)
+ port_num -= 1;
+
+ reg = CHANGE_FWRD_MAP_IB_REP_ARL | BIT(port_num + DST_MAP_IB_SHIFT) |
+ CHANGE_TC | queue_num << NEW_TC_SHIFT;
+
+ core_writel(priv, reg, CORE_ACT_POL_DATA0);
+
+ /* Set classification ID that needs to be put in Broadcom tag */
+ core_writel(priv, rule_index << CHAIN_ID_SHIFT,
+ CORE_ACT_POL_DATA1);
+
+ core_writel(priv, 0, CORE_ACT_POL_DATA2);
+
+ /* 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;
+ }
+
+ /* 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;
+
+ return 0;
+}
+
+static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port,
+ u32 loc)
+{
+ int ret;
+ u32 reg;
+
+ /* Refuse deletion of unused rules, and the default reserved rule */
+ if (!test_bit(loc, priv->cfp.used) || loc == 0)
+ return -EINVAL;
+
+ /* Indicate which rule we want to read */
+ bcm_sf2_cfp_rule_addr_set(priv, loc);
+
+ ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL);
+ if (ret)
+ return ret;
+
+ /* Clear its valid bits */
+ reg = core_readl(priv, CORE_CFP_DATA_PORT(0));
+ reg &= ~SLICE_VALID;
+ core_writel(priv, reg, CORE_CFP_DATA_PORT(0));
+
+ /* Write back this entry into the TCAM now */
+ ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);
+ if (ret)
+ return ret;
+
+ clear_bit(loc, priv->cfp.used);
+
+ return 0;
+}
+
+static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(flow->m_u); i++)
+ flow->m_u.hdata[i] ^= 0xff;
+
+ flow->m_ext.vlan_etype ^= cpu_to_be16(~0);
+ flow->m_ext.vlan_tci ^= cpu_to_be16(~0);
+ flow->m_ext.data[0] ^= cpu_to_be32(~0);
+ 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)
+{
+ struct ethtool_tcpip4_spec *v4_spec;
+ unsigned int queue_num;
+ 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);
+
+ reg = core_readl(priv, CORE_CFP_DATA_PORT(3));
+ /* src port [15:8] */
+ src_dst_port = reg << 8;
+
+ reg = core_readl(priv, CORE_CFP_DATA_PORT(2));
+ /* 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));
+ /* 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))
+ return -EINVAL;
+
+ /* IPv4 src [7:0] */
+ ipv4 |= (reg >> 24) & 0xff;
+ /* 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);
+
+ /* Read last to avoid next entry clobbering the results during search
+ * operations
+ */
+ reg = core_readl(priv, CORE_CFP_DATA_PORT(7));
+ if (!(reg & 1 << port))
+ return -EINVAL;
+
+ bcm_sf2_invert_masks(&nfc->fs);
+
+ /* Put the TCAM size here */
+ nfc->data = bcm_sf2_cfp_rule_size(priv);
+
+ return 0;
+}
+
+/* We implement the search doing a TCAM search operation */
+static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv,
+ int port, struct ethtool_rxnfc *nfc,
+ 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 < CFP_NUM_RULES);
+
+ /* Put the TCAM size here */
+ nfc->data = bcm_sf2_cfp_rule_size(priv);
+ nfc->rule_cnt = rules_cnt;
+
+ return 0;
+}
+
+int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
+ struct ethtool_rxnfc *nfc, u32 *rule_locs)
+{
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ int ret = 0;
+
+ mutex_lock(&priv->cfp.lock);
+
+ switch (nfc->cmd) {
+ case ETHTOOL_GRXCLSRLCNT:
+ /* Subtract the default, unusable rule */
+ nfc->rule_cnt = bitmap_weight(priv->cfp.used,
+ CFP_NUM_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);
+ break;
+ case ETHTOOL_GRXCLSRLALL:
+ ret = bcm_sf2_cfp_rule_get_all(priv, port, nfc, rule_locs);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&priv->cfp.lock);
+
+ return ret;
+}
+
+int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
+ struct ethtool_rxnfc *nfc)
+{
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ int ret = 0;
+
+ mutex_lock(&priv->cfp.lock);
+
+ switch (nfc->cmd) {
+ case ETHTOOL_SRXCLSRLINS:
+ ret = bcm_sf2_cfp_rule_set(ds, port, &nfc->fs);
+ break;
+
+ case ETHTOOL_SRXCLSRLDEL:
+ ret = bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&priv->cfp.lock);
+
+ return ret;
+}
+
+int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv)
+{
+ unsigned int timeout = 1000;
+ u32 reg;
+
+ reg = core_readl(priv, CORE_CFP_ACC);
+ reg |= TCAM_RESET;
+ core_writel(priv, reg, CORE_CFP_ACC);
+
+ do {
+ reg = core_readl(priv, CORE_CFP_ACC);
+ if (!(reg & TCAM_RESET))
+ break;
+
+ cpu_relax();
+ } while (timeout--);
+
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ return 0;
+}
diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h
index 838fe373cd6f..26052450091e 100644
--- a/drivers/net/dsa/bcm_sf2_regs.h
+++ b/drivers/net/dsa/bcm_sf2_regs.h
@@ -12,22 +12,36 @@
#define __BCM_SF2_REGS_H
/* Register set relative to 'REG' */
-#define REG_SWITCH_CNTRL 0x00
-#define MDIO_MASTER_SEL (1 << 0)
-#define REG_SWITCH_STATUS 0x04
-#define REG_DIR_DATA_WRITE 0x08
-#define REG_DIR_DATA_READ 0x0C
+enum bcm_sf2_reg_offs {
+ REG_SWITCH_CNTRL = 0,
+ REG_SWITCH_STATUS,
+ REG_DIR_DATA_WRITE,
+ REG_DIR_DATA_READ,
+ REG_SWITCH_REVISION,
+ REG_PHY_REVISION,
+ REG_SPHY_CNTRL,
+ REG_RGMII_0_CNTRL,
+ REG_RGMII_1_CNTRL,
+ REG_RGMII_2_CNTRL,
+ REG_LED_0_CNTRL,
+ REG_LED_1_CNTRL,
+ REG_LED_2_CNTRL,
+ REG_SWITCH_REG_MAX,
+};
+
+/* Relative to REG_SWITCH_CNTRL */
+#define MDIO_MASTER_SEL (1 << 0)
-#define REG_SWITCH_REVISION 0x18
+/* Relative to REG_SWITCH_REVISION */
#define SF2_REV_MASK 0xffff
#define SWITCH_TOP_REV_SHIFT 16
#define SWITCH_TOP_REV_MASK 0xffff
-#define REG_PHY_REVISION 0x1C
+/* Relative to REG_PHY_REVISION */
#define PHY_REVISION_MASK 0xffff
-#define REG_SPHY_CNTRL 0x2C
+/* Relative to REG_SPHY_CNTRL */
#define IDDQ_BIAS (1 << 0)
#define EXT_PWR_DOWN (1 << 1)
#define FORCE_DLL_EN (1 << 2)
@@ -37,13 +51,8 @@
#define PHY_PHYAD_SHIFT 8
#define PHY_PHYAD_MASK 0x1F
-#define REG_RGMII_0_BASE 0x34
-#define REG_RGMII_CNTRL 0x00
-#define REG_RGMII_IB_STATUS 0x04
-#define REG_RGMII_RX_CLOCK_DELAY_CNTRL 0x08
-#define REG_RGMII_CNTRL_SIZE 0x0C
-#define REG_RGMII_CNTRL_P(x) (REG_RGMII_0_BASE + \
- ((x) * REG_RGMII_CNTRL_SIZE))
+#define REG_RGMII_CNTRL_P(x) (REG_RGMII_0_CNTRL + (x))
+
/* Relative to REG_RGMII_CNTRL */
#define RGMII_MODE_EN (1 << 0)
#define ID_MODE_DIS (1 << 1)
@@ -61,8 +70,8 @@
#define LPI_COUNT_SHIFT 9
#define LPI_COUNT_MASK 0x3F
-#define REG_LED_CNTRL_BASE 0x90
-#define REG_LED_CNTRL(x) (REG_LED_CNTRL_BASE + (x) * 4)
+#define REG_LED_CNTRL(x) (REG_LED_0_CNTRL + (x))
+
#define SPDLNK_SRC_SEL (1 << 24)
/* Register set relative to 'INTRL2_0' and 'INTRL2_1' */
@@ -125,6 +134,9 @@
#define GMII_SPEED_UP_2G (1 << 6)
#define MII_SW_OR (1 << 7)
+/* Alternate layout for e.g: 7278 */
+#define CORE_STS_OVERRIDE_IMP2 0x39040
+
#define CORE_NEW_CTRL 0x00084
#define IP_MC (1 << 0)
#define OUTRANGEERR_DISCARD (1 << 1)
@@ -142,6 +154,7 @@
#define SW_LEARN_CNTL(x) (1 << (x))
#define CORE_STS_OVERRIDE_GMIIP_PORT(x) (0x160 + (x) * 4)
+#define CORE_STS_OVERRIDE_GMIIP2_PORT(x) (0x39000 + (x) * 8)
#define LINK_STS (1 << 0)
#define DUPLX_MODE (1 << 1)
#define SPEED_SHIFT 2
@@ -225,6 +238,10 @@
#define P_TXQ_PSM_VDD(x) (P_TXQ_PSM_VDD_MASK << \
((x) * P_TXQ_PSM_VDD_SHIFT))
+#define CORE_PORT_TC2_QOS_MAP_PORT(x) (0xc1c0 + ((x) * 0x10))
+#define PRT_TO_QID_MASK 0x3
+#define PRT_TO_QID_SHIFT 3
+
#define CORE_PORT_VLAN_CTL_PORT(x) (0xc400 + ((x) * 0x8))
#define PORT_VLAN_CTRL_MASK 0x1ff
@@ -238,4 +255,150 @@
#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
+#define OP_SEL_READ (1 << OP_SEL_SHIFT)
+#define OP_SEL_WRITE (2 << OP_SEL_SHIFT)
+#define OP_SEL_SEARCH (4 << OP_SEL_SHIFT)
+#define OP_SEL_MASK (7 << OP_SEL_SHIFT)
+#define CFP_RAM_CLEAR (1 << 4)
+#define RAM_SEL_SHIFT 10
+#define TCAM_SEL (1 << RAM_SEL_SHIFT)
+#define ACT_POL_RAM (2 << RAM_SEL_SHIFT)
+#define RATE_METER_RAM (4 << RAM_SEL_SHIFT)
+#define GREEN_STAT_RAM (8 << RAM_SEL_SHIFT)
+#define YELLOW_STAT_RAM (16 << RAM_SEL_SHIFT)
+#define RED_STAT_RAM (24 << RAM_SEL_SHIFT)
+#define RAM_SEL_MASK (0x1f << RAM_SEL_SHIFT)
+#define TCAM_RESET (1 << 15)
+#define XCESS_ADDR_SHIFT 16
+#define XCESS_ADDR_MASK 0xff
+#define SEARCH_STS (1 << 27)
+#define RD_STS_SHIFT 28
+#define RD_STS_TCAM (1 << RD_STS_SHIFT)
+#define RD_STS_ACT_POL_RAM (2 << RD_STS_SHIFT)
+#define RD_STS_RATE_METER_RAM (4 << RD_STS_SHIFT)
+#define RD_STS_STAT_RAM (8 << RD_STS_SHIFT)
+
+#define CORE_CFP_RATE_METER_GLOBAL_CTL 0x28010
+
+#define CORE_CFP_DATA_PORT_0 0x28040
+#define CORE_CFP_DATA_PORT(x) (CORE_CFP_DATA_PORT_0 + \
+ (x) * 0x10)
+
+/* UDF_DATA7 */
+#define L3_FRAMING_SHIFT 24
+#define L3_FRAMING_MASK (0x3 << L3_FRAMING_SHIFT)
+#define IPPROTO_SHIFT 8
+#define IPPROTO_MASK (0xff << IPPROTO_SHIFT)
+#define IP_FRAG (1 << 7)
+
+/* UDF_DATA0 */
+#define SLICE_VALID 3
+#define SLICE_NUM_SHIFT 2
+#define SLICE_NUM(x) ((x) << SLICE_NUM_SHIFT)
+
+#define CORE_CFP_MASK_PORT_0 0x280c0
+
+#define CORE_CFP_MASK_PORT(x) (CORE_CFP_MASK_PORT_0 + \
+ (x) * 0x10)
+
+#define CORE_ACT_POL_DATA0 0x28140
+#define VLAN_BYP (1 << 0)
+#define EAP_BYP (1 << 1)
+#define STP_BYP (1 << 2)
+#define REASON_CODE_SHIFT 3
+#define REASON_CODE_MASK 0x3f
+#define LOOP_BK_EN (1 << 9)
+#define NEW_TC_SHIFT 10
+#define NEW_TC_MASK 0x7
+#define CHANGE_TC (1 << 13)
+#define DST_MAP_IB_SHIFT 14
+#define DST_MAP_IB_MASK 0x1ff
+#define CHANGE_FWRD_MAP_IB_SHIFT 24
+#define CHANGE_FWRD_MAP_IB_MASK 0x3
+#define CHANGE_FWRD_MAP_IB_NO_DEST (0 << CHANGE_FWRD_MAP_IB_SHIFT)
+#define CHANGE_FWRD_MAP_IB_REM_ARL (1 << CHANGE_FWRD_MAP_IB_SHIFT)
+#define CHANGE_FWRD_MAP_IB_REP_ARL (2 << CHANGE_FWRD_MAP_IB_SHIFT)
+#define CHANGE_FWRD_MAP_IB_ADD_DST (3 << CHANGE_FWRD_MAP_IB_SHIFT)
+#define NEW_DSCP_IB_SHIFT 26
+#define NEW_DSCP_IB_MASK 0x3f
+
+#define CORE_ACT_POL_DATA1 0x28150
+#define CHANGE_DSCP_IB (1 << 0)
+#define DST_MAP_OB_SHIFT 1
+#define DST_MAP_OB_MASK 0x3ff
+#define CHANGE_FWRD_MAP_OB_SHIT 11
+#define CHANGE_FWRD_MAP_OB_MASK 0x3
+#define NEW_DSCP_OB_SHIFT 13
+#define NEW_DSCP_OB_MASK 0x3f
+#define CHANGE_DSCP_OB (1 << 19)
+#define CHAIN_ID_SHIFT 20
+#define CHAIN_ID_MASK 0xff
+#define CHANGE_COLOR (1 << 28)
+#define NEW_COLOR_SHIFT 29
+#define NEW_COLOR_MASK 0x3
+#define NEW_COLOR_GREEN (0 << NEW_COLOR_SHIFT)
+#define NEW_COLOR_YELLOW (1 << NEW_COLOR_SHIFT)
+#define NEW_COLOR_RED (2 << NEW_COLOR_SHIFT)
+#define RED_DEFAULT (1 << 31)
+
+#define CORE_ACT_POL_DATA2 0x28160
+#define MAC_LIMIT_BYPASS (1 << 0)
+#define CHANGE_TC_O (1 << 1)
+#define NEW_TC_O_SHIFT 2
+#define NEW_TC_O_MASK 0x7
+#define SPCP_RMK_DISABLE (1 << 5)
+#define CPCP_RMK_DISABLE (1 << 6)
+#define DEI_RMK_DISABLE (1 << 7)
+
+#define CORE_RATE_METER0 0x28180
+#define COLOR_MODE (1 << 0)
+#define POLICER_ACTION (1 << 1)
+#define COUPLING_FLAG (1 << 2)
+#define POLICER_MODE_SHIFT 3
+#define POLICER_MODE_MASK 0x3
+#define POLICER_MODE_RFC2698 (0 << POLICER_MODE_SHIFT)
+#define POLICER_MODE_RFC4115 (1 << POLICER_MODE_SHIFT)
+#define POLICER_MODE_MEF (2 << POLICER_MODE_SHIFT)
+#define POLICER_MODE_DISABLE (3 << POLICER_MODE_SHIFT)
+
+#define CORE_RATE_METER1 0x28190
+#define EIR_TK_BKT_MASK 0x7fffff
+
+#define CORE_RATE_METER2 0x281a0
+#define EIR_BKT_SIZE_MASK 0xfffff
+
+#define CORE_RATE_METER3 0x281b0
+#define EIR_REF_CNT_MASK 0x7ffff
+
+#define CORE_RATE_METER4 0x281c0
+#define CIR_TK_BKT_MASK 0x7fffff
+
+#define CORE_RATE_METER5 0x281d0
+#define CIR_BKT_SIZE_MASK 0xfffff
+
+#define CORE_RATE_METER6 0x281e0
+#define CIR_REF_CNT_MASK 0x7ffff
+
+#define CORE_CFP_CTL_REG 0x28400
+#define CFP_EN_MAP_MASK 0x1ff
+
+/* IPv4 slices, 3 of them */
+#define CORE_UDF_0_A_0_8_PORT_0 0x28440
+#define CFG_UDF_OFFSET_MASK 0x1f
+#define CFG_UDF_OFFSET_BASE_SHIFT 5
+#define CFG_UDF_SOF (0 << CFG_UDF_OFFSET_BASE_SHIFT)
+#define CFG_UDF_EOL2 (2 << CFG_UDF_OFFSET_BASE_SHIFT)
+#define CFG_UDF_EOL3 (3 << CFG_UDF_OFFSET_BASE_SHIFT)
+
+/* Number of slices for IPv4, IPv6 and non-IP */
+#define UDF_NUM_SLICES 9
+
+/* Spacing between different slices */
+#define UDF_SLICE_OFFSET 0x40
+
+#define CFP_NUM_RULES 256
+
#endif /* __BCM_SF2_REGS_H */
diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c
new file mode 100644
index 000000000000..fdd8f3872102
--- /dev/null
+++ b/drivers/net/dsa/dsa_loop.c
@@ -0,0 +1,400 @@
+/*
+ * Distributed Switch Architecture loopback driver
+ *
+ * Copyright (C) 2016, Florian Fainelli <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/phy_fixed.h>
+#include <linux/export.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/if_bridge.h>
+#include <net/dsa.h>
+
+#include "dsa_loop.h"
+
+struct dsa_loop_vlan {
+ u16 members;
+ u16 untagged;
+};
+
+struct dsa_loop_mib_entry {
+ char name[ETH_GSTRING_LEN];
+ unsigned long val;
+};
+
+enum dsa_loop_mib_counters {
+ DSA_LOOP_PHY_READ_OK,
+ DSA_LOOP_PHY_READ_ERR,
+ DSA_LOOP_PHY_WRITE_OK,
+ DSA_LOOP_PHY_WRITE_ERR,
+ __DSA_LOOP_CNT_MAX,
+};
+
+static struct dsa_loop_mib_entry dsa_loop_mibs[] = {
+ [DSA_LOOP_PHY_READ_OK] = { "phy_read_ok", },
+ [DSA_LOOP_PHY_READ_ERR] = { "phy_read_err", },
+ [DSA_LOOP_PHY_WRITE_OK] = { "phy_write_ok", },
+ [DSA_LOOP_PHY_WRITE_ERR] = { "phy_write_err", },
+};
+
+struct dsa_loop_port {
+ struct dsa_loop_mib_entry mib[__DSA_LOOP_CNT_MAX];
+};
+
+#define DSA_LOOP_VLANS 5
+
+struct dsa_loop_priv {
+ struct mii_bus *bus;
+ unsigned int port_base;
+ struct dsa_loop_vlan vlans[DSA_LOOP_VLANS];
+ struct net_device *netdev;
+ struct dsa_loop_port ports[DSA_MAX_PORTS];
+ u16 pvid;
+};
+
+static struct phy_device *phydevs[PHY_MAX_ADDR];
+
+static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds)
+{
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ return DSA_TAG_PROTO_NONE;
+}
+
+static int dsa_loop_setup(struct dsa_switch *ds)
+{
+ struct dsa_loop_priv *ps = ds->priv;
+ unsigned int i;
+
+ for (i = 0; i < ds->num_ports; i++)
+ memcpy(ps->ports[i].mib, dsa_loop_mibs,
+ sizeof(dsa_loop_mibs));
+
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static int dsa_loop_get_sset_count(struct dsa_switch *ds)
+{
+ return __DSA_LOOP_CNT_MAX;
+}
+
+static void dsa_loop_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+{
+ struct dsa_loop_priv *ps = ds->priv;
+ unsigned int i;
+
+ for (i = 0; i < __DSA_LOOP_CNT_MAX; i++)
+ memcpy(data + i * ETH_GSTRING_LEN,
+ ps->ports[port].mib[i].name, ETH_GSTRING_LEN);
+}
+
+static void dsa_loop_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *data)
+{
+ struct dsa_loop_priv *ps = ds->priv;
+ unsigned int i;
+
+ for (i = 0; i < __DSA_LOOP_CNT_MAX; i++)
+ 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;
+ struct mii_bus *bus = ps->bus;
+ int ret;
+
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ ret = mdiobus_read_nested(bus, ps->port_base + port, regnum);
+ if (ret < 0)
+ ps->ports[port].mib[DSA_LOOP_PHY_READ_ERR].val++;
+ else
+ ps->ports[port].mib[DSA_LOOP_PHY_READ_OK].val++;
+
+ return ret;
+}
+
+static int dsa_loop_phy_write(struct dsa_switch *ds, int port,
+ int regnum, u16 value)
+{
+ struct dsa_loop_priv *ps = ds->priv;
+ struct mii_bus *bus = ps->bus;
+ int ret;
+
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ ret = mdiobus_write_nested(bus, ps->port_base + port, regnum, value);
+ if (ret < 0)
+ ps->ports[port].mib[DSA_LOOP_PHY_WRITE_ERR].val++;
+ else
+ ps->ports[port].mib[DSA_LOOP_PHY_WRITE_OK].val++;
+
+ return ret;
+}
+
+static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+{
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+{
+ dev_dbg(ds->dev, "%s\n", __func__);
+}
+
+static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,
+ u8 state)
+{
+ dev_dbg(ds->dev, "%s\n", __func__);
+}
+
+static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,
+ bool vlan_filtering)
+{
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static int dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
+{
+ struct dsa_loop_priv *ps = ds->priv;
+ struct mii_bus *bus = ps->bus;
+
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ /* Just do a sleeping operation to make lockdep checks effective */
+ mdiobus_read(bus, ps->port_base + port, MII_BMSR);
+
+ if (vlan->vid_end > DSA_LOOP_VLANS)
+ return -ERANGE;
+
+ return 0;
+}
+
+static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
+{
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+ struct dsa_loop_priv *ps = ds->priv;
+ struct mii_bus *bus = ps->bus;
+ struct dsa_loop_vlan *vl;
+ u16 vid;
+
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ /* Just do a sleeping operation to make lockdep checks effective */
+ mdiobus_read(bus, ps->port_base + port, MII_BMSR);
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+ vl = &ps->vlans[vid];
+
+ vl->members |= BIT(port);
+ if (untagged)
+ vl->untagged |= BIT(port);
+ else
+ vl->untagged &= ~BIT(port);
+ }
+
+ if (pvid)
+ ps->pvid = vid;
+}
+
+static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ struct dsa_loop_priv *ps = ds->priv;
+ struct mii_bus *bus = ps->bus;
+ struct dsa_loop_vlan *vl;
+ u16 vid, pvid = ps->pvid;
+
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ /* Just do a sleeping operation to make lockdep checks effective */
+ mdiobus_read(bus, ps->port_base + port, MII_BMSR);
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+ vl = &ps->vlans[vid];
+
+ vl->members &= ~BIT(port);
+ if (untagged)
+ vl->untagged &= ~BIT(port);
+
+ if (pvid == vid)
+ pvid = 1;
+ }
+ ps->pvid = pvid;
+
+ return 0;
+}
+
+static int dsa_loop_port_vlan_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_vlan *vlan,
+ switchdev_obj_dump_cb_t *cb)
+{
+ struct dsa_loop_priv *ps = ds->priv;
+ struct mii_bus *bus = ps->bus;
+ struct dsa_loop_vlan *vl;
+ u16 vid, vid_start = 0;
+ int err = 0;
+
+ dev_dbg(ds->dev, "%s\n", __func__);
+
+ /* Just do a sleeping operation to make lockdep checks effective */
+ mdiobus_read(bus, ps->port_base + port, MII_BMSR);
+
+ for (vid = vid_start; vid < DSA_LOOP_VLANS; vid++) {
+ vl = &ps->vlans[vid];
+
+ if (!(vl->members & BIT(port)))
+ continue;
+
+ vlan->vid_begin = vlan->vid_end = vid;
+ vlan->flags = 0;
+
+ if (vl->untagged & BIT(port))
+ vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+ if (ps->pvid == vid)
+ vlan->flags |= BRIDGE_VLAN_INFO_PVID;
+
+ err = cb(&vlan->obj);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static struct dsa_switch_ops dsa_loop_driver = {
+ .get_tag_protocol = dsa_loop_get_protocol,
+ .setup = dsa_loop_setup,
+ .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,
+ .port_bridge_leave = dsa_loop_port_bridge_leave,
+ .port_stp_state_set = dsa_loop_port_stp_state_set,
+ .port_vlan_filtering = dsa_loop_port_vlan_filtering,
+ .port_vlan_prepare = dsa_loop_port_vlan_prepare,
+ .port_vlan_add = dsa_loop_port_vlan_add,
+ .port_vlan_del = dsa_loop_port_vlan_del,
+ .port_vlan_dump = dsa_loop_port_vlan_dump,
+};
+
+static int dsa_loop_drv_probe(struct mdio_device *mdiodev)
+{
+ struct dsa_loop_pdata *pdata = mdiodev->dev.platform_data;
+ struct dsa_loop_priv *ps;
+ struct dsa_switch *ds;
+
+ if (!pdata)
+ return -ENODEV;
+
+ dev_info(&mdiodev->dev, "%s: 0x%0x\n",
+ pdata->name, pdata->enabled_ports);
+
+ ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS);
+ if (!ds)
+ return -ENOMEM;
+
+ ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL);
+ if (!ps)
+ return -ENOMEM;
+
+ ps->netdev = dev_get_by_name(&init_net, pdata->netdev);
+ if (!ps->netdev)
+ return -EPROBE_DEFER;
+
+ pdata->cd.netdev[DSA_LOOP_CPU_PORT] = &ps->netdev->dev;
+
+ ds->dev = &mdiodev->dev;
+ ds->ops = &dsa_loop_driver;
+ ds->priv = ps;
+ ps->bus = mdiodev->bus;
+
+ dev_set_drvdata(&mdiodev->dev, ds);
+
+ return dsa_register_switch(ds);
+}
+
+static void dsa_loop_drv_remove(struct mdio_device *mdiodev)
+{
+ struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
+ struct dsa_loop_priv *ps = ds->priv;
+
+ dsa_unregister_switch(ds);
+ dev_put(ps->netdev);
+}
+
+static struct mdio_driver dsa_loop_drv = {
+ .mdiodrv.driver = {
+ .name = "dsa-loop",
+ },
+ .probe = dsa_loop_drv_probe,
+ .remove = dsa_loop_drv_remove,
+};
+
+#define NUM_FIXED_PHYS (DSA_LOOP_NUM_PORTS - 2)
+
+static int __init dsa_loop_init(void)
+{
+ struct fixed_phy_status status = {
+ .link = 1,
+ .speed = SPEED_100,
+ .duplex = DUPLEX_FULL,
+ };
+ unsigned int i;
+
+ for (i = 0; i < NUM_FIXED_PHYS; i++)
+ phydevs[i] = fixed_phy_register(PHY_POLL, &status, -1, NULL);
+
+ return mdio_driver_register(&dsa_loop_drv);
+}
+module_init(dsa_loop_init);
+
+static void __exit dsa_loop_exit(void)
+{
+ unsigned int i;
+
+ mdio_driver_unregister(&dsa_loop_drv);
+ for (i = 0; i < NUM_FIXED_PHYS; i++)
+ if (phydevs[i])
+ fixed_phy_unregister(phydevs[i]);
+}
+module_exit(dsa_loop_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Florian Fainelli");
+MODULE_DESCRIPTION("DSA loopback driver");
diff --git a/drivers/net/dsa/dsa_loop.h b/drivers/net/dsa/dsa_loop.h
new file mode 100644
index 000000000000..dc396877fc95
--- /dev/null
+++ b/drivers/net/dsa/dsa_loop.h
@@ -0,0 +1,19 @@
+#ifndef __DSA_LOOP_H
+#define __DSA_LOOP_H
+
+struct dsa_chip_data;
+
+struct dsa_loop_pdata {
+ /* Must be first, such that dsa_register_switch() can access this
+ * without gory pointer manipulations
+ */
+ struct dsa_chip_data cd;
+ const char *name;
+ unsigned int enabled_ports;
+ const char *netdev;
+};
+
+#define DSA_LOOP_NUM_PORTS 6
+#define DSA_LOOP_CPU_PORT (DSA_LOOP_NUM_PORTS - 1)
+
+#endif /* __DSA_LOOP_H */
diff --git a/drivers/net/dsa/dsa_loop_bdinfo.c b/drivers/net/dsa/dsa_loop_bdinfo.c
new file mode 100644
index 000000000000..fb8d5dc71013
--- /dev/null
+++ b/drivers/net/dsa/dsa_loop_bdinfo.c
@@ -0,0 +1,34 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/phy.h>
+#include <net/dsa.h>
+
+#include "dsa_loop.h"
+
+static struct dsa_loop_pdata dsa_loop_pdata = {
+ .cd = {
+ .port_names[0] = "lan1",
+ .port_names[1] = "lan2",
+ .port_names[2] = "lan3",
+ .port_names[3] = "lan4",
+ .port_names[DSA_LOOP_CPU_PORT] = "cpu",
+ },
+ .name = "DSA mockup driver",
+ .enabled_ports = 0x1f,
+ .netdev = "eth0",
+};
+
+static const struct mdio_board_info bdinfo = {
+ .bus_id = "fixed-0",
+ .modalias = "dsa-loop",
+ .mdio_addr = 31,
+ .platform_data = &dsa_loop_pdata,
+};
+
+static int __init dsa_loop_bdinfo_init(void)
+{
+ return mdiobus_register_board_info(&bdinfo, 1);
+}
+arch_initcall(dsa_loop_bdinfo_init)
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c
new file mode 100644
index 000000000000..cd76e61f1fca
--- /dev/null
+++ b/drivers/net/dsa/lan9303-core.c
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2017 Pengutronix, Juergen Borleis <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/mutex.h>
+#include <linux/mii.h>
+
+#include "lan9303.h"
+
+#define LAN9303_CHIP_REV 0x14
+# define LAN9303_CHIP_ID 0x9303
+#define LAN9303_IRQ_CFG 0x15
+# define LAN9303_IRQ_CFG_IRQ_ENABLE BIT(8)
+# define LAN9303_IRQ_CFG_IRQ_POL BIT(4)
+# define LAN9303_IRQ_CFG_IRQ_TYPE BIT(0)
+#define LAN9303_INT_STS 0x16
+# define LAN9303_INT_STS_PHY_INT2 BIT(27)
+# define LAN9303_INT_STS_PHY_INT1 BIT(26)
+#define LAN9303_INT_EN 0x17
+# define LAN9303_INT_EN_PHY_INT2_EN BIT(27)
+# define LAN9303_INT_EN_PHY_INT1_EN BIT(26)
+#define LAN9303_HW_CFG 0x1D
+# define LAN9303_HW_CFG_READY BIT(27)
+# define LAN9303_HW_CFG_AMDX_EN_PORT2 BIT(26)
+# define LAN9303_HW_CFG_AMDX_EN_PORT1 BIT(25)
+#define LAN9303_PMI_DATA 0x29
+#define LAN9303_PMI_ACCESS 0x2A
+# define LAN9303_PMI_ACCESS_PHY_ADDR(x) (((x) & 0x1f) << 11)
+# define LAN9303_PMI_ACCESS_MIIRINDA(x) (((x) & 0x1f) << 6)
+# define LAN9303_PMI_ACCESS_MII_BUSY BIT(0)
+# define LAN9303_PMI_ACCESS_MII_WRITE BIT(1)
+#define LAN9303_MANUAL_FC_1 0x68
+#define LAN9303_MANUAL_FC_2 0x69
+#define LAN9303_MANUAL_FC_0 0x6a
+#define LAN9303_SWITCH_CSR_DATA 0x6b
+#define LAN9303_SWITCH_CSR_CMD 0x6c
+#define LAN9303_SWITCH_CSR_CMD_BUSY BIT(31)
+#define LAN9303_SWITCH_CSR_CMD_RW BIT(30)
+#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_SW_DEV_ID 0x0000
+#define LAN9303_SW_RESET 0x0001
+#define LAN9303_SW_RESET_RESET BIT(0)
+#define LAN9303_SW_IMR 0x0004
+#define LAN9303_SW_IPR 0x0005
+#define LAN9303_MAC_VER_ID_0 0x0400
+#define LAN9303_MAC_RX_CFG_0 0x0401
+# define LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES BIT(1)
+# define LAN9303_MAC_RX_CFG_X_RX_ENABLE BIT(0)
+#define LAN9303_MAC_RX_UNDSZE_CNT_0 0x0410
+#define LAN9303_MAC_RX_64_CNT_0 0x0411
+#define LAN9303_MAC_RX_127_CNT_0 0x0412
+#define LAN9303_MAC_RX_255_CNT_0 0x413
+#define LAN9303_MAC_RX_511_CNT_0 0x0414
+#define LAN9303_MAC_RX_1023_CNT_0 0x0415
+#define LAN9303_MAC_RX_MAX_CNT_0 0x0416
+#define LAN9303_MAC_RX_OVRSZE_CNT_0 0x0417
+#define LAN9303_MAC_RX_PKTOK_CNT_0 0x0418
+#define LAN9303_MAC_RX_CRCERR_CNT_0 0x0419
+#define LAN9303_MAC_RX_MULCST_CNT_0 0x041a
+#define LAN9303_MAC_RX_BRDCST_CNT_0 0x041b
+#define LAN9303_MAC_RX_PAUSE_CNT_0 0x041c
+#define LAN9303_MAC_RX_FRAG_CNT_0 0x041d
+#define LAN9303_MAC_RX_JABB_CNT_0 0x041e
+#define LAN9303_MAC_RX_ALIGN_CNT_0 0x041f
+#define LAN9303_MAC_RX_PKTLEN_CNT_0 0x0420
+#define LAN9303_MAC_RX_GOODPKTLEN_CNT_0 0x0421
+#define LAN9303_MAC_RX_SYMBL_CNT_0 0x0422
+#define LAN9303_MAC_RX_CTLFRM_CNT_0 0x0423
+
+#define LAN9303_MAC_TX_CFG_0 0x0440
+# define LAN9303_MAC_TX_CFG_X_TX_IFG_CONFIG_DEFAULT (21 << 2)
+# define LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE BIT(1)
+# define LAN9303_MAC_TX_CFG_X_TX_ENABLE BIT(0)
+#define LAN9303_MAC_TX_DEFER_CNT_0 0x0451
+#define LAN9303_MAC_TX_PAUSE_CNT_0 0x0452
+#define LAN9303_MAC_TX_PKTOK_CNT_0 0x0453
+#define LAN9303_MAC_TX_64_CNT_0 0x0454
+#define LAN9303_MAC_TX_127_CNT_0 0x0455
+#define LAN9303_MAC_TX_255_CNT_0 0x0456
+#define LAN9303_MAC_TX_511_CNT_0 0x0457
+#define LAN9303_MAC_TX_1023_CNT_0 0x0458
+#define LAN9303_MAC_TX_MAX_CNT_0 0x0459
+#define LAN9303_MAC_TX_UNDSZE_CNT_0 0x045a
+#define LAN9303_MAC_TX_PKTLEN_CNT_0 0x045c
+#define LAN9303_MAC_TX_BRDCST_CNT_0 0x045d
+#define LAN9303_MAC_TX_MULCST_CNT_0 0x045e
+#define LAN9303_MAC_TX_LATECOL_0 0x045f
+#define LAN9303_MAC_TX_EXCOL_CNT_0 0x0460
+#define LAN9303_MAC_TX_SNGLECOL_CNT_0 0x0461
+#define LAN9303_MAC_TX_MULTICOL_CNT_0 0x0462
+#define LAN9303_MAC_TX_TOTALCOL_CNT_0 0x0463
+
+#define LAN9303_MAC_VER_ID_1 0x0800
+#define LAN9303_MAC_RX_CFG_1 0x0801
+#define LAN9303_MAC_TX_CFG_1 0x0840
+#define LAN9303_MAC_VER_ID_2 0x0c00
+#define LAN9303_MAC_RX_CFG_2 0x0c01
+#define LAN9303_MAC_TX_CFG_2 0x0c40
+#define LAN9303_SWE_ALR_CMD 0x1800
+#define LAN9303_SWE_VLAN_CMD 0x180b
+# define LAN9303_SWE_VLAN_CMD_RNW BIT(5)
+# define LAN9303_SWE_VLAN_CMD_PVIDNVLAN BIT(4)
+#define LAN9303_SWE_VLAN_WR_DATA 0x180c
+#define LAN9303_SWE_VLAN_RD_DATA 0x180e
+# define LAN9303_SWE_VLAN_MEMBER_PORT2 BIT(17)
+# define LAN9303_SWE_VLAN_UNTAG_PORT2 BIT(16)
+# define LAN9303_SWE_VLAN_MEMBER_PORT1 BIT(15)
+# define LAN9303_SWE_VLAN_UNTAG_PORT1 BIT(14)
+# define LAN9303_SWE_VLAN_MEMBER_PORT0 BIT(13)
+# 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_PORT_STATE 0x1843
+# define LAN9303_SWE_PORT_STATE_FORWARDING_PORT2 (0)
+# define LAN9303_SWE_PORT_STATE_LEARNING_PORT2 BIT(5)
+# define LAN9303_SWE_PORT_STATE_BLOCKING_PORT2 BIT(4)
+# define LAN9303_SWE_PORT_STATE_FORWARDING_PORT1 (0)
+# define LAN9303_SWE_PORT_STATE_LEARNING_PORT1 BIT(3)
+# define LAN9303_SWE_PORT_STATE_BLOCKING_PORT1 BIT(2)
+# 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_MIRROR 0x1846
+# define LAN9303_SWE_PORT_MIRROR_SNIFF_ALL BIT(8)
+# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT2 BIT(7)
+# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT1 BIT(6)
+# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT0 BIT(5)
+# define LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT2 BIT(4)
+# define LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT1 BIT(3)
+# 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_INGRESS_PORT_TYPE 0x1847
+#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))
+# define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT1 (BIT(9) | BIT(8))
+# define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0 (BIT(1) | BIT(0))
+
+#define LAN9303_PORT_0_OFFSET 0x400
+#define LAN9303_PORT_1_OFFSET 0x800
+#define LAN9303_PORT_2_OFFSET 0xc00
+
+/* the built-in PHYs are of type LAN911X */
+#define MII_LAN911X_SPECIAL_MODES 0x12
+#define MII_LAN911X_SPECIAL_CONTROL_STATUS 0x1f
+
+static const struct regmap_range lan9303_valid_regs[] = {
+ regmap_reg_range(0x14, 0x17), /* misc, interrupt */
+ regmap_reg_range(0x19, 0x19), /* endian test */
+ regmap_reg_range(0x1d, 0x1d), /* hardware config */
+ regmap_reg_range(0x23, 0x24), /* general purpose timer */
+ regmap_reg_range(0x27, 0x27), /* counter */
+ regmap_reg_range(0x29, 0x2a), /* PMI index regs */
+ regmap_reg_range(0x68, 0x6a), /* flow control */
+ regmap_reg_range(0x6b, 0x6c), /* switch fabric indirect regs */
+ regmap_reg_range(0x6d, 0x6f), /* misc */
+ regmap_reg_range(0x70, 0x77), /* virtual phy */
+ regmap_reg_range(0x78, 0x7a), /* GPIO */
+ regmap_reg_range(0x7c, 0x7e), /* MAC & reset */
+ regmap_reg_range(0x80, 0xb7), /* switch fabric direct regs (wr only) */
+};
+
+static const struct regmap_range lan9303_reserved_ranges[] = {
+ regmap_reg_range(0x00, 0x13),
+ regmap_reg_range(0x18, 0x18),
+ regmap_reg_range(0x1a, 0x1c),
+ regmap_reg_range(0x1e, 0x22),
+ regmap_reg_range(0x25, 0x26),
+ regmap_reg_range(0x28, 0x28),
+ regmap_reg_range(0x2b, 0x67),
+ regmap_reg_range(0x7b, 0x7b),
+ regmap_reg_range(0x7f, 0x7f),
+ regmap_reg_range(0xb8, 0xff),
+};
+
+const struct regmap_access_table lan9303_register_set = {
+ .yes_ranges = lan9303_valid_regs,
+ .n_yes_ranges = ARRAY_SIZE(lan9303_valid_regs),
+ .no_ranges = lan9303_reserved_ranges,
+ .n_no_ranges = ARRAY_SIZE(lan9303_reserved_ranges),
+};
+EXPORT_SYMBOL(lan9303_register_set);
+
+static int lan9303_read(struct regmap *regmap, unsigned int offset, u32 *reg)
+{
+ int ret, i;
+
+ /* we can lose arbitration for the I2C case, because the device
+ * tries to detect and read an external EEPROM after reset and acts as
+ * a master on the shared I2C bus itself. This conflicts with our
+ * attempts to access the device as a slave at the same moment.
+ */
+ for (i = 0; i < 5; i++) {
+ ret = regmap_read(regmap, offset, reg);
+ if (!ret)
+ return 0;
+ if (ret != -EAGAIN)
+ break;
+ msleep(500);
+ }
+
+ return -EIO;
+}
+
+static int lan9303_virt_phy_reg_read(struct lan9303 *chip, int regnum)
+{
+ int ret;
+ u32 val;
+
+ if (regnum > MII_EXPANSION)
+ return -EINVAL;
+
+ ret = lan9303_read(chip->regmap, LAN9303_VIRT_PHY_BASE + regnum, &val);
+ if (ret)
+ return ret;
+
+ return val & 0xffff;
+}
+
+static int lan9303_virt_phy_reg_write(struct lan9303 *chip, int regnum, u16 val)
+{
+ if (regnum > MII_EXPANSION)
+ return -EINVAL;
+
+ return regmap_write(chip->regmap, LAN9303_VIRT_PHY_BASE + regnum, val);
+}
+
+static int lan9303_port_phy_reg_wait_for_completion(struct lan9303 *chip)
+{
+ int ret, i;
+ u32 reg;
+
+ for (i = 0; i < 25; i++) {
+ ret = lan9303_read(chip->regmap, LAN9303_PMI_ACCESS, &reg);
+ if (ret) {
+ dev_err(chip->dev,
+ "Failed to read pmi access status: %d\n", ret);
+ return ret;
+ }
+ if (!(reg & LAN9303_PMI_ACCESS_MII_BUSY))
+ return 0;
+ msleep(1);
+ }
+
+ return -EIO;
+}
+
+static int lan9303_port_phy_reg_read(struct lan9303 *chip, int addr, int regnum)
+{
+ int ret;
+ u32 val;
+
+ val = LAN9303_PMI_ACCESS_PHY_ADDR(addr);
+ val |= LAN9303_PMI_ACCESS_MIIRINDA(regnum);
+
+ mutex_lock(&chip->indirect_mutex);
+
+ ret = lan9303_port_phy_reg_wait_for_completion(chip);
+ if (ret)
+ goto on_error;
+
+ /* start the MII read cycle */
+ ret = regmap_write(chip->regmap, LAN9303_PMI_ACCESS, val);
+ if (ret)
+ goto on_error;
+
+ ret = lan9303_port_phy_reg_wait_for_completion(chip);
+ if (ret)
+ goto on_error;
+
+ /* read the result of this operation */
+ ret = lan9303_read(chip->regmap, LAN9303_PMI_DATA, &val);
+ if (ret)
+ goto on_error;
+
+ mutex_unlock(&chip->indirect_mutex);
+
+ return val & 0xffff;
+
+on_error:
+ mutex_unlock(&chip->indirect_mutex);
+ return ret;
+}
+
+static int lan9303_phy_reg_write(struct lan9303 *chip, int addr, int regnum,
+ unsigned int val)
+{
+ int ret;
+ u32 reg;
+
+ reg = LAN9303_PMI_ACCESS_PHY_ADDR(addr);
+ reg |= LAN9303_PMI_ACCESS_MIIRINDA(regnum);
+ reg |= LAN9303_PMI_ACCESS_MII_WRITE;
+
+ mutex_lock(&chip->indirect_mutex);
+
+ ret = lan9303_port_phy_reg_wait_for_completion(chip);
+ if (ret)
+ goto on_error;
+
+ /* write the data first... */
+ ret = regmap_write(chip->regmap, LAN9303_PMI_DATA, val);
+ if (ret)
+ goto on_error;
+
+ /* ...then start the MII write cycle */
+ ret = regmap_write(chip->regmap, LAN9303_PMI_ACCESS, reg);
+
+on_error:
+ mutex_unlock(&chip->indirect_mutex);
+ return ret;
+}
+
+static int lan9303_switch_wait_for_completion(struct lan9303 *chip)
+{
+ int ret, i;
+ u32 reg;
+
+ for (i = 0; i < 25; i++) {
+ ret = lan9303_read(chip->regmap, LAN9303_SWITCH_CSR_CMD, &reg);
+ if (ret) {
+ dev_err(chip->dev,
+ "Failed to read csr command status: %d\n", ret);
+ return ret;
+ }
+ if (!(reg & LAN9303_SWITCH_CSR_CMD_BUSY))
+ return 0;
+ msleep(1);
+ }
+
+ return -EIO;
+}
+
+static int lan9303_write_switch_reg(struct lan9303 *chip, u16 regnum, u32 val)
+{
+ u32 reg;
+ int ret;
+
+ reg = regnum;
+ reg |= LAN9303_SWITCH_CSR_CMD_LANES;
+ reg |= LAN9303_SWITCH_CSR_CMD_BUSY;
+
+ mutex_lock(&chip->indirect_mutex);
+
+ ret = lan9303_switch_wait_for_completion(chip);
+ if (ret)
+ goto on_error;
+
+ ret = regmap_write(chip->regmap, LAN9303_SWITCH_CSR_DATA, val);
+ if (ret) {
+ dev_err(chip->dev, "Failed to write csr data reg: %d\n", ret);
+ goto on_error;
+ }
+
+ /* trigger write */
+ ret = regmap_write(chip->regmap, LAN9303_SWITCH_CSR_CMD, reg);
+ if (ret)
+ dev_err(chip->dev, "Failed to write csr command reg: %d\n",
+ ret);
+
+on_error:
+ mutex_unlock(&chip->indirect_mutex);
+ return ret;
+}
+
+static int lan9303_read_switch_reg(struct lan9303 *chip, u16 regnum, u32 *val)
+{
+ u32 reg;
+ int ret;
+
+ reg = regnum;
+ reg |= LAN9303_SWITCH_CSR_CMD_LANES;
+ reg |= LAN9303_SWITCH_CSR_CMD_RW;
+ reg |= LAN9303_SWITCH_CSR_CMD_BUSY;
+
+ mutex_lock(&chip->indirect_mutex);
+
+ ret = lan9303_switch_wait_for_completion(chip);
+ if (ret)
+ goto on_error;
+
+ /* trigger read */
+ ret = regmap_write(chip->regmap, LAN9303_SWITCH_CSR_CMD, reg);
+ if (ret) {
+ dev_err(chip->dev, "Failed to write csr command reg: %d\n",
+ ret);
+ goto on_error;
+ }
+
+ ret = lan9303_switch_wait_for_completion(chip);
+ if (ret)
+ goto on_error;
+
+ ret = lan9303_read(chip->regmap, LAN9303_SWITCH_CSR_DATA, val);
+ if (ret)
+ dev_err(chip->dev, "Failed to read csr data reg: %d\n", ret);
+on_error:
+ mutex_unlock(&chip->indirect_mutex);
+ return ret;
+}
+
+static int lan9303_detect_phy_setup(struct lan9303 *chip)
+{
+ int reg;
+
+ /* depending on the 'phy_addr_sel_strap' setting, the three phys are
+ * using IDs 0-1-2 or IDs 1-2-3. We cannot read back the
+ * 'phy_addr_sel_strap' setting directly, so we need a test, which
+ * configuration is active:
+ * Special reg 18 of phy 3 reads as 0x0000, if 'phy_addr_sel_strap' is 0
+ * and the IDs are 0-1-2, else it contains something different from
+ * 0x0000, which means 'phy_addr_sel_strap' is 1 and the IDs are 1-2-3.
+ */
+ reg = lan9303_port_phy_reg_read(chip, 3, MII_LAN911X_SPECIAL_MODES);
+ if (reg < 0) {
+ dev_err(chip->dev, "Failed to detect phy config: %d\n", reg);
+ return reg;
+ }
+
+ if (reg != 0)
+ chip->phy_addr_sel_strap = 1;
+ else
+ chip->phy_addr_sel_strap = 0;
+
+ dev_dbg(chip->dev, "Phy setup '%s' detected\n",
+ chip->phy_addr_sel_strap ? "1-2-3" : "0-1-2");
+
+ return 0;
+}
+
+#define LAN9303_MAC_RX_CFG_OFFS (LAN9303_MAC_RX_CFG_0 - LAN9303_PORT_0_OFFSET)
+#define LAN9303_MAC_TX_CFG_OFFS (LAN9303_MAC_TX_CFG_0 - LAN9303_PORT_0_OFFSET)
+
+static int lan9303_disable_packet_processing(struct lan9303 *chip,
+ unsigned int port)
+{
+ int ret;
+
+ /* disable RX, but keep register reset default values else */
+ ret = lan9303_write_switch_reg(chip, LAN9303_MAC_RX_CFG_OFFS + port,
+ LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES);
+ if (ret)
+ return ret;
+
+ /* disable TX, but keep register reset default values else */
+ return lan9303_write_switch_reg(chip, LAN9303_MAC_TX_CFG_OFFS + port,
+ LAN9303_MAC_TX_CFG_X_TX_IFG_CONFIG_DEFAULT |
+ LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE);
+}
+
+static int lan9303_enable_packet_processing(struct lan9303 *chip,
+ unsigned int port)
+{
+ int ret;
+
+ /* enable RX and keep register reset default values else */
+ ret = lan9303_write_switch_reg(chip, LAN9303_MAC_RX_CFG_OFFS + port,
+ LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES |
+ LAN9303_MAC_RX_CFG_X_RX_ENABLE);
+ if (ret)
+ return ret;
+
+ /* enable TX and keep register reset default values else */
+ return lan9303_write_switch_reg(chip, LAN9303_MAC_TX_CFG_OFFS + port,
+ LAN9303_MAC_TX_CFG_X_TX_IFG_CONFIG_DEFAULT |
+ LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE |
+ LAN9303_MAC_TX_CFG_X_TX_ENABLE);
+}
+
+/* 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;
+
+ ret = lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_MIRROR,
+ LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT0 |
+ LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT1 |
+ LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT2 |
+ LAN9303_SWE_PORT_MIRROR_ENABLE_RX_MIRRORING |
+ LAN9303_SWE_PORT_MIRROR_SNIFF_ALL);
+ 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 |
+ LAN9303_SWE_PORT_STATE_BLOCKING_PORT1 |
+ LAN9303_SWE_PORT_STATE_BLOCKING_PORT2);
+}
+
+static int lan9303_handle_reset(struct lan9303 *chip)
+{
+ if (!chip->reset_gpio)
+ return 0;
+
+ if (chip->reset_duration != 0)
+ msleep(chip->reset_duration);
+
+ /* release (deassert) reset and activate the device */
+ gpiod_set_value_cansleep(chip->reset_gpio, 0);
+
+ return 0;
+}
+
+/* stop processing packets for all ports */
+static int lan9303_disable_processing(struct lan9303 *chip)
+{
+ int ret;
+
+ ret = lan9303_disable_packet_processing(chip, LAN9303_PORT_0_OFFSET);
+ if (ret)
+ return ret;
+ ret = lan9303_disable_packet_processing(chip, LAN9303_PORT_1_OFFSET);
+ if (ret)
+ return ret;
+ return lan9303_disable_packet_processing(chip, LAN9303_PORT_2_OFFSET);
+}
+
+static int lan9303_check_device(struct lan9303 *chip)
+{
+ int ret;
+ u32 reg;
+
+ ret = lan9303_read(chip->regmap, LAN9303_CHIP_REV, &reg);
+ if (ret) {
+ dev_err(chip->dev, "failed to read chip revision register: %d\n",
+ ret);
+ if (!chip->reset_gpio) {
+ dev_dbg(chip->dev,
+ "hint: maybe failed due to missing reset GPIO\n");
+ }
+ return ret;
+ }
+
+ if ((reg >> 16) != LAN9303_CHIP_ID) {
+ dev_err(chip->dev, "expecting LAN9303 chip, but found: %X\n",
+ reg >> 16);
+ return ret;
+ }
+
+ /* The default state of the LAN9303 device is to forward packets between
+ * all ports (if not configured differently by an external EEPROM).
+ * The initial state of a DSA device must be forwarding packets only
+ * between the external and the internal ports and no forwarding
+ * between the external ports. In preparation we stop packet handling
+ * at all for now until the LAN9303 device is re-programmed accordingly.
+ */
+ ret = lan9303_disable_processing(chip);
+ if (ret)
+ dev_warn(chip->dev, "failed to disable switching %d\n", ret);
+
+ dev_info(chip->dev, "Found LAN9303 rev. %u\n", reg & 0xffff);
+
+ ret = lan9303_detect_phy_setup(chip);
+ if (ret) {
+ dev_err(chip->dev,
+ "failed to discover phy bootstrap setup: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* ---------------------------- DSA -----------------------------------*/
+
+static enum dsa_tag_protocol lan9303_get_tag_protocol(struct dsa_switch *ds)
+{
+ return DSA_TAG_PROTO_LAN9303;
+}
+
+static int lan9303_setup(struct dsa_switch *ds)
+{
+ struct lan9303 *chip = ds->priv;
+ int ret;
+
+ /* Make sure that port 0 is the cpu port */
+ if (!dsa_is_cpu_port(ds, 0)) {
+ dev_err(chip->dev, "port 0 is not the CPU port\n");
+ return -EINVAL;
+ }
+
+ ret = lan9303_separate_ports(chip);
+ if (ret)
+ dev_err(chip->dev, "failed to separate ports %d\n", ret);
+
+ ret = lan9303_enable_packet_processing(chip, LAN9303_PORT_0_OFFSET);
+ if (ret)
+ dev_err(chip->dev, "failed to re-enable switching %d\n", ret);
+
+ return 0;
+}
+
+struct lan9303_mib_desc {
+ unsigned int offset; /* offset of first MAC */
+ const char *name;
+};
+
+static const struct lan9303_mib_desc lan9303_mib[] = {
+ { .offset = LAN9303_MAC_RX_BRDCST_CNT_0, .name = "RxBroad", },
+ { .offset = LAN9303_MAC_RX_PAUSE_CNT_0, .name = "RxPause", },
+ { .offset = LAN9303_MAC_RX_MULCST_CNT_0, .name = "RxMulti", },
+ { .offset = LAN9303_MAC_RX_PKTOK_CNT_0, .name = "RxOk", },
+ { .offset = LAN9303_MAC_RX_CRCERR_CNT_0, .name = "RxCrcErr", },
+ { .offset = LAN9303_MAC_RX_ALIGN_CNT_0, .name = "RxAlignErr", },
+ { .offset = LAN9303_MAC_RX_JABB_CNT_0, .name = "RxJabber", },
+ { .offset = LAN9303_MAC_RX_FRAG_CNT_0, .name = "RxFragment", },
+ { .offset = LAN9303_MAC_RX_64_CNT_0, .name = "Rx64Byte", },
+ { .offset = LAN9303_MAC_RX_127_CNT_0, .name = "Rx128Byte", },
+ { .offset = LAN9303_MAC_RX_255_CNT_0, .name = "Rx256Byte", },
+ { .offset = LAN9303_MAC_RX_511_CNT_0, .name = "Rx512Byte", },
+ { .offset = LAN9303_MAC_RX_1023_CNT_0, .name = "Rx1024Byte", },
+ { .offset = LAN9303_MAC_RX_MAX_CNT_0, .name = "RxMaxByte", },
+ { .offset = LAN9303_MAC_RX_PKTLEN_CNT_0, .name = "RxByteCnt", },
+ { .offset = LAN9303_MAC_RX_SYMBL_CNT_0, .name = "RxSymbolCnt", },
+ { .offset = LAN9303_MAC_RX_CTLFRM_CNT_0, .name = "RxCfs", },
+ { .offset = LAN9303_MAC_RX_OVRSZE_CNT_0, .name = "RxOverFlow", },
+ { .offset = LAN9303_MAC_TX_UNDSZE_CNT_0, .name = "TxShort", },
+ { .offset = LAN9303_MAC_TX_BRDCST_CNT_0, .name = "TxBroad", },
+ { .offset = LAN9303_MAC_TX_PAUSE_CNT_0, .name = "TxPause", },
+ { .offset = LAN9303_MAC_TX_MULCST_CNT_0, .name = "TxMulti", },
+ { .offset = LAN9303_MAC_RX_UNDSZE_CNT_0, .name = "TxUnderRun", },
+ { .offset = LAN9303_MAC_TX_64_CNT_0, .name = "Tx64Byte", },
+ { .offset = LAN9303_MAC_TX_127_CNT_0, .name = "Tx128Byte", },
+ { .offset = LAN9303_MAC_TX_255_CNT_0, .name = "Tx256Byte", },
+ { .offset = LAN9303_MAC_TX_511_CNT_0, .name = "Tx512Byte", },
+ { .offset = LAN9303_MAC_TX_1023_CNT_0, .name = "Tx1024Byte", },
+ { .offset = LAN9303_MAC_TX_MAX_CNT_0, .name = "TxMaxByte", },
+ { .offset = LAN9303_MAC_TX_PKTLEN_CNT_0, .name = "TxByteCnt", },
+ { .offset = LAN9303_MAC_TX_PKTOK_CNT_0, .name = "TxOk", },
+ { .offset = LAN9303_MAC_TX_TOTALCOL_CNT_0, .name = "TxCollision", },
+ { .offset = LAN9303_MAC_TX_MULTICOL_CNT_0, .name = "TxMultiCol", },
+ { .offset = LAN9303_MAC_TX_SNGLECOL_CNT_0, .name = "TxSingleCol", },
+ { .offset = LAN9303_MAC_TX_EXCOL_CNT_0, .name = "TxExcCol", },
+ { .offset = LAN9303_MAC_TX_DEFER_CNT_0, .name = "TxDefer", },
+ { .offset = LAN9303_MAC_TX_LATECOL_0, .name = "TxLateCol", },
+};
+
+static void lan9303_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+{
+ unsigned int u;
+
+ for (u = 0; u < ARRAY_SIZE(lan9303_mib); u++) {
+ strncpy(data + u * ETH_GSTRING_LEN, lan9303_mib[u].name,
+ ETH_GSTRING_LEN);
+ }
+}
+
+static void lan9303_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *data)
+{
+ struct lan9303 *chip = ds->priv;
+ u32 reg;
+ unsigned int u, poff;
+ int ret;
+
+ poff = port * 0x400;
+
+ for (u = 0; u < ARRAY_SIZE(lan9303_mib); u++) {
+ ret = lan9303_read_switch_reg(chip,
+ lan9303_mib[u].offset + poff,
+ &reg);
+ if (ret)
+ dev_warn(chip->dev, "Reading status reg %u failed\n",
+ lan9303_mib[u].offset + poff);
+ data[u] = reg;
+ }
+}
+
+static int lan9303_get_sset_count(struct dsa_switch *ds)
+{
+ return ARRAY_SIZE(lan9303_mib);
+}
+
+static int lan9303_phy_read(struct dsa_switch *ds, int phy, int regnum)
+{
+ struct lan9303 *chip = ds->priv;
+ int phy_base = chip->phy_addr_sel_strap;
+
+ if (phy == phy_base)
+ return lan9303_virt_phy_reg_read(chip, regnum);
+ if (phy > phy_base + 2)
+ return -ENODEV;
+
+ return lan9303_port_phy_reg_read(chip, phy, regnum);
+}
+
+static int lan9303_phy_write(struct dsa_switch *ds, int phy, int regnum,
+ u16 val)
+{
+ struct lan9303 *chip = ds->priv;
+ int phy_base = chip->phy_addr_sel_strap;
+
+ if (phy == phy_base)
+ return lan9303_virt_phy_reg_write(chip, regnum, val);
+ if (phy > phy_base + 2)
+ return -ENODEV;
+
+ return lan9303_phy_reg_write(chip, phy, regnum, val);
+}
+
+static int lan9303_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct lan9303 *chip = ds->priv;
+
+ /* enable internal packet processing */
+ switch (port) {
+ case 1:
+ return lan9303_enable_packet_processing(chip,
+ LAN9303_PORT_1_OFFSET);
+ case 2:
+ return lan9303_enable_packet_processing(chip,
+ LAN9303_PORT_2_OFFSET);
+ default:
+ dev_dbg(chip->dev,
+ "Error: request to power up invalid port %d\n", port);
+ }
+
+ return -ENODEV;
+}
+
+static void lan9303_port_disable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct lan9303 *chip = ds->priv;
+
+ /* disable internal packet processing */
+ switch (port) {
+ case 1:
+ lan9303_disable_packet_processing(chip, LAN9303_PORT_1_OFFSET);
+ lan9303_phy_reg_write(chip, chip->phy_addr_sel_strap + 1,
+ MII_BMCR, BMCR_PDOWN);
+ break;
+ case 2:
+ lan9303_disable_packet_processing(chip, LAN9303_PORT_2_OFFSET);
+ lan9303_phy_reg_write(chip, chip->phy_addr_sel_strap + 2,
+ MII_BMCR, BMCR_PDOWN);
+ break;
+ default:
+ dev_dbg(chip->dev,
+ "Error: request to power down invalid port %d\n", port);
+ }
+}
+
+static struct dsa_switch_ops lan9303_switch_ops = {
+ .get_tag_protocol = lan9303_get_tag_protocol,
+ .setup = lan9303_setup,
+ .get_strings = lan9303_get_strings,
+ .phy_read = lan9303_phy_read,
+ .phy_write = lan9303_phy_write,
+ .get_ethtool_stats = lan9303_get_ethtool_stats,
+ .get_sset_count = lan9303_get_sset_count,
+ .port_enable = lan9303_port_enable,
+ .port_disable = lan9303_port_disable,
+};
+
+static int lan9303_register_switch(struct lan9303 *chip)
+{
+ chip->ds = dsa_switch_alloc(chip->dev, DSA_MAX_PORTS);
+ if (!chip->ds)
+ return -ENOMEM;
+
+ chip->ds->priv = chip;
+ chip->ds->ops = &lan9303_switch_ops;
+ chip->ds->phys_mii_mask = chip->phy_addr_sel_strap ? 0xe : 0x7;
+
+ return dsa_register_switch(chip->ds);
+}
+
+static void lan9303_probe_reset_gpio(struct lan9303 *chip,
+ struct device_node *np)
+{
+ chip->reset_gpio = devm_gpiod_get_optional(chip->dev, "reset",
+ GPIOD_OUT_LOW);
+
+ if (!chip->reset_gpio) {
+ dev_dbg(chip->dev, "No reset GPIO defined\n");
+ return;
+ }
+
+ chip->reset_duration = 200;
+
+ if (np) {
+ of_property_read_u32(np, "reset-duration",
+ &chip->reset_duration);
+ } else {
+ dev_dbg(chip->dev, "reset duration defaults to 200 ms\n");
+ }
+
+ /* A sane reset duration should not be longer than 1s */
+ if (chip->reset_duration > 1000)
+ chip->reset_duration = 1000;
+}
+
+int lan9303_probe(struct lan9303 *chip, struct device_node *np)
+{
+ int ret;
+
+ mutex_init(&chip->indirect_mutex);
+
+ lan9303_probe_reset_gpio(chip, np);
+
+ ret = lan9303_handle_reset(chip);
+ if (ret)
+ return ret;
+
+ ret = lan9303_check_device(chip);
+ if (ret)
+ return ret;
+
+ ret = lan9303_register_switch(chip);
+ if (ret) {
+ dev_dbg(chip->dev, "Failed to register switch: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(lan9303_probe);
+
+int lan9303_remove(struct lan9303 *chip)
+{
+ int rc;
+
+ rc = lan9303_disable_processing(chip);
+ if (rc != 0)
+ dev_warn(chip->dev, "shutting down failed\n");
+
+ dsa_unregister_switch(chip->ds);
+
+ /* assert reset to the whole device to prevent it from doing anything */
+ gpiod_set_value_cansleep(chip->reset_gpio, 1);
+ gpiod_unexport(chip->reset_gpio);
+
+ return 0;
+}
+EXPORT_SYMBOL(lan9303_remove);
+
+MODULE_AUTHOR("Juergen Borleis <[email protected]>");
+MODULE_DESCRIPTION("Core driver for SMSC/Microchip LAN9303 three port ethernet switch");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/lan9303.h b/drivers/net/dsa/lan9303.h
new file mode 100644
index 000000000000..d1512dad2d90
--- /dev/null
+++ b/drivers/net/dsa/lan9303.h
@@ -0,0 +1,19 @@
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include <net/dsa.h>
+
+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 */
+};
+
+extern const struct regmap_access_table lan9303_register_set;
+
+int lan9303_probe(struct lan9303 *chip, struct device_node *np);
+int lan9303_remove(struct lan9303 *chip);
diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c
new file mode 100644
index 000000000000..ab3ce0da5071
--- /dev/null
+++ b/drivers/net/dsa/lan9303_i2c.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 Pengutronix, Juergen Borleis <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+
+#include "lan9303.h"
+
+struct lan9303_i2c {
+ struct i2c_client *device;
+ struct lan9303 chip;
+};
+
+static const struct regmap_config lan9303_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 1,
+ .can_multi_write = true,
+ .max_register = 0x0ff, /* address bits 0..1 are not used */
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+
+ .volatile_table = &lan9303_register_set,
+ .wr_table = &lan9303_register_set,
+ .rd_table = &lan9303_register_set,
+
+ .cache_type = REGCACHE_NONE,
+};
+
+static int lan9303_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lan9303_i2c *sw_dev;
+ int ret;
+
+ sw_dev = devm_kzalloc(&client->dev, sizeof(struct lan9303_i2c),
+ GFP_KERNEL);
+ if (!sw_dev)
+ return -ENOMEM;
+
+ sw_dev->chip.regmap = devm_regmap_init_i2c(client,
+ &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",
+ ret);
+ return ret;
+ }
+
+ /* link forward and backward */
+ sw_dev->device = client;
+ i2c_set_clientdata(client, sw_dev);
+ sw_dev->chip.dev = &client->dev;
+
+ ret = lan9303_probe(&sw_dev->chip, client->dev.of_node);
+ if (ret != 0)
+ return ret;
+
+ dev_info(&client->dev, "LAN9303 I2C driver loaded successfully\n");
+
+ return 0;
+}
+
+static int lan9303_i2c_remove(struct i2c_client *client)
+{
+ struct lan9303_i2c *sw_dev;
+
+ sw_dev = i2c_get_clientdata(client);
+ if (!sw_dev)
+ return -ENODEV;
+
+ return lan9303_remove(&sw_dev->chip);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct i2c_device_id lan9303_i2c_id[] = {
+ { "lan9303", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, lan9303_i2c_id);
+
+static const struct of_device_id lan9303_i2c_of_match[] = {
+ { .compatible = "smsc,lan9303-i2c", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, lan9303_i2c_of_match);
+
+static struct i2c_driver lan9303_i2c_driver = {
+ .driver = {
+ .name = "LAN9303_I2C",
+ .of_match_table = of_match_ptr(lan9303_i2c_of_match),
+ },
+ .probe = lan9303_i2c_probe,
+ .remove = lan9303_i2c_remove,
+ .id_table = lan9303_i2c_id,
+};
+module_i2c_driver(lan9303_i2c_driver);
+
+MODULE_AUTHOR("Juergen Borleis <[email protected]>");
+MODULE_DESCRIPTION("Driver for SMSC/Microchip LAN9303 three port ethernet switch in I2C managed mode");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c
new file mode 100644
index 000000000000..93c36c0541cf
--- /dev/null
+++ b/drivers/net/dsa/lan9303_mdio.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 Pengutronix, Juergen Borleis <[email protected]>
+ *
+ * Partially based on a patch from
+ * Copyright (c) 2014 Stefan Roese <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+
+#include "lan9303.h"
+
+/* Generate phy-addr and -reg from the input address */
+#define PHY_ADDR(x) ((((x) >> 6) + 0x10) & 0x1f)
+#define PHY_REG(x) (((x) >> 1) & 0x1f)
+
+struct lan9303_mdio {
+ struct mdio_device *device;
+ struct lan9303 chip;
+};
+
+static void lan9303_mdio_real_write(struct mdio_device *mdio, int reg, u16 val)
+{
+ mdio->bus->write(mdio->bus, PHY_ADDR(reg), PHY_REG(reg), val);
+}
+
+static int lan9303_mdio_write(void *ctx, uint32_t reg, uint32_t val)
+{
+ struct lan9303_mdio *sw_dev = (struct lan9303_mdio *)ctx;
+
+ mutex_lock(&sw_dev->device->bus->mdio_lock);
+ lan9303_mdio_real_write(sw_dev->device, reg, val & 0xffff);
+ lan9303_mdio_real_write(sw_dev->device, reg + 2, (val >> 16) & 0xffff);
+ mutex_unlock(&sw_dev->device->bus->mdio_lock);
+
+ return 0;
+}
+
+static u16 lan9303_mdio_real_read(struct mdio_device *mdio, int reg)
+{
+ return mdio->bus->read(mdio->bus, PHY_ADDR(reg), PHY_REG(reg));
+}
+
+static int lan9303_mdio_read(void *ctx, uint32_t reg, uint32_t *val)
+{
+ struct lan9303_mdio *sw_dev = (struct lan9303_mdio *)ctx;
+
+ mutex_lock(&sw_dev->device->bus->mdio_lock);
+ *val = lan9303_mdio_real_read(sw_dev->device, reg);
+ *val |= (lan9303_mdio_real_read(sw_dev->device, reg + 2) << 16);
+ mutex_unlock(&sw_dev->device->bus->mdio_lock);
+
+ return 0;
+}
+
+static const struct regmap_config lan9303_mdio_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 1,
+ .can_multi_write = true,
+ .max_register = 0x0ff, /* address bits 0..1 are not used */
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+
+ .volatile_table = &lan9303_register_set,
+ .wr_table = &lan9303_register_set,
+ .rd_table = &lan9303_register_set,
+
+ .reg_read = lan9303_mdio_read,
+ .reg_write = lan9303_mdio_write,
+
+ .cache_type = REGCACHE_NONE,
+};
+
+static int lan9303_mdio_probe(struct mdio_device *mdiodev)
+{
+ struct lan9303_mdio *sw_dev;
+ int ret;
+
+ sw_dev = devm_kzalloc(&mdiodev->dev, sizeof(struct lan9303_mdio),
+ GFP_KERNEL);
+ if (!sw_dev)
+ return -ENOMEM;
+
+ sw_dev->chip.regmap = devm_regmap_init(&mdiodev->dev, NULL, sw_dev,
+ &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);
+ return ret;
+ }
+
+ /* link forward and backward */
+ sw_dev->device = mdiodev;
+ dev_set_drvdata(&mdiodev->dev, sw_dev);
+ sw_dev->chip.dev = &mdiodev->dev;
+
+ ret = lan9303_probe(&sw_dev->chip, mdiodev->dev.of_node);
+ if (ret != 0)
+ return ret;
+
+ dev_info(&mdiodev->dev, "LAN9303 MDIO driver loaded successfully\n");
+
+ return 0;
+}
+
+static void lan9303_mdio_remove(struct mdio_device *mdiodev)
+{
+ struct lan9303_mdio *sw_dev = dev_get_drvdata(&mdiodev->dev);
+
+ if (!sw_dev)
+ return;
+
+ lan9303_remove(&sw_dev->chip);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct of_device_id lan9303_mdio_of_match[] = {
+ { .compatible = "smsc,lan9303-mdio" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, lan9303_mdio_of_match);
+
+static struct mdio_driver lan9303_mdio_driver = {
+ .mdiodrv.driver = {
+ .name = "LAN9303_MDIO",
+ .of_match_table = of_match_ptr(lan9303_mdio_of_match),
+ },
+ .probe = lan9303_mdio_probe,
+ .remove = lan9303_mdio_remove,
+};
+mdio_module_driver(lan9303_mdio_driver);
+
+MODULE_AUTHOR("Stefan Roese <[email protected]>, Juergen Borleis <[email protected]>");
+MODULE_DESCRIPTION("Driver for SMSC/Microchip LAN9303 three port ethernet switch in MDIO managed mode");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig
new file mode 100644
index 000000000000..a8b8f59099ce
--- /dev/null
+++ b/drivers/net/dsa/microchip/Kconfig
@@ -0,0 +1,12 @@
+menuconfig MICROCHIP_KSZ
+ tristate "Microchip KSZ series switch support"
+ depends on NET_DSA
+ select NET_DSA_TAG_KSZ
+ help
+ This driver adds support for Microchip KSZ switch chips.
+
+config MICROCHIP_KSZ_SPI_DRIVER
+ tristate "KSZ series SPI connected switch driver"
+ depends on MICROCHIP_KSZ && SPI
+ help
+ Select to enable support for registering switches configured through SPI.
diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile
new file mode 100644
index 000000000000..ed335e29fae8
--- /dev/null
+++ b/drivers/net/dsa/microchip/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MICROCHIP_KSZ) += ksz_common.o
+obj-$(CONFIG_MICROCHIP_KSZ_SPI_DRIVER) += ksz_spi.o
diff --git a/drivers/net/dsa/microchip/ksz_9477_reg.h b/drivers/net/dsa/microchip/ksz_9477_reg.h
new file mode 100644
index 000000000000..6aa6752035a1
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_9477_reg.h
@@ -0,0 +1,1676 @@
+/*
+ * Microchip KSZ9477 register definitions
+ *
+ * Copyright (C) 2017
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __KSZ9477_REGS_H
+#define __KSZ9477_REGS_H
+
+#define KS_PRIO_M 0x7
+#define KS_PRIO_S 4
+
+/* 0 - Operation */
+#define REG_CHIP_ID0__1 0x0000
+
+#define REG_CHIP_ID1__1 0x0001
+
+#define FAMILY_ID 0x95
+#define FAMILY_ID_94 0x94
+#define FAMILY_ID_95 0x95
+#define FAMILY_ID_85 0x85
+#define FAMILY_ID_98 0x98
+#define FAMILY_ID_88 0x88
+
+#define REG_CHIP_ID2__1 0x0002
+
+#define CHIP_ID_63 0x63
+#define CHIP_ID_66 0x66
+#define CHIP_ID_67 0x67
+#define CHIP_ID_77 0x77
+#define CHIP_ID_93 0x93
+#define CHIP_ID_96 0x96
+#define CHIP_ID_97 0x97
+
+#define REG_CHIP_ID3__1 0x0003
+
+#define SWITCH_REVISION_M 0x0F
+#define SWITCH_REVISION_S 4
+#define SWITCH_RESET 0x01
+
+#define REG_SW_PME_CTRL 0x0006
+
+#define PME_ENABLE BIT(1)
+#define PME_POLARITY BIT(0)
+
+#define REG_GLOBAL_OPTIONS 0x000F
+
+#define SW_GIGABIT_ABLE BIT(6)
+#define SW_REDUNDANCY_ABLE BIT(5)
+#define SW_AVB_ABLE BIT(4)
+#define SW_9567_RL_5_2 0xC
+#define SW_9477_SL_5_2 0xD
+
+#define SW_9896_GL_5_1 0xB
+#define SW_9896_RL_5_1 0x8
+#define SW_9896_SL_5_1 0x9
+
+#define SW_9895_GL_4_1 0x7
+#define SW_9895_RL_4_1 0x4
+#define SW_9895_SL_4_1 0x5
+
+#define SW_9896_RL_4_2 0x6
+
+#define SW_9893_RL_2_1 0x0
+#define SW_9893_SL_2_1 0x1
+#define SW_9893_GL_2_1 0x3
+
+#define SW_QW_ABLE BIT(5)
+#define SW_9893_RN_2_1 0xC
+
+#define REG_SW_INT_STATUS__4 0x0010
+#define REG_SW_INT_MASK__4 0x0014
+
+#define LUE_INT BIT(31)
+#define TRIG_TS_INT BIT(30)
+#define APB_TIMEOUT_INT BIT(29)
+
+#define SWITCH_INT_MASK (TRIG_TS_INT | APB_TIMEOUT_INT)
+
+#define REG_SW_PORT_INT_STATUS__4 0x0018
+#define REG_SW_PORT_INT_MASK__4 0x001C
+#define REG_SW_PHY_INT_STATUS 0x0020
+#define REG_SW_PHY_INT_ENABLE 0x0024
+
+/* 1 - Global */
+#define REG_SW_GLOBAL_SERIAL_CTRL_0 0x0100
+#define SW_SPARE_REG_2 BIT(7)
+#define SW_SPARE_REG_1 BIT(6)
+#define SW_SPARE_REG_0 BIT(5)
+#define SW_BIG_ENDIAN BIT(4)
+#define SPI_AUTO_EDGE_DETECTION BIT(1)
+#define SPI_CLOCK_OUT_RISING_EDGE BIT(0)
+
+#define REG_SW_GLOBAL_OUTPUT_CTRL__1 0x0103
+#define SW_ENABLE_REFCLKO BIT(1)
+#define SW_REFCLKO_IS_125MHZ BIT(0)
+
+#define REG_SW_IBA__4 0x0104
+
+#define SW_IBA_ENABLE BIT(31)
+#define SW_IBA_DA_MATCH BIT(30)
+#define SW_IBA_INIT BIT(29)
+#define SW_IBA_QID_M 0xF
+#define SW_IBA_QID_S 22
+#define SW_IBA_PORT_M 0x2F
+#define SW_IBA_PORT_S 16
+#define SW_IBA_FRAME_TPID_M 0xFFFF
+
+#define REG_SW_APB_TIMEOUT_ADDR__4 0x0108
+
+#define APB_TIMEOUT_ACKNOWLEDGE BIT(31)
+
+#define REG_SW_IBA_SYNC__1 0x010C
+
+#define REG_SW_IO_STRENGTH__1 0x010D
+#define SW_DRIVE_STRENGTH_M 0x7
+#define SW_DRIVE_STRENGTH_2MA 0
+#define SW_DRIVE_STRENGTH_4MA 1
+#define SW_DRIVE_STRENGTH_8MA 2
+#define SW_DRIVE_STRENGTH_12MA 3
+#define SW_DRIVE_STRENGTH_16MA 4
+#define SW_DRIVE_STRENGTH_20MA 5
+#define SW_DRIVE_STRENGTH_24MA 6
+#define SW_DRIVE_STRENGTH_28MA 7
+#define SW_HI_SPEED_DRIVE_STRENGTH_S 4
+#define SW_LO_SPEED_DRIVE_STRENGTH_S 0
+
+#define REG_SW_IBA_STATUS__4 0x0110
+
+#define SW_IBA_REQ BIT(31)
+#define SW_IBA_RESP BIT(30)
+#define SW_IBA_DA_MISMATCH BIT(14)
+#define SW_IBA_FMT_MISMATCH BIT(13)
+#define SW_IBA_CODE_ERROR BIT(12)
+#define SW_IBA_CMD_ERROR BIT(11)
+#define SW_IBA_CMD_LOC_M (BIT(6) - 1)
+
+#define REG_SW_IBA_STATES__4 0x0114
+
+#define SW_IBA_BUF_STATE_S 30
+#define SW_IBA_CMD_STATE_S 28
+#define SW_IBA_RESP_STATE_S 26
+#define SW_IBA_STATE_M 0x3
+#define SW_IBA_PACKET_SIZE_M 0x7F
+#define SW_IBA_PACKET_SIZE_S 16
+#define SW_IBA_FMT_ID_M 0xFFFF
+
+#define REG_SW_IBA_RESULT__4 0x0118
+
+#define SW_IBA_SIZE_S 24
+
+#define SW_IBA_RETRY_CNT_M (BIT(5) - 1)
+
+/* 2 - PHY */
+#define REG_SW_POWER_MANAGEMENT_CTRL 0x0201
+
+#define SW_PLL_POWER_DOWN BIT(5)
+#define SW_POWER_DOWN_MODE 0x3
+#define SW_ENERGY_DETECTION 1
+#define SW_SOFT_POWER_DOWN 2
+#define SW_POWER_SAVING 3
+
+/* 3 - Operation Control */
+#define REG_SW_OPERATION 0x0300
+
+#define SW_DOUBLE_TAG BIT(7)
+#define SW_RESET BIT(1)
+#define SW_START BIT(0)
+
+#define REG_SW_MAC_ADDR_0 0x0302
+#define REG_SW_MAC_ADDR_1 0x0303
+#define REG_SW_MAC_ADDR_2 0x0304
+#define REG_SW_MAC_ADDR_3 0x0305
+#define REG_SW_MAC_ADDR_4 0x0306
+#define REG_SW_MAC_ADDR_5 0x0307
+
+#define REG_SW_MTU__2 0x0308
+
+#define REG_SW_ISP_TPID__2 0x030A
+
+#define REG_SW_HSR_TPID__2 0x030C
+
+#define REG_AVB_STRATEGY__2 0x030E
+
+#define SW_SHAPING_CREDIT_ACCT BIT(1)
+#define SW_POLICING_CREDIT_ACCT BIT(0)
+
+#define REG_SW_LUE_CTRL_0 0x0310
+
+#define SW_VLAN_ENABLE BIT(7)
+#define SW_DROP_INVALID_VID BIT(6)
+#define SW_AGE_CNT_M 0x7
+#define SW_AGE_CNT_S 3
+#define SW_RESV_MCAST_ENABLE BIT(2)
+#define SW_HASH_OPTION_M 0x03
+#define SW_HASH_OPTION_CRC 1
+#define SW_HASH_OPTION_XOR 2
+#define SW_HASH_OPTION_DIRECT 3
+
+#define REG_SW_LUE_CTRL_1 0x0311
+
+#define UNICAST_LEARN_DISABLE BIT(7)
+#define SW_SRC_ADDR_FILTER BIT(6)
+#define SW_FLUSH_STP_TABLE BIT(5)
+#define SW_FLUSH_MSTP_TABLE BIT(4)
+#define SW_FWD_MCAST_SRC_ADDR BIT(3)
+#define SW_AGING_ENABLE BIT(2)
+#define SW_FAST_AGING BIT(1)
+#define SW_LINK_AUTO_AGING BIT(0)
+
+#define REG_SW_LUE_CTRL_2 0x0312
+
+#define SW_TRAP_DOUBLE_TAG BIT(6)
+#define SW_EGRESS_VLAN_FILTER_DYN BIT(5)
+#define SW_EGRESS_VLAN_FILTER_STA BIT(4)
+#define SW_FLUSH_OPTION_M 0x3
+#define SW_FLUSH_OPTION_S 2
+#define SW_FLUSH_OPTION_DYN_MAC 1
+#define SW_FLUSH_OPTION_STA_MAC 2
+#define SW_FLUSH_OPTION_BOTH 3
+#define SW_PRIO_M 0x3
+#define SW_PRIO_DA 0
+#define SW_PRIO_SA 1
+#define SW_PRIO_HIGHEST_DA_SA 2
+#define SW_PRIO_LOWEST_DA_SA 3
+
+#define REG_SW_LUE_CTRL_3 0x0313
+
+#define REG_SW_LUE_INT_STATUS 0x0314
+#define REG_SW_LUE_INT_ENABLE 0x0315
+
+#define LEARN_FAIL_INT BIT(2)
+#define ALMOST_FULL_INT BIT(1)
+#define WRITE_FAIL_INT BIT(0)
+
+#define REG_SW_LUE_INDEX_0__2 0x0316
+
+#define ENTRY_INDEX_M 0x0FFF
+
+#define REG_SW_LUE_INDEX_1__2 0x0318
+
+#define FAIL_INDEX_M 0x03FF
+
+#define REG_SW_LUE_INDEX_2__2 0x031A
+
+#define REG_SW_LUE_UNK_UCAST_CTRL__4 0x0320
+
+#define SW_UNK_UCAST_ENABLE BIT(31)
+
+#define REG_SW_LUE_UNK_MCAST_CTRL__4 0x0324
+
+#define SW_UNK_MCAST_ENABLE BIT(31)
+
+#define REG_SW_LUE_UNK_VID_CTRL__4 0x0328
+
+#define SW_UNK_VID_ENABLE BIT(31)
+
+#define REG_SW_MAC_CTRL_0 0x0330
+
+#define SW_NEW_BACKOFF BIT(7)
+#define SW_CHECK_LENGTH BIT(3)
+#define SW_PAUSE_UNH_MODE BIT(1)
+#define SW_AGGR_BACKOFF BIT(0)
+
+#define REG_SW_MAC_CTRL_1 0x0331
+
+#define MULTICAST_STORM_DISABLE BIT(6)
+#define SW_BACK_PRESSURE BIT(5)
+#define FAIR_FLOW_CTRL BIT(4)
+#define NO_EXC_COLLISION_DROP BIT(3)
+#define SW_JUMBO_PACKET BIT(2)
+#define SW_LEGAL_PACKET_DISABLE BIT(1)
+#define SW_PASS_SHORT_FRAME BIT(0)
+
+#define REG_SW_MAC_CTRL_2 0x0332
+
+#define SW_REPLACE_VID BIT(3)
+#define BROADCAST_STORM_RATE_HI 0x07
+
+#define REG_SW_MAC_CTRL_3 0x0333
+
+#define BROADCAST_STORM_RATE_LO 0xFF
+#define BROADCAST_STORM_RATE 0x07FF
+
+#define REG_SW_MAC_CTRL_4 0x0334
+
+#define SW_PASS_PAUSE BIT(3)
+
+#define REG_SW_MAC_CTRL_5 0x0335
+
+#define SW_OUT_RATE_LIMIT_QUEUE_BASED BIT(3)
+
+#define REG_SW_MAC_CTRL_6 0x0336
+
+#define SW_MIB_COUNTER_FLUSH BIT(7)
+#define SW_MIB_COUNTER_FREEZE BIT(6)
+
+#define REG_SW_MAC_802_1P_MAP_0 0x0338
+#define REG_SW_MAC_802_1P_MAP_1 0x0339
+#define REG_SW_MAC_802_1P_MAP_2 0x033A
+#define REG_SW_MAC_802_1P_MAP_3 0x033B
+
+#define SW_802_1P_MAP_M KS_PRIO_M
+#define SW_802_1P_MAP_S KS_PRIO_S
+
+#define REG_SW_MAC_ISP_CTRL 0x033C
+
+#define REG_SW_MAC_TOS_CTRL 0x033E
+
+#define SW_TOS_DSCP_REMARK BIT(1)
+#define SW_TOS_DSCP_REMAP BIT(0)
+
+#define REG_SW_MAC_TOS_PRIO_0 0x0340
+#define REG_SW_MAC_TOS_PRIO_1 0x0341
+#define REG_SW_MAC_TOS_PRIO_2 0x0342
+#define REG_SW_MAC_TOS_PRIO_3 0x0343
+#define REG_SW_MAC_TOS_PRIO_4 0x0344
+#define REG_SW_MAC_TOS_PRIO_5 0x0345
+#define REG_SW_MAC_TOS_PRIO_6 0x0346
+#define REG_SW_MAC_TOS_PRIO_7 0x0347
+#define REG_SW_MAC_TOS_PRIO_8 0x0348
+#define REG_SW_MAC_TOS_PRIO_9 0x0349
+#define REG_SW_MAC_TOS_PRIO_10 0x034A
+#define REG_SW_MAC_TOS_PRIO_11 0x034B
+#define REG_SW_MAC_TOS_PRIO_12 0x034C
+#define REG_SW_MAC_TOS_PRIO_13 0x034D
+#define REG_SW_MAC_TOS_PRIO_14 0x034E
+#define REG_SW_MAC_TOS_PRIO_15 0x034F
+#define REG_SW_MAC_TOS_PRIO_16 0x0350
+#define REG_SW_MAC_TOS_PRIO_17 0x0351
+#define REG_SW_MAC_TOS_PRIO_18 0x0352
+#define REG_SW_MAC_TOS_PRIO_19 0x0353
+#define REG_SW_MAC_TOS_PRIO_20 0x0354
+#define REG_SW_MAC_TOS_PRIO_21 0x0355
+#define REG_SW_MAC_TOS_PRIO_22 0x0356
+#define REG_SW_MAC_TOS_PRIO_23 0x0357
+#define REG_SW_MAC_TOS_PRIO_24 0x0358
+#define REG_SW_MAC_TOS_PRIO_25 0x0359
+#define REG_SW_MAC_TOS_PRIO_26 0x035A
+#define REG_SW_MAC_TOS_PRIO_27 0x035B
+#define REG_SW_MAC_TOS_PRIO_28 0x035C
+#define REG_SW_MAC_TOS_PRIO_29 0x035D
+#define REG_SW_MAC_TOS_PRIO_30 0x035E
+#define REG_SW_MAC_TOS_PRIO_31 0x035F
+
+#define REG_SW_MRI_CTRL_0 0x0370
+
+#define SW_IGMP_SNOOP BIT(6)
+#define SW_IPV6_MLD_OPTION BIT(3)
+#define SW_IPV6_MLD_SNOOP BIT(2)
+#define SW_MIRROR_RX_TX BIT(0)
+
+#define REG_SW_CLASS_D_IP_CTRL__4 0x0374
+
+#define SW_CLASS_D_IP_ENABLE BIT(31)
+
+#define REG_SW_MRI_CTRL_8 0x0378
+
+#define SW_NO_COLOR_S 6
+#define SW_RED_COLOR_S 4
+#define SW_YELLOW_COLOR_S 2
+#define SW_GREEN_COLOR_S 0
+#define SW_COLOR_M 0x3
+
+#define REG_SW_QM_CTRL__4 0x0390
+
+#define PRIO_SCHEME_SELECT_M KS_PRIO_M
+#define PRIO_SCHEME_SELECT_S 6
+#define PRIO_MAP_3_HI 0
+#define PRIO_MAP_2_HI 2
+#define PRIO_MAP_0_LO 3
+#define UNICAST_VLAN_BOUNDARY BIT(1)
+
+#define REG_SW_EEE_QM_CTRL__2 0x03C0
+
+#define REG_SW_EEE_TXQ_WAIT_TIME__2 0x03C2
+
+/* 4 - */
+#define REG_SW_VLAN_ENTRY__4 0x0400
+
+#define VLAN_VALID BIT(31)
+#define VLAN_FORWARD_OPTION BIT(27)
+#define VLAN_PRIO_M KS_PRIO_M
+#define VLAN_PRIO_S 24
+#define VLAN_MSTP_M 0x7
+#define VLAN_MSTP_S 12
+#define VLAN_FID_M 0x7F
+
+#define REG_SW_VLAN_ENTRY_UNTAG__4 0x0404
+#define REG_SW_VLAN_ENTRY_PORTS__4 0x0408
+
+#define REG_SW_VLAN_ENTRY_INDEX__2 0x040C
+
+#define VLAN_INDEX_M 0x0FFF
+
+#define REG_SW_VLAN_CTRL 0x040E
+
+#define VLAN_START BIT(7)
+#define VLAN_ACTION 0x3
+#define VLAN_WRITE 1
+#define VLAN_READ 2
+#define VLAN_CLEAR 3
+
+#define REG_SW_ALU_INDEX_0 0x0410
+
+#define ALU_FID_INDEX_S 16
+#define ALU_MAC_ADDR_HI 0xFFFF
+
+#define REG_SW_ALU_INDEX_1 0x0414
+
+#define ALU_DIRECT_INDEX_M (BIT(12) - 1)
+
+#define REG_SW_ALU_CTRL__4 0x0418
+
+#define ALU_VALID_CNT_M (BIT(14) - 1)
+#define ALU_VALID_CNT_S 16
+#define ALU_START BIT(7)
+#define ALU_VALID BIT(6)
+#define ALU_DIRECT BIT(2)
+#define ALU_ACTION 0x3
+#define ALU_WRITE 1
+#define ALU_READ 2
+#define ALU_SEARCH 3
+
+#define REG_SW_ALU_STAT_CTRL__4 0x041C
+
+#define ALU_STAT_INDEX_M (BIT(4) - 1)
+#define ALU_STAT_INDEX_S 16
+#define ALU_RESV_MCAST_INDEX_M (BIT(6) - 1)
+#define ALU_STAT_START BIT(7)
+#define ALU_RESV_MCAST_ADDR BIT(1)
+#define ALU_STAT_READ BIT(0)
+
+#define REG_SW_ALU_VAL_A 0x0420
+
+#define ALU_V_STATIC_VALID BIT(31)
+#define ALU_V_SRC_FILTER BIT(30)
+#define ALU_V_DST_FILTER BIT(29)
+#define ALU_V_PRIO_AGE_CNT_M (BIT(3) - 1)
+#define ALU_V_PRIO_AGE_CNT_S 26
+#define ALU_V_MSTP_M 0x7
+
+#define REG_SW_ALU_VAL_B 0x0424
+
+#define ALU_V_OVERRIDE BIT(31)
+#define ALU_V_USE_FID BIT(30)
+#define ALU_V_PORT_MAP (BIT(24) - 1)
+
+#define REG_SW_ALU_VAL_C 0x0428
+
+#define ALU_V_FID_M (BIT(16) - 1)
+#define ALU_V_FID_S 16
+#define ALU_V_MAC_ADDR_HI 0xFFFF
+
+#define REG_SW_ALU_VAL_D 0x042C
+
+#define REG_HSR_ALU_INDEX_0 0x0440
+
+#define REG_HSR_ALU_INDEX_1 0x0444
+
+#define HSR_DST_MAC_INDEX_LO_S 16
+#define HSR_SRC_MAC_INDEX_HI 0xFFFF
+
+#define REG_HSR_ALU_INDEX_2 0x0448
+
+#define HSR_INDEX_MAX BIT(9)
+#define HSR_DIRECT_INDEX_M (HSR_INDEX_MAX - 1)
+
+#define REG_HSR_ALU_INDEX_3 0x044C
+
+#define HSR_PATH_INDEX_M (BIT(4) - 1)
+
+#define REG_HSR_ALU_CTRL__4 0x0450
+
+#define HSR_VALID_CNT_M (BIT(14) - 1)
+#define HSR_VALID_CNT_S 16
+#define HSR_START BIT(7)
+#define HSR_VALID BIT(6)
+#define HSR_SEARCH_END BIT(5)
+#define HSR_DIRECT BIT(2)
+#define HSR_ACTION 0x3
+#define HSR_WRITE 1
+#define HSR_READ 2
+#define HSR_SEARCH 3
+
+#define REG_HSR_ALU_VAL_A 0x0454
+
+#define HSR_V_STATIC_VALID BIT(31)
+#define HSR_V_AGE_CNT_M (BIT(3) - 1)
+#define HSR_V_AGE_CNT_S 26
+#define HSR_V_PATH_ID_M (BIT(4) - 1)
+
+#define REG_HSR_ALU_VAL_B 0x0458
+
+#define REG_HSR_ALU_VAL_C 0x045C
+
+#define HSR_V_DST_MAC_ADDR_LO_S 16
+#define HSR_V_SRC_MAC_ADDR_HI 0xFFFF
+
+#define REG_HSR_ALU_VAL_D 0x0460
+
+#define REG_HSR_ALU_VAL_E 0x0464
+
+#define HSR_V_START_SEQ_1_S 16
+#define HSR_V_START_SEQ_2_S 0
+
+#define REG_HSR_ALU_VAL_F 0x0468
+
+#define HSR_V_EXP_SEQ_1_S 16
+#define HSR_V_EXP_SEQ_2_S 0
+
+#define REG_HSR_ALU_VAL_G 0x046C
+
+#define HSR_V_SEQ_CNT_1_S 16
+#define HSR_V_SEQ_CNT_2_S 0
+
+#define HSR_V_SEQ_M (BIT(16) - 1)
+
+/* 5 - PTP Clock */
+#define REG_PTP_CLK_CTRL 0x0500
+
+#define PTP_STEP_ADJ BIT(6)
+#define PTP_STEP_DIR BIT(5)
+#define PTP_READ_TIME BIT(4)
+#define PTP_LOAD_TIME BIT(3)
+#define PTP_CLK_ADJ_ENABLE BIT(2)
+#define PTP_CLK_ENABLE BIT(1)
+#define PTP_CLK_RESET BIT(0)
+
+#define REG_PTP_RTC_SUB_NANOSEC__2 0x0502
+
+#define PTP_RTC_SUB_NANOSEC_M 0x0007
+
+#define REG_PTP_RTC_NANOSEC 0x0504
+#define REG_PTP_RTC_NANOSEC_H 0x0504
+#define REG_PTP_RTC_NANOSEC_L 0x0506
+
+#define REG_PTP_RTC_SEC 0x0508
+#define REG_PTP_RTC_SEC_H 0x0508
+#define REG_PTP_RTC_SEC_L 0x050A
+
+#define REG_PTP_SUBNANOSEC_RATE 0x050C
+#define REG_PTP_SUBNANOSEC_RATE_H 0x050C
+
+#define PTP_RATE_DIR BIT(31)
+#define PTP_TMP_RATE_ENABLE BIT(30)
+
+#define REG_PTP_SUBNANOSEC_RATE_L 0x050E
+
+#define REG_PTP_RATE_DURATION 0x0510
+#define REG_PTP_RATE_DURATION_H 0x0510
+#define REG_PTP_RATE_DURATION_L 0x0512
+
+#define REG_PTP_MSG_CONF1 0x0514
+
+#define PTP_802_1AS BIT(7)
+#define PTP_ENABLE BIT(6)
+#define PTP_ETH_ENABLE BIT(5)
+#define PTP_IPV4_UDP_ENABLE BIT(4)
+#define PTP_IPV6_UDP_ENABLE BIT(3)
+#define PTP_TC_P2P BIT(2)
+#define PTP_MASTER BIT(1)
+#define PTP_1STEP BIT(0)
+
+#define REG_PTP_MSG_CONF2 0x0516
+
+#define PTP_UNICAST_ENABLE BIT(12)
+#define PTP_ALTERNATE_MASTER BIT(11)
+#define PTP_ALL_HIGH_PRIO BIT(10)
+#define PTP_SYNC_CHECK BIT(9)
+#define PTP_DELAY_CHECK BIT(8)
+#define PTP_PDELAY_CHECK BIT(7)
+#define PTP_DROP_SYNC_DELAY_REQ BIT(5)
+#define PTP_DOMAIN_CHECK BIT(4)
+#define PTP_UDP_CHECKSUM BIT(2)
+
+#define REG_PTP_DOMAIN_VERSION 0x0518
+#define PTP_VERSION_M 0xFF00
+#define PTP_DOMAIN_M 0x00FF
+
+#define REG_PTP_UNIT_INDEX__4 0x0520
+
+#define PTP_UNIT_M 0xF
+
+#define PTP_GPIO_INDEX_S 16
+#define PTP_TSI_INDEX_S 8
+#define PTP_TOU_INDEX_S 0
+
+#define REG_PTP_TRIG_STATUS__4 0x0524
+
+#define TRIG_ERROR_S 16
+#define TRIG_DONE_S 0
+
+#define REG_PTP_INT_STATUS__4 0x0528
+
+#define TRIG_INT_S 16
+#define TS_INT_S 0
+
+#define TRIG_UNIT_M 0x7
+#define TS_UNIT_M 0x3
+
+#define REG_PTP_CTRL_STAT__4 0x052C
+
+#define GPIO_IN BIT(7)
+#define GPIO_OUT BIT(6)
+#define TS_INT_ENABLE BIT(5)
+#define TRIG_ACTIVE BIT(4)
+#define TRIG_ENABLE BIT(3)
+#define TRIG_RESET BIT(2)
+#define TS_ENABLE BIT(1)
+#define TS_RESET BIT(0)
+
+#define GPIO_CTRL_M (GPIO_IN | GPIO_OUT)
+
+#define TRIG_CTRL_M \
+ (TRIG_ACTIVE | TRIG_ENABLE | TRIG_RESET)
+
+#define TS_CTRL_M \
+ (TS_INT_ENABLE | TS_ENABLE | TS_RESET)
+
+#define REG_TRIG_TARGET_NANOSEC 0x0530
+#define REG_TRIG_TARGET_SEC 0x0534
+
+#define REG_TRIG_CTRL__4 0x0538
+
+#define TRIG_CASCADE_ENABLE BIT(31)
+#define TRIG_CASCADE_TAIL BIT(30)
+#define TRIG_CASCADE_UPS_M 0xF
+#define TRIG_CASCADE_UPS_S 26
+#define TRIG_NOW BIT(25)
+#define TRIG_NOTIFY BIT(24)
+#define TRIG_EDGE BIT(23)
+#define TRIG_PATTERN_S 20
+#define TRIG_PATTERN_M 0x7
+#define TRIG_NEG_EDGE 0
+#define TRIG_POS_EDGE 1
+#define TRIG_NEG_PULSE 2
+#define TRIG_POS_PULSE 3
+#define TRIG_NEG_PERIOD 4
+#define TRIG_POS_PERIOD 5
+#define TRIG_REG_OUTPUT 6
+#define TRIG_GPO_S 16
+#define TRIG_GPO_M 0xF
+#define TRIG_CASCADE_ITERATE_CNT_M 0xFFFF
+
+#define REG_TRIG_CYCLE_WIDTH 0x053C
+
+#define REG_TRIG_CYCLE_CNT 0x0540
+
+#define TRIG_CYCLE_CNT_M 0xFFFF
+#define TRIG_CYCLE_CNT_S 16
+#define TRIG_BIT_PATTERN_M 0xFFFF
+
+#define REG_TRIG_ITERATE_TIME 0x0544
+
+#define REG_TRIG_PULSE_WIDTH__4 0x0548
+
+#define TRIG_PULSE_WIDTH_M 0x00FFFFFF
+
+#define REG_TS_CTRL_STAT__4 0x0550
+
+#define TS_EVENT_DETECT_M 0xF
+#define TS_EVENT_DETECT_S 17
+#define TS_EVENT_OVERFLOW BIT(16)
+#define TS_GPI_M 0xF
+#define TS_GPI_S 8
+#define TS_DETECT_RISE BIT(7)
+#define TS_DETECT_FALL BIT(6)
+#define TS_DETECT_S 6
+#define TS_CASCADE_TAIL BIT(5)
+#define TS_CASCADE_UPS_M 0xF
+#define TS_CASCADE_UPS_S 1
+#define TS_CASCADE_ENABLE BIT(0)
+
+#define DETECT_RISE (TS_DETECT_RISE >> TS_DETECT_S)
+#define DETECT_FALL (TS_DETECT_FALL >> TS_DETECT_S)
+
+#define REG_TS_EVENT_0_NANOSEC 0x0554
+#define REG_TS_EVENT_0_SEC 0x0558
+#define REG_TS_EVENT_0_SUB_NANOSEC 0x055C
+
+#define REG_TS_EVENT_1_NANOSEC 0x0560
+#define REG_TS_EVENT_1_SEC 0x0564
+#define REG_TS_EVENT_1_SUB_NANOSEC 0x0568
+
+#define REG_TS_EVENT_2_NANOSEC 0x056C
+#define REG_TS_EVENT_2_SEC 0x0570
+#define REG_TS_EVENT_2_SUB_NANOSEC 0x0574
+
+#define REG_TS_EVENT_3_NANOSEC 0x0578
+#define REG_TS_EVENT_3_SEC 0x057C
+#define REG_TS_EVENT_3_SUB_NANOSEC 0x0580
+
+#define REG_TS_EVENT_4_NANOSEC 0x0584
+#define REG_TS_EVENT_4_SEC 0x0588
+#define REG_TS_EVENT_4_SUB_NANOSEC 0x058C
+
+#define REG_TS_EVENT_5_NANOSEC 0x0590
+#define REG_TS_EVENT_5_SEC 0x0594
+#define REG_TS_EVENT_5_SUB_NANOSEC 0x0598
+
+#define REG_TS_EVENT_6_NANOSEC 0x059C
+#define REG_TS_EVENT_6_SEC 0x05A0
+#define REG_TS_EVENT_6_SUB_NANOSEC 0x05A4
+
+#define REG_TS_EVENT_7_NANOSEC 0x05A8
+#define REG_TS_EVENT_7_SEC 0x05AC
+#define REG_TS_EVENT_7_SUB_NANOSEC 0x05B0
+
+#define TS_EVENT_EDGE_M 0x1
+#define TS_EVENT_EDGE_S 30
+#define TS_EVENT_NANOSEC_M (BIT(30) - 1)
+
+#define TS_EVENT_SUB_NANOSEC_M 0x7
+
+#define TS_EVENT_SAMPLE \
+ (REG_TS_EVENT_1_NANOSEC - REG_TS_EVENT_0_NANOSEC)
+
+#define PORT_CTRL_ADDR(port, addr) ((addr) | (((port) + 1) << 12))
+
+#define REG_GLOBAL_RR_INDEX__1 0x0600
+
+/* DLR */
+#define REG_DLR_SRC_PORT__4 0x0604
+
+#define DLR_SRC_PORT_UNICAST BIT(31)
+#define DLR_SRC_PORT_M 0x3
+#define DLR_SRC_PORT_BOTH 0
+#define DLR_SRC_PORT_EACH 1
+
+#define REG_DLR_IP_ADDR__4 0x0608
+
+#define REG_DLR_CTRL__1 0x0610
+
+#define DLR_RESET_SEQ_ID BIT(3)
+#define DLR_BACKUP_AUTO_ON BIT(2)
+#define DLR_BEACON_TX_ENABLE BIT(1)
+#define DLR_ASSIST_ENABLE BIT(0)
+
+#define REG_DLR_STATE__1 0x0611
+
+#define DLR_NODE_STATE_M 0x3
+#define DLR_NODE_STATE_S 1
+#define DLR_NODE_STATE_IDLE 0
+#define DLR_NODE_STATE_FAULT 1
+#define DLR_NODE_STATE_NORMAL 2
+#define DLR_RING_STATE_FAULT 0
+#define DLR_RING_STATE_NORMAL 1
+
+#define REG_DLR_PRECEDENCE__1 0x0612
+
+#define REG_DLR_BEACON_INTERVAL__4 0x0614
+
+#define REG_DLR_BEACON_TIMEOUT__4 0x0618
+
+#define REG_DLR_TIMEOUT_WINDOW__4 0x061C
+
+#define DLR_TIMEOUT_WINDOW_M (BIT(22) - 1)
+
+#define REG_DLR_VLAN_ID__2 0x0620
+
+#define DLR_VLAN_ID_M (BIT(12) - 1)
+
+#define REG_DLR_DEST_ADDR_0 0x0622
+#define REG_DLR_DEST_ADDR_1 0x0623
+#define REG_DLR_DEST_ADDR_2 0x0624
+#define REG_DLR_DEST_ADDR_3 0x0625
+#define REG_DLR_DEST_ADDR_4 0x0626
+#define REG_DLR_DEST_ADDR_5 0x0627
+
+#define REG_DLR_PORT_MAP__4 0x0628
+
+#define REG_DLR_CLASS__1 0x062C
+
+#define DLR_FRAME_QID_M 0x3
+
+/* HSR */
+#define REG_HSR_PORT_MAP__4 0x0640
+
+#define REG_HSR_ALU_CTRL_0__1 0x0644
+
+#define HSR_DUPLICATE_DISCARD BIT(7)
+#define HSR_NODE_UNICAST BIT(6)
+#define HSR_AGE_CNT_DEFAULT_M 0x7
+#define HSR_AGE_CNT_DEFAULT_S 3
+#define HSR_LEARN_MCAST_DISABLE BIT(2)
+#define HSR_HASH_OPTION_M 0x3
+#define HSR_HASH_DISABLE 0
+#define HSR_HASH_UPPER_BITS 1
+#define HSR_HASH_LOWER_BITS 2
+#define HSR_HASH_XOR_BOTH_BITS 3
+
+#define REG_HSR_ALU_CTRL_1__1 0x0645
+
+#define HSR_LEARN_UCAST_DISABLE BIT(7)
+#define HSR_FLUSH_TABLE BIT(5)
+#define HSR_PROC_MCAST_SRC BIT(3)
+#define HSR_AGING_ENABLE BIT(2)
+
+#define REG_HSR_ALU_CTRL_2__2 0x0646
+
+#define REG_HSR_ALU_AGE_PERIOD__4 0x0648
+
+#define REG_HSR_ALU_INT_STATUS__1 0x064C
+#define REG_HSR_ALU_INT_MASK__1 0x064D
+
+#define HSR_WINDOW_OVERFLOW_INT BIT(3)
+#define HSR_LEARN_FAIL_INT BIT(2)
+#define HSR_ALMOST_FULL_INT BIT(1)
+#define HSR_WRITE_FAIL_INT BIT(0)
+
+#define REG_HSR_ALU_ENTRY_0__2 0x0650
+
+#define HSR_ENTRY_INDEX_M (BIT(10) - 1)
+#define HSR_FAIL_INDEX_M (BIT(8) - 1)
+
+#define REG_HSR_ALU_ENTRY_1__2 0x0652
+
+#define HSR_FAIL_LEARN_INDEX_M (BIT(8) - 1)
+
+#define REG_HSR_ALU_ENTRY_3__2 0x0654
+
+#define HSR_CPU_ACCESS_ENTRY_INDEX_M (BIT(8) - 1)
+
+/* 0 - Operation */
+#define REG_PORT_DEFAULT_VID 0x0000
+
+#define REG_PORT_CUSTOM_VID 0x0002
+#define REG_PORT_AVB_SR_1_VID 0x0004
+#define REG_PORT_AVB_SR_2_VID 0x0006
+
+#define REG_PORT_AVB_SR_1_TYPE 0x0008
+#define REG_PORT_AVB_SR_2_TYPE 0x000A
+
+#define REG_PORT_PME_STATUS 0x0013
+#define REG_PORT_PME_CTRL 0x0017
+
+#define PME_WOL_MAGICPKT BIT(2)
+#define PME_WOL_LINKUP BIT(1)
+#define PME_WOL_ENERGY BIT(0)
+
+#define REG_PORT_INT_STATUS 0x001B
+#define REG_PORT_INT_MASK 0x001F
+
+#define PORT_SGMII_INT BIT(3)
+#define PORT_PTP_INT BIT(2)
+#define PORT_PHY_INT BIT(1)
+#define PORT_ACL_INT BIT(0)
+
+#define PORT_INT_MASK \
+ (PORT_SGMII_INT | PORT_PTP_INT | PORT_PHY_INT | PORT_ACL_INT)
+
+#define REG_PORT_CTRL_0 0x0020
+
+#define PORT_MAC_LOOPBACK BIT(7)
+#define PORT_FORCE_TX_FLOW_CTRL BIT(4)
+#define PORT_FORCE_RX_FLOW_CTRL BIT(3)
+#define PORT_TAIL_TAG_ENABLE BIT(2)
+#define PORT_QUEUE_SPLIT_ENABLE 0x3
+
+#define REG_PORT_CTRL_1 0x0021
+
+#define PORT_SRP_ENABLE 0x3
+
+#define REG_PORT_STATUS_0 0x0030
+
+#define PORT_INTF_SPEED_M 0x3
+#define PORT_INTF_SPEED_S 3
+#define PORT_INTF_FULL_DUPLEX BIT(2)
+#define PORT_TX_FLOW_CTRL BIT(1)
+#define PORT_RX_FLOW_CTRL BIT(0)
+
+#define REG_PORT_STATUS_1 0x0034
+
+/* 1 - PHY */
+#define REG_PORT_PHY_CTRL 0x0100
+
+#define PORT_PHY_RESET BIT(15)
+#define PORT_PHY_LOOPBACK BIT(14)
+#define PORT_SPEED_100MBIT BIT(13)
+#define PORT_AUTO_NEG_ENABLE BIT(12)
+#define PORT_POWER_DOWN BIT(11)
+#define PORT_ISOLATE BIT(10)
+#define PORT_AUTO_NEG_RESTART BIT(9)
+#define PORT_FULL_DUPLEX BIT(8)
+#define PORT_COLLISION_TEST BIT(7)
+#define PORT_SPEED_1000MBIT BIT(6)
+
+#define REG_PORT_PHY_STATUS 0x0102
+
+#define PORT_100BT4_CAPABLE BIT(15)
+#define PORT_100BTX_FD_CAPABLE BIT(14)
+#define PORT_100BTX_CAPABLE BIT(13)
+#define PORT_10BT_FD_CAPABLE BIT(12)
+#define PORT_10BT_CAPABLE BIT(11)
+#define PORT_EXTENDED_STATUS BIT(8)
+#define PORT_MII_SUPPRESS_CAPABLE BIT(6)
+#define PORT_AUTO_NEG_ACKNOWLEDGE BIT(5)
+#define PORT_REMOTE_FAULT BIT(4)
+#define PORT_AUTO_NEG_CAPABLE BIT(3)
+#define PORT_LINK_STATUS BIT(2)
+#define PORT_JABBER_DETECT BIT(1)
+#define PORT_EXTENDED_CAPABILITY BIT(0)
+
+#define REG_PORT_PHY_ID_HI 0x0104
+#define REG_PORT_PHY_ID_LO 0x0106
+
+#define KSZ9477_ID_HI 0x0022
+#define KSZ9477_ID_LO 0x1622
+
+#define REG_PORT_PHY_AUTO_NEGOTIATION 0x0108
+
+#define PORT_AUTO_NEG_NEXT_PAGE BIT(15)
+#define PORT_AUTO_NEG_REMOTE_FAULT BIT(13)
+#define PORT_AUTO_NEG_ASYM_PAUSE BIT(11)
+#define PORT_AUTO_NEG_SYM_PAUSE BIT(10)
+#define PORT_AUTO_NEG_100BT4 BIT(9)
+#define PORT_AUTO_NEG_100BTX_FD BIT(8)
+#define PORT_AUTO_NEG_100BTX BIT(7)
+#define PORT_AUTO_NEG_10BT_FD BIT(6)
+#define PORT_AUTO_NEG_10BT BIT(5)
+#define PORT_AUTO_NEG_SELECTOR 0x001F
+#define PORT_AUTO_NEG_802_3 0x0001
+
+#define PORT_AUTO_NEG_PAUSE \
+ (PORT_AUTO_NEG_ASYM_PAUSE | PORT_AUTO_NEG_SYM_PAUSE)
+
+#define REG_PORT_PHY_REMOTE_CAPABILITY 0x010A
+
+#define PORT_REMOTE_NEXT_PAGE BIT(15)
+#define PORT_REMOTE_ACKNOWLEDGE BIT(14)
+#define PORT_REMOTE_REMOTE_FAULT BIT(13)
+#define PORT_REMOTE_ASYM_PAUSE BIT(11)
+#define PORT_REMOTE_SYM_PAUSE BIT(10)
+#define PORT_REMOTE_100BTX_FD BIT(8)
+#define PORT_REMOTE_100BTX BIT(7)
+#define PORT_REMOTE_10BT_FD BIT(6)
+#define PORT_REMOTE_10BT BIT(5)
+
+#define REG_PORT_PHY_1000_CTRL 0x0112
+
+#define PORT_AUTO_NEG_MANUAL BIT(12)
+#define PORT_AUTO_NEG_MASTER BIT(11)
+#define PORT_AUTO_NEG_MASTER_PREFERRED BIT(10)
+#define PORT_AUTO_NEG_1000BT_FD BIT(9)
+#define PORT_AUTO_NEG_1000BT BIT(8)
+
+#define REG_PORT_PHY_1000_STATUS 0x0114
+
+#define PORT_MASTER_FAULT BIT(15)
+#define PORT_LOCAL_MASTER BIT(14)
+#define PORT_LOCAL_RX_OK BIT(13)
+#define PORT_REMOTE_RX_OK BIT(12)
+#define PORT_REMOTE_1000BT_FD BIT(11)
+#define PORT_REMOTE_1000BT BIT(10)
+#define PORT_REMOTE_IDLE_CNT_M 0x0F
+
+#define PORT_PHY_1000_STATIC_STATUS \
+ (PORT_LOCAL_RX_OK | \
+ PORT_REMOTE_RX_OK | \
+ PORT_REMOTE_1000BT_FD | \
+ PORT_REMOTE_1000BT)
+
+#define REG_PORT_PHY_MMD_SETUP 0x011A
+
+#define PORT_MMD_OP_MODE_M 0x3
+#define PORT_MMD_OP_MODE_S 14
+#define PORT_MMD_OP_INDEX 0
+#define PORT_MMD_OP_DATA_NO_INCR 1
+#define PORT_MMD_OP_DATA_INCR_RW 2
+#define PORT_MMD_OP_DATA_INCR_W 3
+#define PORT_MMD_DEVICE_ID_M 0x1F
+
+#define MMD_SETUP(mode, dev) \
+ (((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev))
+
+#define REG_PORT_PHY_MMD_INDEX_DATA 0x011C
+
+#define MMD_DEVICE_ID_DSP 1
+
+#define MMD_DSP_SQI_CHAN_A 0xAC
+#define MMD_DSP_SQI_CHAN_B 0xAD
+#define MMD_DSP_SQI_CHAN_C 0xAE
+#define MMD_DSP_SQI_CHAN_D 0xAF
+
+#define DSP_SQI_ERR_DETECTED BIT(15)
+#define DSP_SQI_AVG_ERR 0x7FFF
+
+#define MMD_DEVICE_ID_COMMON 2
+
+#define MMD_DEVICE_ID_EEE_ADV 7
+
+#define MMD_EEE_ADV 0x3C
+#define EEE_ADV_100MBIT BIT(1)
+#define EEE_ADV_1GBIT BIT(2)
+
+#define MMD_EEE_LP_ADV 0x3D
+#define MMD_EEE_MSG_CODE 0x3F
+
+#define MMD_DEVICE_ID_AFED 0x1C
+
+#define REG_PORT_PHY_EXTENDED_STATUS 0x011E
+
+#define PORT_100BTX_FD_ABLE BIT(15)
+#define PORT_100BTX_ABLE BIT(14)
+#define PORT_10BT_FD_ABLE BIT(13)
+#define PORT_10BT_ABLE BIT(12)
+
+#define REG_PORT_SGMII_ADDR__4 0x0200
+#define PORT_SGMII_AUTO_INCR BIT(23)
+#define PORT_SGMII_DEVICE_ID_M 0x1F
+#define PORT_SGMII_DEVICE_ID_S 16
+#define PORT_SGMII_ADDR_M (BIT(21) - 1)
+
+#define REG_PORT_SGMII_DATA__4 0x0204
+#define PORT_SGMII_DATA_M (BIT(16) - 1)
+
+#define MMD_DEVICE_ID_PMA 0x01
+#define MMD_DEVICE_ID_PCS 0x03
+#define MMD_DEVICE_ID_PHY_XS 0x04
+#define MMD_DEVICE_ID_DTE_XS 0x05
+#define MMD_DEVICE_ID_AN 0x07
+#define MMD_DEVICE_ID_VENDOR_CTRL 0x1E
+#define MMD_DEVICE_ID_VENDOR_MII 0x1F
+
+#define SR_MII MMD_DEVICE_ID_VENDOR_MII
+
+#define MMD_SR_MII_CTRL 0x0000
+
+#define SR_MII_RESET BIT(15)
+#define SR_MII_LOOPBACK BIT(14)
+#define SR_MII_SPEED_100MBIT BIT(13)
+#define SR_MII_AUTO_NEG_ENABLE BIT(12)
+#define SR_MII_POWER_DOWN BIT(11)
+#define SR_MII_AUTO_NEG_RESTART BIT(9)
+#define SR_MII_FULL_DUPLEX BIT(8)
+#define SR_MII_SPEED_1000MBIT BIT(6)
+
+#define MMD_SR_MII_STATUS 0x0001
+#define MMD_SR_MII_ID_1 0x0002
+#define MMD_SR_MII_ID_2 0x0003
+#define MMD_SR_MII_AUTO_NEGOTIATION 0x0004
+
+#define SR_MII_AUTO_NEG_NEXT_PAGE BIT(15)
+#define SR_MII_AUTO_NEG_REMOTE_FAULT_M 0x3
+#define SR_MII_AUTO_NEG_REMOTE_FAULT_S 12
+#define SR_MII_AUTO_NEG_NO_ERROR 0
+#define SR_MII_AUTO_NEG_OFFLINE 1
+#define SR_MII_AUTO_NEG_LINK_FAILURE 2
+#define SR_MII_AUTO_NEG_ERROR 3
+#define SR_MII_AUTO_NEG_PAUSE_M 0x3
+#define SR_MII_AUTO_NEG_PAUSE_S 7
+#define SR_MII_AUTO_NEG_NO_PAUSE 0
+#define SR_MII_AUTO_NEG_ASYM_PAUSE_TX 1
+#define SR_MII_AUTO_NEG_SYM_PAUSE 2
+#define SR_MII_AUTO_NEG_ASYM_PAUSE_RX 3
+#define SR_MII_AUTO_NEG_HALF_DUPLEX BIT(6)
+#define SR_MII_AUTO_NEG_FULL_DUPLEX BIT(5)
+
+#define MMD_SR_MII_REMOTE_CAPABILITY 0x0005
+#define MMD_SR_MII_AUTO_NEG_EXP 0x0006
+#define MMD_SR_MII_AUTO_NEG_EXT 0x000F
+
+#define MMD_SR_MII_DIGITAL_CTRL_1 0x8000
+
+#define MMD_SR_MII_AUTO_NEG_CTRL 0x8001
+
+#define SR_MII_8_BIT BIT(8)
+#define SR_MII_SGMII_LINK_UP BIT(4)
+#define SR_MII_TX_CFG_PHY_MASTER BIT(3)
+#define SR_MII_PCS_MODE_M 0x3
+#define SR_MII_PCS_MODE_S 1
+#define SR_MII_PCS_SGMII 2
+#define SR_MII_AUTO_NEG_COMPLETE_INTR BIT(0)
+
+#define MMD_SR_MII_AUTO_NEG_STATUS 0x8002
+
+#define SR_MII_STAT_LINK_UP BIT(4)
+#define SR_MII_STAT_M 0x3
+#define SR_MII_STAT_S 2
+#define SR_MII_STAT_10_MBPS 0
+#define SR_MII_STAT_100_MBPS 1
+#define SR_MII_STAT_1000_MBPS 2
+#define SR_MII_STAT_FULL_DUPLEX BIT(1)
+
+#define MMD_SR_MII_PHY_CTRL 0x80A0
+
+#define SR_MII_PHY_LANE_SEL_M 0xF
+#define SR_MII_PHY_LANE_SEL_S 8
+#define SR_MII_PHY_WRITE BIT(1)
+#define SR_MII_PHY_START_BUSY BIT(0)
+
+#define MMD_SR_MII_PHY_ADDR 0x80A1
+
+#define SR_MII_PHY_ADDR_M (BIT(16) - 1)
+
+#define MMD_SR_MII_PHY_DATA 0x80A2
+
+#define SR_MII_PHY_DATA_M (BIT(16) - 1)
+
+#define SR_MII_PHY_JTAG_CHIP_ID_HI 0x000C
+#define SR_MII_PHY_JTAG_CHIP_ID_LO 0x000D
+
+#define REG_PORT_PHY_REMOTE_LB_LED 0x0122
+
+#define PORT_REMOTE_LOOPBACK BIT(8)
+#define PORT_LED_SELECT (3 << 6)
+#define PORT_LED_CTRL (3 << 4)
+#define PORT_LED_CTRL_TEST BIT(3)
+#define PORT_10BT_PREAMBLE BIT(2)
+#define PORT_LINK_MD_10BT_ENABLE BIT(1)
+#define PORT_LINK_MD_PASS BIT(0)
+
+#define REG_PORT_PHY_LINK_MD 0x0124
+
+#define PORT_START_CABLE_DIAG BIT(15)
+#define PORT_TX_DISABLE BIT(14)
+#define PORT_CABLE_DIAG_PAIR_M 0x3
+#define PORT_CABLE_DIAG_PAIR_S 12
+#define PORT_CABLE_DIAG_SELECT_M 0x3
+#define PORT_CABLE_DIAG_SELECT_S 10
+#define PORT_CABLE_DIAG_RESULT_M 0x3
+#define PORT_CABLE_DIAG_RESULT_S 8
+#define PORT_CABLE_STAT_NORMAL 0
+#define PORT_CABLE_STAT_OPEN 1
+#define PORT_CABLE_STAT_SHORT 2
+#define PORT_CABLE_STAT_FAILED 3
+#define PORT_CABLE_FAULT_COUNTER 0x00FF
+
+#define REG_PORT_PHY_PMA_STATUS 0x0126
+
+#define PORT_1000_LINK_GOOD BIT(1)
+#define PORT_100_LINK_GOOD BIT(0)
+
+#define REG_PORT_PHY_DIGITAL_STATUS 0x0128
+
+#define PORT_LINK_DETECT BIT(14)
+#define PORT_SIGNAL_DETECT BIT(13)
+#define PORT_PHY_STAT_MDI BIT(12)
+#define PORT_PHY_STAT_MASTER BIT(11)
+
+#define REG_PORT_PHY_RXER_COUNTER 0x012A
+
+#define REG_PORT_PHY_INT_ENABLE 0x0136
+#define REG_PORT_PHY_INT_STATUS 0x0137
+
+#define JABBER_INT BIT(7)
+#define RX_ERR_INT BIT(6)
+#define PAGE_RX_INT BIT(5)
+#define PARALLEL_DETECT_FAULT_INT BIT(4)
+#define LINK_PARTNER_ACK_INT BIT(3)
+#define LINK_DOWN_INT BIT(2)
+#define REMOTE_FAULT_INT BIT(1)
+#define LINK_UP_INT BIT(0)
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_1 0x0138
+
+#define PORT_REG_CLK_SPEED_25_MHZ BIT(14)
+#define PORT_PHY_FORCE_MDI BIT(7)
+#define PORT_PHY_AUTO_MDIX_DISABLE BIT(6)
+
+/* Same as PORT_PHY_LOOPBACK */
+#define PORT_PHY_PCS_LOOPBACK BIT(0)
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_2 0x013A
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_3 0x013C
+
+#define PORT_100BT_FIXED_LATENCY BIT(15)
+
+#define REG_PORT_PHY_PHY_CTRL 0x013E
+
+#define PORT_INT_PIN_HIGH BIT(14)
+#define PORT_ENABLE_JABBER BIT(9)
+#define PORT_STAT_SPEED_1000MBIT BIT(6)
+#define PORT_STAT_SPEED_100MBIT BIT(5)
+#define PORT_STAT_SPEED_10MBIT BIT(4)
+#define PORT_STAT_FULL_DUPLEX BIT(3)
+
+/* Same as PORT_PHY_STAT_MASTER */
+#define PORT_STAT_MASTER BIT(2)
+#define PORT_RESET BIT(1)
+#define PORT_LINK_STATUS_FAIL BIT(0)
+
+/* 3 - xMII */
+#define REG_PORT_XMII_CTRL_0 0x0300
+
+#define PORT_SGMII_SEL BIT(7)
+#define PORT_MII_FULL_DUPLEX BIT(6)
+#define PORT_MII_100MBIT BIT(4)
+#define PORT_GRXC_ENABLE BIT(0)
+
+#define REG_PORT_XMII_CTRL_1 0x0301
+
+#define PORT_RMII_CLK_SEL BIT(7)
+/* S1 */
+#define PORT_MII_1000MBIT_S1 BIT(6)
+/* S2 */
+#define PORT_MII_NOT_1GBIT BIT(6)
+#define PORT_MII_SEL_EDGE BIT(5)
+#define PORT_RGMII_ID_IG_ENABLE BIT(4)
+#define PORT_RGMII_ID_EG_ENABLE BIT(3)
+#define PORT_MII_MAC_MODE BIT(2)
+#define PORT_MII_SEL_M 0x3
+/* S1 */
+#define PORT_MII_SEL_S1 0x0
+#define PORT_RMII_SEL_S1 0x1
+#define PORT_GMII_SEL_S1 0x2
+#define PORT_RGMII_SEL_S1 0x3
+/* S2 */
+#define PORT_RGMII_SEL 0x0
+#define PORT_RMII_SEL 0x1
+#define PORT_GMII_SEL 0x2
+#define PORT_MII_SEL 0x3
+
+/* 4 - MAC */
+#define REG_PORT_MAC_CTRL_0 0x0400
+
+#define PORT_BROADCAST_STORM BIT(1)
+#define PORT_JUMBO_FRAME BIT(0)
+
+#define REG_PORT_MAC_CTRL_1 0x0401
+
+#define PORT_BACK_PRESSURE BIT(3)
+#define PORT_PASS_ALL BIT(0)
+
+#define REG_PORT_MAC_CTRL_2 0x0402
+
+#define PORT_100BT_EEE_DISABLE BIT(7)
+#define PORT_1000BT_EEE_DISABLE BIT(6)
+
+#define REG_PORT_MAC_IN_RATE_LIMIT 0x0403
+
+#define PORT_IN_PORT_BASED_S 6
+#define PORT_RATE_PACKET_BASED_S 5
+#define PORT_IN_FLOW_CTRL_S 4
+#define PORT_COUNT_IFG_S 1
+#define PORT_COUNT_PREAMBLE_S 0
+#define PORT_IN_PORT_BASED BIT(6)
+#define PORT_IN_PACKET_BASED BIT(5)
+#define PORT_IN_FLOW_CTRL BIT(4)
+#define PORT_IN_LIMIT_MODE_M 0x3
+#define PORT_IN_LIMIT_MODE_S 2
+#define PORT_IN_ALL 0
+#define PORT_IN_UNICAST 1
+#define PORT_IN_MULTICAST 2
+#define PORT_IN_BROADCAST 3
+#define PORT_COUNT_IFG BIT(1)
+#define PORT_COUNT_PREAMBLE BIT(0)
+
+#define REG_PORT_IN_RATE_0 0x0410
+#define REG_PORT_IN_RATE_1 0x0411
+#define REG_PORT_IN_RATE_2 0x0412
+#define REG_PORT_IN_RATE_3 0x0413
+#define REG_PORT_IN_RATE_4 0x0414
+#define REG_PORT_IN_RATE_5 0x0415
+#define REG_PORT_IN_RATE_6 0x0416
+#define REG_PORT_IN_RATE_7 0x0417
+
+#define REG_PORT_OUT_RATE_0 0x0420
+#define REG_PORT_OUT_RATE_1 0x0421
+#define REG_PORT_OUT_RATE_2 0x0422
+#define REG_PORT_OUT_RATE_3 0x0423
+
+#define PORT_RATE_LIMIT_M (BIT(7) - 1)
+
+/* 5 - MIB Counters */
+#define REG_PORT_MIB_CTRL_STAT__4 0x0500
+
+#define MIB_COUNTER_OVERFLOW BIT(31)
+#define MIB_COUNTER_VALID BIT(30)
+#define MIB_COUNTER_READ BIT(25)
+#define MIB_COUNTER_FLUSH_FREEZE BIT(24)
+#define MIB_COUNTER_INDEX_M (BIT(8) - 1)
+#define MIB_COUNTER_INDEX_S 16
+#define MIB_COUNTER_DATA_HI_M 0xF
+
+#define REG_PORT_MIB_DATA 0x0504
+
+/* 6 - ACL */
+#define REG_PORT_ACL_0 0x0600
+
+#define ACL_FIRST_RULE_M 0xF
+
+#define REG_PORT_ACL_1 0x0601
+
+#define ACL_MODE_M 0x3
+#define ACL_MODE_S 4
+#define ACL_MODE_DISABLE 0
+#define ACL_MODE_LAYER_2 1
+#define ACL_MODE_LAYER_3 2
+#define ACL_MODE_LAYER_4 3
+#define ACL_ENABLE_M 0x3
+#define ACL_ENABLE_S 2
+#define ACL_ENABLE_2_COUNT 0
+#define ACL_ENABLE_2_TYPE 1
+#define ACL_ENABLE_2_MAC 2
+#define ACL_ENABLE_2_BOTH 3
+#define ACL_ENABLE_3_IP 1
+#define ACL_ENABLE_3_SRC_DST_COMP 2
+#define ACL_ENABLE_4_PROTOCOL 0
+#define ACL_ENABLE_4_TCP_PORT_COMP 1
+#define ACL_ENABLE_4_UDP_PORT_COMP 2
+#define ACL_ENABLE_4_TCP_SEQN_COMP 3
+#define ACL_SRC BIT(1)
+#define ACL_EQUAL BIT(0)
+
+#define REG_PORT_ACL_2 0x0602
+#define REG_PORT_ACL_3 0x0603
+
+#define ACL_MAX_PORT 0xFFFF
+
+#define REG_PORT_ACL_4 0x0604
+#define REG_PORT_ACL_5 0x0605
+
+#define ACL_MIN_PORT 0xFFFF
+#define ACL_IP_ADDR 0xFFFFFFFF
+#define ACL_TCP_SEQNUM 0xFFFFFFFF
+
+#define REG_PORT_ACL_6 0x0606
+
+#define ACL_RESERVED 0xF8
+#define ACL_PORT_MODE_M 0x3
+#define ACL_PORT_MODE_S 1
+#define ACL_PORT_MODE_DISABLE 0
+#define ACL_PORT_MODE_EITHER 1
+#define ACL_PORT_MODE_IN_RANGE 2
+#define ACL_PORT_MODE_OUT_OF_RANGE 3
+
+#define REG_PORT_ACL_7 0x0607
+
+#define ACL_TCP_FLAG_ENABLE BIT(0)
+
+#define REG_PORT_ACL_8 0x0608
+
+#define ACL_TCP_FLAG_M 0xFF
+
+#define REG_PORT_ACL_9 0x0609
+
+#define ACL_TCP_FLAG 0xFF
+#define ACL_ETH_TYPE 0xFFFF
+#define ACL_IP_M 0xFFFFFFFF
+
+#define REG_PORT_ACL_A 0x060A
+
+#define ACL_PRIO_MODE_M 0x3
+#define ACL_PRIO_MODE_S 6
+#define ACL_PRIO_MODE_DISABLE 0
+#define ACL_PRIO_MODE_HIGHER 1
+#define ACL_PRIO_MODE_LOWER 2
+#define ACL_PRIO_MODE_REPLACE 3
+#define ACL_PRIO_M KS_PRIO_M
+#define ACL_PRIO_S 3
+#define ACL_VLAN_PRIO_REPLACE BIT(2)
+#define ACL_VLAN_PRIO_M KS_PRIO_M
+#define ACL_VLAN_PRIO_HI_M 0x3
+
+#define REG_PORT_ACL_B 0x060B
+
+#define ACL_VLAN_PRIO_LO_M 0x8
+#define ACL_VLAN_PRIO_S 7
+#define ACL_MAP_MODE_M 0x3
+#define ACL_MAP_MODE_S 5
+#define ACL_MAP_MODE_DISABLE 0
+#define ACL_MAP_MODE_OR 1
+#define ACL_MAP_MODE_AND 2
+#define ACL_MAP_MODE_REPLACE 3
+
+#define ACL_CNT_M (BIT(11) - 1)
+#define ACL_CNT_S 5
+
+#define REG_PORT_ACL_C 0x060C
+
+#define REG_PORT_ACL_D 0x060D
+#define ACL_MSEC_UNIT BIT(6)
+#define ACL_INTR_MODE BIT(5)
+#define ACL_PORT_MAP 0x7F
+
+#define REG_PORT_ACL_E 0x060E
+#define REG_PORT_ACL_F 0x060F
+
+#define REG_PORT_ACL_BYTE_EN_MSB 0x0610
+#define REG_PORT_ACL_BYTE_EN_LSB 0x0611
+
+#define ACL_ACTION_START 0xA
+#define ACL_ACTION_LEN 4
+#define ACL_INTR_CNT_START 0xD
+#define ACL_RULESET_START 0xE
+#define ACL_RULESET_LEN 2
+#define ACL_TABLE_LEN 16
+
+#define ACL_ACTION_ENABLE 0x003C
+#define ACL_MATCH_ENABLE 0x7FC3
+#define ACL_RULESET_ENABLE 0x8003
+#define ACL_BYTE_ENABLE 0xFFFF
+
+#define REG_PORT_ACL_CTRL_0 0x0612
+
+#define PORT_ACL_WRITE_DONE BIT(6)
+#define PORT_ACL_READ_DONE BIT(5)
+#define PORT_ACL_WRITE BIT(4)
+#define PORT_ACL_INDEX_M 0xF
+
+#define REG_PORT_ACL_CTRL_1 0x0613
+
+/* 8 - Classification and Policing */
+#define REG_PORT_MRI_MIRROR_CTRL 0x0800
+
+#define PORT_MIRROR_RX BIT(6)
+#define PORT_MIRROR_TX BIT(5)
+#define PORT_MIRROR_SNIFFER BIT(1)
+
+#define REG_PORT_MRI_PRIO_CTRL 0x0801
+
+#define PORT_HIGHEST_PRIO BIT(7)
+#define PORT_OR_PRIO BIT(6)
+#define PORT_MAC_PRIO_ENABLE BIT(4)
+#define PORT_VLAN_PRIO_ENABLE BIT(3)
+#define PORT_802_1P_PRIO_ENABLE BIT(2)
+#define PORT_DIFFSERV_PRIO_ENABLE BIT(1)
+#define PORT_ACL_PRIO_ENABLE BIT(0)
+
+#define REG_PORT_MRI_MAC_CTRL 0x0802
+
+#define PORT_USER_PRIO_CEILING BIT(7)
+#define PORT_DROP_NON_VLAN BIT(4)
+#define PORT_DROP_TAG BIT(3)
+#define PORT_BASED_PRIO_M KS_PRIO_M
+#define PORT_BASED_PRIO_S 0
+
+#define REG_PORT_MRI_AUTHEN_CTRL 0x0803
+
+#define PORT_ACL_ENABLE BIT(2)
+#define PORT_AUTHEN_MODE 0x3
+#define PORT_AUTHEN_PASS 0
+#define PORT_AUTHEN_BLOCK 1
+#define PORT_AUTHEN_TRAP 2
+
+#define REG_PORT_MRI_INDEX__4 0x0804
+
+#define MRI_INDEX_P_M 0x7
+#define MRI_INDEX_P_S 16
+#define MRI_INDEX_Q_M 0x3
+#define MRI_INDEX_Q_S 0
+
+#define REG_PORT_MRI_TC_MAP__4 0x0808
+
+#define PORT_TC_MAP_M 0xf
+#define PORT_TC_MAP_S 4
+
+#define REG_PORT_MRI_POLICE_CTRL__4 0x080C
+
+#define POLICE_DROP_ALL BIT(10)
+#define POLICE_PACKET_TYPE_M 0x3
+#define POLICE_PACKET_TYPE_S 8
+#define POLICE_PACKET_DROPPED 0
+#define POLICE_PACKET_GREEN 1
+#define POLICE_PACKET_YELLOW 2
+#define POLICE_PACKET_RED 3
+#define PORT_BASED_POLICING BIT(7)
+#define NON_DSCP_COLOR_M 0x3
+#define NON_DSCP_COLOR_S 5
+#define COLOR_MARK_ENABLE BIT(4)
+#define COLOR_REMAP_ENABLE BIT(3)
+#define POLICE_DROP_SRP BIT(2)
+#define POLICE_COLOR_NOT_AWARE BIT(1)
+#define POLICE_ENABLE BIT(0)
+
+#define REG_PORT_POLICE_COLOR_0__4 0x0810
+#define REG_PORT_POLICE_COLOR_1__4 0x0814
+#define REG_PORT_POLICE_COLOR_2__4 0x0818
+#define REG_PORT_POLICE_COLOR_3__4 0x081C
+
+#define POLICE_COLOR_MAP_S 2
+#define POLICE_COLOR_MAP_M (BIT(POLICE_COLOR_MAP_S) - 1)
+
+#define REG_PORT_POLICE_RATE__4 0x0820
+
+#define POLICE_CIR_S 16
+#define POLICE_PIR_S 0
+
+#define REG_PORT_POLICE_BURST_SIZE__4 0x0824
+
+#define POLICE_BURST_SIZE_M 0x3FFF
+#define POLICE_CBS_S 16
+#define POLICE_PBS_S 0
+
+#define REG_PORT_WRED_PM_CTRL_0__4 0x0830
+
+#define WRED_PM_CTRL_M (BIT(11) - 1)
+
+#define WRED_PM_MAX_THRESHOLD_S 16
+#define WRED_PM_MIN_THRESHOLD_S 0
+
+#define REG_PORT_WRED_PM_CTRL_1__4 0x0834
+
+#define WRED_PM_MULTIPLIER_S 16
+#define WRED_PM_AVG_QUEUE_SIZE_S 0
+
+#define REG_PORT_WRED_QUEUE_CTRL_0__4 0x0840
+#define REG_PORT_WRED_QUEUE_CTRL_1__4 0x0844
+
+#define REG_PORT_WRED_QUEUE_PMON__4 0x0848
+
+#define WRED_RANDOM_DROP_ENABLE BIT(31)
+#define WRED_PMON_FLUSH BIT(30)
+#define WRED_DROP_GYR_DISABLE BIT(29)
+#define WRED_DROP_YR_DISABLE BIT(28)
+#define WRED_DROP_R_DISABLE BIT(27)
+#define WRED_DROP_ALL BIT(26)
+#define WRED_PMON_M (BIT(24) - 1)
+
+/* 9 - Shaping */
+
+#define REG_PORT_MTI_QUEUE_INDEX__4 0x0900
+
+#define REG_PORT_MTI_QUEUE_CTRL_0__4 0x0904
+
+#define MTI_PVID_REPLACE BIT(0)
+
+#define REG_PORT_MTI_QUEUE_CTRL_0 0x0914
+
+#define MTI_SCHEDULE_MODE_M 0x3
+#define MTI_SCHEDULE_MODE_S 6
+#define MTI_SCHEDULE_STRICT_PRIO 0
+#define MTI_SCHEDULE_WRR 2
+#define MTI_SHAPING_M 0x3
+#define MTI_SHAPING_S 4
+#define MTI_SHAPING_OFF 0
+#define MTI_SHAPING_SRP 1
+#define MTI_SHAPING_TIME_AWARE 2
+
+#define REG_PORT_MTI_QUEUE_CTRL_1 0x0915
+
+#define MTI_TX_RATIO_M (BIT(7) - 1)
+
+#define REG_PORT_MTI_QUEUE_CTRL_2__2 0x0916
+#define REG_PORT_MTI_HI_WATER_MARK 0x0916
+#define REG_PORT_MTI_QUEUE_CTRL_3__2 0x0918
+#define REG_PORT_MTI_LO_WATER_MARK 0x0918
+#define REG_PORT_MTI_QUEUE_CTRL_4__2 0x091A
+#define REG_PORT_MTI_CREDIT_INCREMENT 0x091A
+
+/* A - QM */
+
+#define REG_PORT_QM_CTRL__4 0x0A00
+
+#define PORT_QM_DROP_PRIO_M 0x3
+
+#define REG_PORT_VLAN_MEMBERSHIP__4 0x0A04
+
+#define REG_PORT_QM_QUEUE_INDEX__4 0x0A08
+
+#define PORT_QM_QUEUE_INDEX_S 24
+#define PORT_QM_BURST_SIZE_S 16
+#define PORT_QM_MIN_RESV_SPACE_M (BIT(11) - 1)
+
+#define REG_PORT_QM_WATER_MARK__4 0x0A0C
+
+#define PORT_QM_HI_WATER_MARK_S 16
+#define PORT_QM_LO_WATER_MARK_S 0
+#define PORT_QM_WATER_MARK_M (BIT(11) - 1)
+
+#define REG_PORT_QM_TX_CNT_0__4 0x0A10
+
+#define PORT_QM_TX_CNT_USED_S 0
+#define PORT_QM_TX_CNT_M (BIT(11) - 1)
+
+#define REG_PORT_QM_TX_CNT_1__4 0x0A14
+
+#define PORT_QM_TX_CNT_CALCULATED_S 16
+#define PORT_QM_TX_CNT_AVAIL_S 0
+
+/* B - LUE */
+#define REG_PORT_LUE_CTRL 0x0B00
+
+#define PORT_VLAN_LOOKUP_VID_0 BIT(7)
+#define PORT_INGRESS_FILTER BIT(6)
+#define PORT_DISCARD_NON_VID BIT(5)
+#define PORT_MAC_BASED_802_1X BIT(4)
+#define PORT_SRC_ADDR_FILTER BIT(3)
+
+#define REG_PORT_LUE_MSTP_INDEX 0x0B01
+
+#define REG_PORT_LUE_MSTP_STATE 0x0B04
+
+#define PORT_TX_ENABLE BIT(2)
+#define PORT_RX_ENABLE BIT(1)
+#define PORT_LEARN_DISABLE BIT(0)
+
+/* C - PTP */
+
+#define REG_PTP_PORT_RX_DELAY__2 0x0C00
+#define REG_PTP_PORT_TX_DELAY__2 0x0C02
+#define REG_PTP_PORT_ASYM_DELAY__2 0x0C04
+
+#define REG_PTP_PORT_XDELAY_TS 0x0C08
+#define REG_PTP_PORT_XDELAY_TS_H 0x0C08
+#define REG_PTP_PORT_XDELAY_TS_L 0x0C0A
+
+#define REG_PTP_PORT_SYNC_TS 0x0C0C
+#define REG_PTP_PORT_SYNC_TS_H 0x0C0C
+#define REG_PTP_PORT_SYNC_TS_L 0x0C0E
+
+#define REG_PTP_PORT_PDRESP_TS 0x0C10
+#define REG_PTP_PORT_PDRESP_TS_H 0x0C10
+#define REG_PTP_PORT_PDRESP_TS_L 0x0C12
+
+#define REG_PTP_PORT_TX_INT_STATUS__2 0x0C14
+#define REG_PTP_PORT_TX_INT_ENABLE__2 0x0C16
+
+#define PTP_PORT_SYNC_INT BIT(15)
+#define PTP_PORT_XDELAY_REQ_INT BIT(14)
+#define PTP_PORT_PDELAY_RESP_INT BIT(13)
+
+#define REG_PTP_PORT_LINK_DELAY__4 0x0C18
+
+#define PRIO_QUEUES 4
+#define RX_PRIO_QUEUES 8
+
+#define KS_PRIO_IN_REG 2
+
+#define TOTAL_PORT_NUM 7
+
+#define KSZ9477_COUNTER_NUM 0x20
+#define TOTAL_KSZ9477_COUNTER_NUM (KSZ9477_COUNTER_NUM + 2 + 2)
+
+#define SWITCH_COUNTER_NUM KSZ9477_COUNTER_NUM
+#define TOTAL_SWITCH_COUNTER_NUM TOTAL_KSZ9477_COUNTER_NUM
+
+#define P_BCAST_STORM_CTRL REG_PORT_MAC_CTRL_0
+#define P_PRIO_CTRL REG_PORT_MRI_PRIO_CTRL
+#define P_MIRROR_CTRL REG_PORT_MRI_MIRROR_CTRL
+#define P_STP_CTRL REG_PORT_LUE_MSTP_STATE
+#define P_PHY_CTRL REG_PORT_PHY_CTRL
+#define P_NEG_RESTART_CTRL REG_PORT_PHY_CTRL
+#define P_LINK_STATUS REG_PORT_PHY_STATUS
+#define P_SPEED_STATUS REG_PORT_PHY_PHY_CTRL
+#define P_RATE_LIMIT_CTRL REG_PORT_MAC_IN_RATE_LIMIT
+
+#define S_LINK_AGING_CTRL REG_SW_LUE_CTRL_1
+#define S_MIRROR_CTRL REG_SW_MRI_CTRL_0
+#define S_REPLACE_VID_CTRL REG_SW_MAC_CTRL_2
+#define S_802_1P_PRIO_CTRL REG_SW_MAC_802_1P_MAP_0
+#define S_TOS_PRIO_CTRL REG_SW_MAC_TOS_PRIO_0
+#define S_FLUSH_TABLE_CTRL REG_SW_LUE_CTRL_1
+
+#define SW_FLUSH_DYN_MAC_TABLE SW_FLUSH_MSTP_TABLE
+
+#define MAX_TIMESTAMP_UNIT 2
+#define MAX_TRIG_UNIT 3
+#define MAX_TIMESTAMP_EVENT_UNIT 8
+#define MAX_GPIO 4
+
+#define PTP_TRIG_UNIT_M (BIT(MAX_TRIG_UNIT) - 1)
+#define PTP_TS_UNIT_M (BIT(MAX_TIMESTAMP_UNIT) - 1)
+
+/* Driver set switch broadcast storm protection at 10% rate. */
+#define BROADCAST_STORM_PROT_RATE 10
+
+/* 148,800 frames * 67 ms / 100 */
+#define BROADCAST_STORM_VALUE 9969
+
+#endif /* KSZ9477_REGS_H */
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
new file mode 100644
index 000000000000..b313ecdf2919
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -0,0 +1,1279 @@
+/*
+ * Microchip switch driver main logic
+ *
+ * Copyright (C) 2017
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_data/microchip-ksz.h>
+#include <linux/phy.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "ksz_priv.h"
+
+static const struct {
+ int index;
+ char string[ETH_GSTRING_LEN];
+} mib_names[TOTAL_SWITCH_COUNTER_NUM] = {
+ { 0x00, "rx_hi" },
+ { 0x01, "rx_undersize" },
+ { 0x02, "rx_fragments" },
+ { 0x03, "rx_oversize" },
+ { 0x04, "rx_jabbers" },
+ { 0x05, "rx_symbol_err" },
+ { 0x06, "rx_crc_err" },
+ { 0x07, "rx_align_err" },
+ { 0x08, "rx_mac_ctrl" },
+ { 0x09, "rx_pause" },
+ { 0x0A, "rx_bcast" },
+ { 0x0B, "rx_mcast" },
+ { 0x0C, "rx_ucast" },
+ { 0x0D, "rx_64_or_less" },
+ { 0x0E, "rx_65_127" },
+ { 0x0F, "rx_128_255" },
+ { 0x10, "rx_256_511" },
+ { 0x11, "rx_512_1023" },
+ { 0x12, "rx_1024_1522" },
+ { 0x13, "rx_1523_2000" },
+ { 0x14, "rx_2001" },
+ { 0x15, "tx_hi" },
+ { 0x16, "tx_late_col" },
+ { 0x17, "tx_pause" },
+ { 0x18, "tx_bcast" },
+ { 0x19, "tx_mcast" },
+ { 0x1A, "tx_ucast" },
+ { 0x1B, "tx_deferred" },
+ { 0x1C, "tx_total_col" },
+ { 0x1D, "tx_exc_col" },
+ { 0x1E, "tx_single_col" },
+ { 0x1F, "tx_mult_col" },
+ { 0x80, "rx_total" },
+ { 0x81, "tx_total" },
+ { 0x82, "rx_discards" },
+ { 0x83, "tx_discards" },
+};
+
+static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
+{
+ u8 data;
+
+ ksz_read8(dev, addr, &data);
+ if (set)
+ data |= bits;
+ else
+ data &= ~bits;
+ ksz_write8(dev, addr, data);
+}
+
+static void ksz_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set)
+{
+ u32 data;
+
+ ksz_read32(dev, addr, &data);
+ if (set)
+ data |= bits;
+ else
+ data &= ~bits;
+ ksz_write32(dev, addr, data);
+}
+
+static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
+ bool set)
+{
+ u32 addr;
+ u8 data;
+
+ addr = PORT_CTRL_ADDR(port, offset);
+ ksz_read8(dev, addr, &data);
+
+ if (set)
+ data |= bits;
+ else
+ data &= ~bits;
+
+ ksz_write8(dev, addr, data);
+}
+
+static void ksz_port_cfg32(struct ksz_device *dev, int port, int offset,
+ u32 bits, bool set)
+{
+ u32 addr;
+ u32 data;
+
+ addr = PORT_CTRL_ADDR(port, offset);
+ ksz_read32(dev, addr, &data);
+
+ if (set)
+ data |= bits;
+ else
+ data &= ~bits;
+
+ ksz_write32(dev, addr, data);
+}
+
+static int wait_vlan_ctrl_ready(struct ksz_device *dev, u32 waiton, int timeout)
+{
+ u8 data;
+
+ do {
+ ksz_read8(dev, REG_SW_VLAN_CTRL, &data);
+ if (!(data & waiton))
+ break;
+ usleep_range(1, 10);
+ } while (timeout-- > 0);
+
+ if (timeout <= 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int get_vlan_table(struct dsa_switch *ds, u16 vid, u32 *vlan_table)
+{
+ struct ksz_device *dev = ds->priv;
+ int ret;
+
+ mutex_lock(&dev->vlan_mutex);
+
+ ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+ ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);
+
+ /* wait to be cleared */
+ ret = wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
+ if (ret < 0) {
+ dev_dbg(dev->dev, "Failed to read vlan table\n");
+ goto exit;
+ }
+
+ ksz_read32(dev, REG_SW_VLAN_ENTRY__4, &vlan_table[0]);
+ ksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, &vlan_table[1]);
+ ksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4, &vlan_table[2]);
+
+ ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+
+exit:
+ mutex_unlock(&dev->vlan_mutex);
+
+ return ret;
+}
+
+static int set_vlan_table(struct dsa_switch *ds, u16 vid, u32 *vlan_table)
+{
+ struct ksz_device *dev = ds->priv;
+ int ret;
+
+ mutex_lock(&dev->vlan_mutex);
+
+ ksz_write32(dev, REG_SW_VLAN_ENTRY__4, vlan_table[0]);
+ ksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, vlan_table[1]);
+ ksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_table[2]);
+
+ ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+ ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);
+
+ /* wait to be cleared */
+ ret = wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
+ if (ret < 0) {
+ dev_dbg(dev->dev, "Failed to write vlan table\n");
+ goto exit;
+ }
+
+ ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+
+ /* update vlan cache table */
+ dev->vlan_cache[vid].table[0] = vlan_table[0];
+ dev->vlan_cache[vid].table[1] = vlan_table[1];
+ dev->vlan_cache[vid].table[2] = vlan_table[2];
+
+exit:
+ mutex_unlock(&dev->vlan_mutex);
+
+ return ret;
+}
+
+static void read_table(struct dsa_switch *ds, u32 *table)
+{
+ struct ksz_device *dev = ds->priv;
+
+ ksz_read32(dev, REG_SW_ALU_VAL_A, &table[0]);
+ ksz_read32(dev, REG_SW_ALU_VAL_B, &table[1]);
+ ksz_read32(dev, REG_SW_ALU_VAL_C, &table[2]);
+ ksz_read32(dev, REG_SW_ALU_VAL_D, &table[3]);
+}
+
+static void write_table(struct dsa_switch *ds, u32 *table)
+{
+ struct ksz_device *dev = ds->priv;
+
+ ksz_write32(dev, REG_SW_ALU_VAL_A, table[0]);
+ ksz_write32(dev, REG_SW_ALU_VAL_B, table[1]);
+ ksz_write32(dev, REG_SW_ALU_VAL_C, table[2]);
+ ksz_write32(dev, REG_SW_ALU_VAL_D, table[3]);
+}
+
+static int wait_alu_ready(struct ksz_device *dev, u32 waiton, int timeout)
+{
+ u32 data;
+
+ do {
+ ksz_read32(dev, REG_SW_ALU_CTRL__4, &data);
+ if (!(data & waiton))
+ break;
+ usleep_range(1, 10);
+ } while (timeout-- > 0);
+
+ if (timeout <= 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int wait_alu_sta_ready(struct ksz_device *dev, u32 waiton, int timeout)
+{
+ u32 data;
+
+ do {
+ ksz_read32(dev, REG_SW_ALU_STAT_CTRL__4, &data);
+ if (!(data & waiton))
+ break;
+ usleep_range(1, 10);
+ } while (timeout-- > 0);
+
+ if (timeout <= 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int ksz_reset_switch(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 data8;
+ u16 data16;
+ u32 data32;
+
+ /* reset switch */
+ ksz_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
+
+ /* turn off SPI DO Edge select */
+ ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
+ data8 &= ~SPI_AUTO_EDGE_DETECTION;
+ ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
+
+ /* default configuration */
+ ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
+ data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
+ SW_SRC_ADDR_FILTER | SW_FLUSH_STP_TABLE | SW_FLUSH_MSTP_TABLE;
+ ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+
+ /* disable interrupts */
+ ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
+ ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0x7F);
+ ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
+
+ /* set broadcast storm protection 10% rate */
+ ksz_read16(dev, REG_SW_MAC_CTRL_2, &data16);
+ data16 &= ~BROADCAST_STORM_RATE;
+ data16 |= (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
+ ksz_write16(dev, REG_SW_MAC_CTRL_2, data16);
+
+ return 0;
+}
+
+static void port_setup(struct ksz_device *dev, int port, bool cpu_port)
+{
+ u8 data8;
+ u16 data16;
+
+ /* enable tag tail for host port */
+ if (cpu_port)
+ ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE,
+ true);
+
+ ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, false);
+
+ /* set back pressure */
+ ksz_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, true);
+
+ /* set flow control */
+ ksz_port_cfg(dev, port, REG_PORT_CTRL_0,
+ PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, true);
+
+ /* enable broadcast storm limit */
+ ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
+
+ /* disable DiffServ priority */
+ ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false);
+
+ /* replace priority */
+ ksz_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,
+ false);
+ ksz_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,
+ MTI_PVID_REPLACE, false);
+
+ /* enable 802.1p priority */
+ ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
+
+ /* configure MAC to 1G & RGMII mode */
+ ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
+ data8 |= PORT_RGMII_ID_EG_ENABLE;
+ data8 &= ~PORT_MII_NOT_1GBIT;
+ data8 &= ~PORT_MII_SEL_M;
+ data8 |= PORT_RGMII_SEL;
+ ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);
+
+ /* clear pending interrupts */
+ ksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16);
+}
+
+static void ksz_config_cpu_port(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ int i;
+
+ ds->num_ports = dev->port_cnt;
+
+ for (i = 0; i < ds->num_ports; i++) {
+ if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
+ dev->cpu_port = i;
+
+ /* enable cpu port */
+ port_setup(dev, i, true);
+ }
+ }
+}
+
+static int ksz_setup(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ int ret = 0;
+
+ dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+ dev->num_vlans, GFP_KERNEL);
+ if (!dev->vlan_cache)
+ return -ENOMEM;
+
+ ret = ksz_reset_switch(ds);
+ if (ret) {
+ dev_err(ds->dev, "failed to reset switch\n");
+ return ret;
+ }
+
+ /* accept packet up to 2000bytes */
+ ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true);
+
+ ksz_config_cpu_port(ds);
+
+ ksz_cfg(dev, REG_SW_MAC_CTRL_1, MULTICAST_STORM_DISABLE, true);
+
+ /* queue based egress rate limit */
+ ksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);
+
+ /* start switch */
+ ksz_cfg(dev, REG_SW_OPERATION, SW_START, true);
+
+ return 0;
+}
+
+static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds)
+{
+ return DSA_TAG_PROTO_KSZ;
+}
+
+static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg)
+{
+ struct ksz_device *dev = ds->priv;
+ u16 val = 0;
+
+ ksz_pread16(dev, addr, 0x100 + (reg << 1), &val);
+
+ return val;
+}
+
+static int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)
+{
+ struct ksz_device *dev = ds->priv;
+
+ ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val);
+
+ return 0;
+}
+
+static int ksz_enable_port(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct ksz_device *dev = ds->priv;
+
+ /* setup slave port */
+ port_setup(dev, port, false);
+
+ return 0;
+}
+
+static void ksz_disable_port(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct ksz_device *dev = ds->priv;
+
+ /* there is no port disable */
+ ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, true);
+}
+
+static int ksz_sset_count(struct dsa_switch *ds)
+{
+ return TOTAL_SWITCH_COUNTER_NUM;
+}
+
+static void ksz_get_strings(struct dsa_switch *ds, int port, uint8_t *buf)
+{
+ int i;
+
+ for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
+ memcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string,
+ ETH_GSTRING_LEN);
+ }
+}
+
+static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *buf)
+{
+ struct ksz_device *dev = ds->priv;
+ int i;
+ u32 data;
+ int timeout;
+
+ mutex_lock(&dev->stats_mutex);
+
+ for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
+ data = MIB_COUNTER_READ;
+ data |= ((mib_names[i].index & 0xFF) << MIB_COUNTER_INDEX_S);
+ ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
+
+ timeout = 1000;
+ do {
+ ksz_pread32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
+ &data);
+ usleep_range(1, 10);
+ if (!(data & MIB_COUNTER_READ))
+ break;
+ } while (timeout-- > 0);
+
+ /* failed to read MIB. get out of loop */
+ if (!timeout) {
+ dev_dbg(dev->dev, "Failed to get MIB\n");
+ break;
+ }
+
+ /* count resets upon read */
+ ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data);
+
+ dev->mib_value[i] += (uint64_t)data;
+ buf[i] = dev->mib_value[i];
+ }
+
+ mutex_unlock(&dev->stats_mutex);
+}
+
+static void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 data;
+
+ ksz_pread8(dev, port, P_STP_CTRL, &data);
+ data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ data |= PORT_LEARN_DISABLE;
+ break;
+ case BR_STATE_LISTENING:
+ data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+ break;
+ case BR_STATE_LEARNING:
+ data |= PORT_RX_ENABLE;
+ break;
+ case BR_STATE_FORWARDING:
+ data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+ break;
+ case BR_STATE_BLOCKING:
+ data |= PORT_LEARN_DISABLE;
+ break;
+ default:
+ dev_err(ds->dev, "invalid STP state: %d\n", state);
+ return;
+ }
+
+ ksz_pwrite8(dev, port, P_STP_CTRL, data);
+}
+
+static void ksz_port_fast_age(struct dsa_switch *ds, int port)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 data8;
+
+ ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
+ data8 |= SW_FAST_AGING;
+ ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+
+ data8 &= ~SW_FAST_AGING;
+ ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+}
+
+static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag)
+{
+ struct ksz_device *dev = ds->priv;
+
+ if (flag) {
+ ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
+ PORT_VLAN_LOOKUP_VID_0, true);
+ ksz_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, true);
+ ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true);
+ } else {
+ ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false);
+ ksz_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, false);
+ ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
+ PORT_VLAN_LOOKUP_VID_0, false);
+ }
+
+ return 0;
+}
+
+static int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
+{
+ /* nothing needed */
+
+ return 0;
+}
+
+static void ksz_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
+{
+ struct ksz_device *dev = ds->priv;
+ u32 vlan_table[3];
+ u16 vid;
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+ if (get_vlan_table(ds, vid, vlan_table)) {
+ dev_dbg(dev->dev, "Failed to get vlan table\n");
+ return;
+ }
+
+ vlan_table[0] = VLAN_VALID | (vid & VLAN_FID_M);
+ if (untagged)
+ vlan_table[1] |= BIT(port);
+ else
+ vlan_table[1] &= ~BIT(port);
+ vlan_table[1] &= ~(BIT(dev->cpu_port));
+
+ vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
+
+ if (set_vlan_table(ds, vid, vlan_table)) {
+ dev_dbg(dev->dev, "Failed to set vlan table\n");
+ return;
+ }
+
+ /* change PVID */
+ if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+ ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid);
+ }
+}
+
+static int ksz_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct ksz_device *dev = ds->priv;
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ u32 vlan_table[3];
+ u16 vid;
+ u16 pvid;
+
+ ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
+ pvid = pvid & 0xFFF;
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+ if (get_vlan_table(ds, vid, vlan_table)) {
+ dev_dbg(dev->dev, "Failed to get vlan table\n");
+ return -ETIMEDOUT;
+ }
+
+ vlan_table[2] &= ~BIT(port);
+
+ if (pvid == vid)
+ pvid = 1;
+
+ if (untagged)
+ vlan_table[1] &= ~BIT(port);
+
+ if (set_vlan_table(ds, vid, vlan_table)) {
+ dev_dbg(dev->dev, "Failed to set vlan table\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
+
+ return 0;
+}
+
+static int ksz_port_vlan_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_vlan *vlan,
+ switchdev_obj_dump_cb_t *cb)
+{
+ struct ksz_device *dev = ds->priv;
+ u16 vid;
+ u16 data;
+ struct vlan_table *vlan_cache;
+ int err = 0;
+
+ mutex_lock(&dev->vlan_mutex);
+
+ /* use dev->vlan_cache due to lack of searching valid vlan entry */
+ for (vid = vlan->vid_begin; vid < dev->num_vlans; vid++) {
+ vlan_cache = &dev->vlan_cache[vid];
+
+ if (!(vlan_cache->table[0] & VLAN_VALID))
+ continue;
+
+ vlan->vid_begin = vid;
+ vlan->vid_end = vid;
+ vlan->flags = 0;
+ if (vlan_cache->table[2] & BIT(port)) {
+ if (vlan_cache->table[1] & BIT(port))
+ vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+ ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &data);
+ if (vid == (data & 0xFFFFF))
+ vlan->flags |= BRIDGE_VLAN_INFO_PVID;
+
+ err = cb(&vlan->obj);
+ if (err)
+ break;
+ }
+ }
+
+ mutex_unlock(&dev->vlan_mutex);
+
+ return err;
+}
+
+static int ksz_port_fdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
+{
+ /* nothing needed */
+
+ return 0;
+}
+
+struct alu_struct {
+ /* entry 1 */
+ u8 is_static:1;
+ u8 is_src_filter:1;
+ u8 is_dst_filter:1;
+ u8 prio_age:3;
+ u32 _reserv_0_1:23;
+ u8 mstp:3;
+ /* entry 2 */
+ u8 is_override:1;
+ u8 is_use_fid:1;
+ u32 _reserv_1_1:23;
+ u8 port_forward:7;
+ /* entry 3 & 4*/
+ u32 _reserv_2_1:9;
+ u8 fid:7;
+ u8 mac[ETH_ALEN];
+};
+
+static void ksz_port_fdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
+{
+ struct ksz_device *dev = ds->priv;
+ u32 alu_table[4];
+ u32 data;
+
+ mutex_lock(&dev->alu_mutex);
+
+ /* find any entry with mac & vid */
+ data = fdb->vid << ALU_FID_INDEX_S;
+ data |= ((fdb->addr[0] << 8) | fdb->addr[1]);
+ ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+
+ data = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16));
+ data |= ((fdb->addr[4] << 8) | fdb->addr[5]);
+ ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+
+ /* start read operation */
+ ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
+
+ /* wait to be finished */
+ if (wait_alu_ready(dev, ALU_START, 1000) < 0) {
+ dev_dbg(dev->dev, "Failed to read ALU\n");
+ goto exit;
+ }
+
+ /* read ALU entry */
+ read_table(ds, alu_table);
+
+ /* update ALU entry */
+ alu_table[0] = ALU_V_STATIC_VALID;
+ alu_table[1] |= BIT(port);
+ if (fdb->vid)
+ alu_table[1] |= ALU_V_USE_FID;
+ alu_table[2] = (fdb->vid << ALU_V_FID_S);
+ alu_table[2] |= ((fdb->addr[0] << 8) | fdb->addr[1]);
+ alu_table[3] = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16));
+ alu_table[3] |= ((fdb->addr[4] << 8) | fdb->addr[5]);
+
+ write_table(ds, alu_table);
+
+ ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
+
+ /* wait to be finished */
+ if (wait_alu_ready(dev, ALU_START, 1000) < 0)
+ dev_dbg(dev->dev, "Failed to read ALU\n");
+
+exit:
+ mutex_unlock(&dev->alu_mutex);
+}
+
+static int ksz_port_fdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb)
+{
+ struct ksz_device *dev = ds->priv;
+ u32 alu_table[4];
+ u32 data;
+ int ret = 0;
+
+ mutex_lock(&dev->alu_mutex);
+
+ /* read any entry with mac & vid */
+ data = fdb->vid << ALU_FID_INDEX_S;
+ data |= ((fdb->addr[0] << 8) | fdb->addr[1]);
+ ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+
+ data = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16));
+ data |= ((fdb->addr[4] << 8) | fdb->addr[5]);
+ ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+
+ /* start read operation */
+ ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
+
+ /* wait to be finished */
+ ret = wait_alu_ready(dev, ALU_START, 1000);
+ if (ret < 0) {
+ dev_dbg(dev->dev, "Failed to read ALU\n");
+ goto exit;
+ }
+
+ ksz_read32(dev, REG_SW_ALU_VAL_A, &alu_table[0]);
+ if (alu_table[0] & ALU_V_STATIC_VALID) {
+ ksz_read32(dev, REG_SW_ALU_VAL_B, &alu_table[1]);
+ ksz_read32(dev, REG_SW_ALU_VAL_C, &alu_table[2]);
+ ksz_read32(dev, REG_SW_ALU_VAL_D, &alu_table[3]);
+
+ /* clear forwarding port */
+ alu_table[2] &= ~BIT(port);
+
+ /* if there is no port to forward, clear table */
+ if ((alu_table[2] & ALU_V_PORT_MAP) == 0) {
+ alu_table[0] = 0;
+ alu_table[1] = 0;
+ alu_table[2] = 0;
+ alu_table[3] = 0;
+ }
+ } else {
+ alu_table[0] = 0;
+ alu_table[1] = 0;
+ alu_table[2] = 0;
+ alu_table[3] = 0;
+ }
+
+ write_table(ds, alu_table);
+
+ ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
+
+ /* wait to be finished */
+ ret = wait_alu_ready(dev, ALU_START, 1000);
+ if (ret < 0)
+ dev_dbg(dev->dev, "Failed to write ALU\n");
+
+exit:
+ mutex_unlock(&dev->alu_mutex);
+
+ return ret;
+}
+
+static void convert_alu(struct alu_struct *alu, u32 *alu_table)
+{
+ alu->is_static = !!(alu_table[0] & ALU_V_STATIC_VALID);
+ alu->is_src_filter = !!(alu_table[0] & ALU_V_SRC_FILTER);
+ alu->is_dst_filter = !!(alu_table[0] & ALU_V_DST_FILTER);
+ alu->prio_age = (alu_table[0] >> ALU_V_PRIO_AGE_CNT_S) &
+ ALU_V_PRIO_AGE_CNT_M;
+ alu->mstp = alu_table[0] & ALU_V_MSTP_M;
+
+ alu->is_override = !!(alu_table[1] & ALU_V_OVERRIDE);
+ alu->is_use_fid = !!(alu_table[1] & ALU_V_USE_FID);
+ alu->port_forward = alu_table[1] & ALU_V_PORT_MAP;
+
+ alu->fid = (alu_table[2] >> ALU_V_FID_S) & ALU_V_FID_M;
+
+ alu->mac[0] = (alu_table[2] >> 8) & 0xFF;
+ alu->mac[1] = alu_table[2] & 0xFF;
+ alu->mac[2] = (alu_table[3] >> 24) & 0xFF;
+ alu->mac[3] = (alu_table[3] >> 16) & 0xFF;
+ alu->mac[4] = (alu_table[3] >> 8) & 0xFF;
+ alu->mac[5] = alu_table[3] & 0xFF;
+}
+
+static int ksz_port_fdb_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_fdb *fdb,
+ switchdev_obj_dump_cb_t *cb)
+{
+ struct ksz_device *dev = ds->priv;
+ int ret = 0;
+ u32 data;
+ u32 alu_table[4];
+ struct alu_struct alu;
+ int timeout;
+
+ mutex_lock(&dev->alu_mutex);
+
+ /* start ALU search */
+ ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_START | ALU_SEARCH);
+
+ do {
+ timeout = 1000;
+ do {
+ ksz_read32(dev, REG_SW_ALU_CTRL__4, &data);
+ if ((data & ALU_VALID) || !(data & ALU_START))
+ break;
+ usleep_range(1, 10);
+ } while (timeout-- > 0);
+
+ if (!timeout) {
+ dev_dbg(dev->dev, "Failed to search ALU\n");
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+
+ /* read ALU table */
+ read_table(ds, alu_table);
+
+ convert_alu(&alu, alu_table);
+
+ if (alu.port_forward & BIT(port)) {
+ fdb->vid = alu.fid;
+ if (alu.is_static)
+ fdb->ndm_state = NUD_NOARP;
+ else
+ fdb->ndm_state = NUD_REACHABLE;
+ ether_addr_copy(fdb->addr, alu.mac);
+
+ ret = cb(&fdb->obj);
+ if (ret)
+ goto exit;
+ }
+ } while (data & ALU_START);
+
+exit:
+
+ /* stop ALU search */
+ ksz_write32(dev, REG_SW_ALU_CTRL__4, 0);
+
+ mutex_unlock(&dev->alu_mutex);
+
+ return ret;
+}
+
+static int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct switchdev_trans *trans)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void ksz_port_mdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct switchdev_trans *trans)
+{
+ struct ksz_device *dev = ds->priv;
+ u32 static_table[4];
+ u32 data;
+ int index;
+ u32 mac_hi, mac_lo;
+
+ mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+ mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+ mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+ mutex_lock(&dev->alu_mutex);
+
+ for (index = 0; index < dev->num_statics; index++) {
+ /* find empty slot first */
+ data = (index << ALU_STAT_INDEX_S) |
+ ALU_STAT_READ | ALU_STAT_START;
+ ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+ /* wait to be finished */
+ if (wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0) {
+ dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+ goto exit;
+ }
+
+ /* read ALU static table */
+ read_table(ds, static_table);
+
+ if (static_table[0] & ALU_V_STATIC_VALID) {
+ /* check this has same vid & mac address */
+ if (((static_table[2] >> ALU_V_FID_S) == (mdb->vid)) &&
+ ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+ (static_table[3] == mac_lo)) {
+ /* found matching one */
+ break;
+ }
+ } else {
+ /* found empty one */
+ break;
+ }
+ }
+
+ /* no available entry */
+ if (index == dev->num_statics)
+ goto exit;
+
+ /* add entry */
+ static_table[0] = ALU_V_STATIC_VALID;
+ static_table[1] |= BIT(port);
+ if (mdb->vid)
+ static_table[1] |= ALU_V_USE_FID;
+ static_table[2] = (mdb->vid << ALU_V_FID_S);
+ static_table[2] |= mac_hi;
+ static_table[3] = mac_lo;
+
+ write_table(ds, static_table);
+
+ data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+ ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+ /* wait to be finished */
+ if (wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0)
+ dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+ mutex_unlock(&dev->alu_mutex);
+}
+
+static int ksz_port_mdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb)
+{
+ struct ksz_device *dev = ds->priv;
+ u32 static_table[4];
+ u32 data;
+ int index;
+ int ret = 0;
+ u32 mac_hi, mac_lo;
+
+ mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+ mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+ mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+ mutex_lock(&dev->alu_mutex);
+
+ for (index = 0; index < dev->num_statics; index++) {
+ /* find empty slot first */
+ data = (index << ALU_STAT_INDEX_S) |
+ ALU_STAT_READ | ALU_STAT_START;
+ ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+ /* wait to be finished */
+ ret = wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
+ if (ret < 0) {
+ dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+ goto exit;
+ }
+
+ /* read ALU static table */
+ read_table(ds, static_table);
+
+ if (static_table[0] & ALU_V_STATIC_VALID) {
+ /* check this has same vid & mac address */
+
+ if (((static_table[2] >> ALU_V_FID_S) == (mdb->vid)) &&
+ ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+ (static_table[3] == mac_lo)) {
+ /* found matching one */
+ break;
+ }
+ }
+ }
+
+ /* no available entry */
+ if (index == dev->num_statics) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* clear port */
+ static_table[1] &= ~BIT(port);
+
+ if ((static_table[1] & ALU_V_PORT_MAP) == 0) {
+ /* delete entry */
+ static_table[0] = 0;
+ static_table[1] = 0;
+ static_table[2] = 0;
+ static_table[3] = 0;
+ }
+
+ write_table(ds, static_table);
+
+ data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+ ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+ /* wait to be finished */
+ ret = wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
+ if (ret < 0)
+ dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+ mutex_unlock(&dev->alu_mutex);
+
+ return ret;
+}
+
+static int ksz_port_mdb_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_mdb *mdb,
+ switchdev_obj_dump_cb_t *cb)
+{
+ /* this is not called by switch layer */
+ return 0;
+}
+
+static int ksz_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+ bool ingress)
+{
+ struct ksz_device *dev = ds->priv;
+
+ if (ingress)
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
+ else
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
+
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
+
+ /* configure mirror port */
+ ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+ PORT_MIRROR_SNIFFER, true);
+
+ ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+
+ return 0;
+}
+
+static void ksz_port_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 data;
+
+ if (mirror->ingress)
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
+ else
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
+
+ ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
+
+ if (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX)))
+ ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+ PORT_MIRROR_SNIFFER, false);
+}
+
+static const struct dsa_switch_ops ksz_switch_ops = {
+ .get_tag_protocol = ksz_get_tag_protocol,
+ .setup = ksz_setup,
+ .phy_read = ksz_phy_read16,
+ .phy_write = ksz_phy_write16,
+ .port_enable = ksz_enable_port,
+ .port_disable = ksz_disable_port,
+ .get_strings = ksz_get_strings,
+ .get_ethtool_stats = ksz_get_ethtool_stats,
+ .get_sset_count = ksz_sset_count,
+ .port_stp_state_set = ksz_port_stp_state_set,
+ .port_fast_age = ksz_port_fast_age,
+ .port_vlan_filtering = ksz_port_vlan_filtering,
+ .port_vlan_prepare = ksz_port_vlan_prepare,
+ .port_vlan_add = ksz_port_vlan_add,
+ .port_vlan_del = ksz_port_vlan_del,
+ .port_vlan_dump = ksz_port_vlan_dump,
+ .port_fdb_prepare = ksz_port_fdb_prepare,
+ .port_fdb_dump = ksz_port_fdb_dump,
+ .port_fdb_add = ksz_port_fdb_add,
+ .port_fdb_del = ksz_port_fdb_del,
+ .port_mdb_prepare = ksz_port_mdb_prepare,
+ .port_mdb_add = ksz_port_mdb_add,
+ .port_mdb_del = ksz_port_mdb_del,
+ .port_mdb_dump = ksz_port_mdb_dump,
+ .port_mirror_add = ksz_port_mirror_add,
+ .port_mirror_del = ksz_port_mirror_del,
+};
+
+struct ksz_chip_data {
+ u32 chip_id;
+ const char *dev_name;
+ int num_vlans;
+ int num_alus;
+ int num_statics;
+ int cpu_ports;
+ int port_cnt;
+};
+
+static const struct ksz_chip_data ksz_switch_chips[] = {
+ {
+ .chip_id = 0x00947700,
+ .dev_name = "KSZ9477",
+ .num_vlans = 4096,
+ .num_alus = 4096,
+ .num_statics = 16,
+ .cpu_ports = 0x7F, /* can be configured as cpu port */
+ .port_cnt = 7, /* total physical port count */
+ },
+};
+
+static int ksz_switch_init(struct ksz_device *dev)
+{
+ int i;
+
+ mutex_init(&dev->reg_mutex);
+ mutex_init(&dev->stats_mutex);
+ mutex_init(&dev->alu_mutex);
+ mutex_init(&dev->vlan_mutex);
+
+ dev->ds->ops = &ksz_switch_ops;
+
+ for (i = 0; i < ARRAY_SIZE(ksz_switch_chips); i++) {
+ const struct ksz_chip_data *chip = &ksz_switch_chips[i];
+
+ if (dev->chip_id == chip->chip_id) {
+ dev->name = chip->dev_name;
+ dev->num_vlans = chip->num_vlans;
+ dev->num_alus = chip->num_alus;
+ dev->num_statics = chip->num_statics;
+ dev->port_cnt = chip->port_cnt;
+ dev->cpu_ports = chip->cpu_ports;
+
+ break;
+ }
+ }
+
+ /* no switch found */
+ if (!dev->port_cnt)
+ return -ENODEV;
+
+ return 0;
+}
+
+struct ksz_device *ksz_switch_alloc(struct device *base,
+ const struct ksz_io_ops *ops,
+ void *priv)
+{
+ struct dsa_switch *ds;
+ struct ksz_device *swdev;
+
+ ds = dsa_switch_alloc(base, DSA_MAX_PORTS);
+ if (!ds)
+ return NULL;
+
+ swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL);
+ if (!swdev)
+ return NULL;
+
+ ds->priv = swdev;
+ swdev->dev = base;
+
+ swdev->ds = ds;
+ swdev->priv = priv;
+ swdev->ops = ops;
+
+ return swdev;
+}
+EXPORT_SYMBOL(ksz_switch_alloc);
+
+int ksz_switch_detect(struct ksz_device *dev)
+{
+ u8 data8;
+ u32 id32;
+ int ret;
+
+ /* turn off SPI DO Edge select */
+ ret = ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
+ if (ret)
+ return ret;
+
+ data8 &= ~SPI_AUTO_EDGE_DETECTION;
+ ret = ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
+ if (ret)
+ return ret;
+
+ /* read chip id */
+ ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);
+ if (ret)
+ return ret;
+
+ dev->chip_id = id32;
+
+ return 0;
+}
+EXPORT_SYMBOL(ksz_switch_detect);
+
+int ksz_switch_register(struct ksz_device *dev)
+{
+ int ret;
+
+ if (dev->pdata)
+ dev->chip_id = dev->pdata->chip_id;
+
+ if (ksz_switch_detect(dev))
+ return -EINVAL;
+
+ ret = ksz_switch_init(dev);
+ if (ret)
+ return ret;
+
+ return dsa_register_switch(dev->ds);
+}
+EXPORT_SYMBOL(ksz_switch_register);
+
+void ksz_switch_remove(struct ksz_device *dev)
+{
+ dsa_unregister_switch(dev->ds);
+}
+EXPORT_SYMBOL(ksz_switch_remove);
+
+MODULE_AUTHOR("Woojung Huh <[email protected]>");
+MODULE_DESCRIPTION("Microchip KSZ Series Switch DSA Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/ksz_priv.h b/drivers/net/dsa/microchip/ksz_priv.h
new file mode 100644
index 000000000000..2a98dbd51456
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_priv.h
@@ -0,0 +1,210 @@
+/*
+ * Microchip KSZ series switch common definitions
+ *
+ * Copyright (C) 2017
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __KSZ_PRIV_H
+#define __KSZ_PRIV_H
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/phy.h>
+#include <linux/etherdevice.h>
+#include <net/dsa.h>
+
+#include "ksz_9477_reg.h"
+
+struct ksz_io_ops;
+
+struct vlan_table {
+ u32 table[3];
+};
+
+struct ksz_device {
+ struct dsa_switch *ds;
+ struct ksz_platform_data *pdata;
+ const char *name;
+
+ struct mutex reg_mutex; /* register access */
+ struct mutex stats_mutex; /* status access */
+ struct mutex alu_mutex; /* ALU access */
+ struct mutex vlan_mutex; /* vlan access */
+ const struct ksz_io_ops *ops;
+
+ struct device *dev;
+
+ void *priv;
+
+ /* chip specific data */
+ u32 chip_id;
+ int num_vlans;
+ int num_alus;
+ int num_statics;
+ int cpu_port; /* port connected to CPU */
+ int cpu_ports; /* port bitmap can be cpu port */
+ int port_cnt;
+
+ struct vlan_table *vlan_cache;
+
+ u64 mib_value[TOTAL_SWITCH_COUNTER_NUM];
+};
+
+struct ksz_io_ops {
+ int (*read8)(struct ksz_device *dev, u32 reg, u8 *value);
+ int (*read16)(struct ksz_device *dev, u32 reg, u16 *value);
+ int (*read24)(struct ksz_device *dev, u32 reg, u32 *value);
+ int (*read32)(struct ksz_device *dev, u32 reg, u32 *value);
+ int (*write8)(struct ksz_device *dev, u32 reg, u8 value);
+ int (*write16)(struct ksz_device *dev, u32 reg, u16 value);
+ int (*write24)(struct ksz_device *dev, u32 reg, u32 value);
+ int (*write32)(struct ksz_device *dev, u32 reg, u32 value);
+ int (*phy_read16)(struct ksz_device *dev, int addr, int reg,
+ u16 *value);
+ int (*phy_write16)(struct ksz_device *dev, int addr, int reg,
+ u16 value);
+};
+
+struct ksz_device *ksz_switch_alloc(struct device *base,
+ const struct ksz_io_ops *ops, void *priv);
+int ksz_switch_detect(struct ksz_device *dev);
+int ksz_switch_register(struct ksz_device *dev);
+void ksz_switch_remove(struct ksz_device *dev);
+
+static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read8(dev, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int ksz_read16(struct ksz_device *dev, u32 reg, u16 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read16(dev, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int ksz_read24(struct ksz_device *dev, u32 reg, u32 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read24(dev, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int ksz_read32(struct ksz_device *dev, u32 reg, u32 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read32(dev, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int ksz_write8(struct ksz_device *dev, u32 reg, u8 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write8(dev, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int ksz_write16(struct ksz_device *dev, u32 reg, u16 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write16(dev, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int ksz_write24(struct ksz_device *dev, u32 reg, u32 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write24(dev, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write32(dev, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline void ksz_pread8(struct ksz_device *dev, int port, int offset,
+ u8 *data)
+{
+ ksz_read8(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+static inline void ksz_pread16(struct ksz_device *dev, int port, int offset,
+ u16 *data)
+{
+ ksz_read16(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+static inline void ksz_pread32(struct ksz_device *dev, int port, int offset,
+ u32 *data)
+{
+ ksz_read32(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+static inline void ksz_pwrite8(struct ksz_device *dev, int port, int offset,
+ u8 data)
+{
+ ksz_write8(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+static inline void ksz_pwrite16(struct ksz_device *dev, int port, int offset,
+ u16 data)
+{
+ ksz_write16(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+static inline void ksz_pwrite32(struct ksz_device *dev, int port, int offset,
+ u32 data)
+{
+ ksz_write32(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+#endif
diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c
new file mode 100644
index 000000000000..c51946983bed
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_spi.c
@@ -0,0 +1,216 @@
+/*
+ * Microchip KSZ series register access through SPI
+ *
+ * Copyright (C) 2017
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "ksz_priv.h"
+
+/* SPI frame opcodes */
+#define KS_SPIOP_RD 3
+#define KS_SPIOP_WR 2
+
+#define SPI_ADDR_SHIFT 24
+#define SPI_ADDR_MASK (BIT(SPI_ADDR_SHIFT) - 1)
+#define SPI_TURNAROUND_SHIFT 5
+
+static int ksz_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
+ unsigned int len)
+{
+ u32 txbuf;
+ int ret;
+
+ txbuf = reg & SPI_ADDR_MASK;
+ txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
+ txbuf <<= SPI_TURNAROUND_SHIFT;
+ txbuf = cpu_to_be32(txbuf);
+
+ ret = spi_write_then_read(spi, &txbuf, 4, val, len);
+ return ret;
+}
+
+static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
+ unsigned int len)
+{
+ struct spi_device *spi = dev->priv;
+
+ return ksz_spi_read_reg(spi, reg, data, len);
+}
+
+static int ksz_spi_read8(struct ksz_device *dev, u32 reg, u8 *val)
+{
+ return ksz_spi_read(dev, reg, val, 1);
+}
+
+static int ksz_spi_read16(struct ksz_device *dev, u32 reg, u16 *val)
+{
+ int ret = ksz_spi_read(dev, reg, (u8 *)val, 2);
+
+ if (!ret)
+ *val = be16_to_cpu(*val);
+
+ return ret;
+}
+
+static int ksz_spi_read24(struct ksz_device *dev, u32 reg, u32 *val)
+{
+ int ret;
+
+ *val = 0;
+ ret = ksz_spi_read(dev, reg, (u8 *)val, 3);
+ if (!ret) {
+ *val = be32_to_cpu(*val);
+ /* convert to 24bit */
+ *val >>= 8;
+ }
+
+ return ret;
+}
+
+static int ksz_spi_read32(struct ksz_device *dev, u32 reg, u32 *val)
+{
+ int ret = ksz_spi_read(dev, reg, (u8 *)val, 4);
+
+ if (!ret)
+ *val = be32_to_cpu(*val);
+
+ return ret;
+}
+
+static int ksz_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
+ unsigned int len)
+{
+ u32 txbuf;
+ u8 data[12];
+ int i;
+
+ txbuf = reg & SPI_ADDR_MASK;
+ txbuf |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
+ txbuf <<= SPI_TURNAROUND_SHIFT;
+ txbuf = cpu_to_be32(txbuf);
+
+ data[0] = txbuf & 0xFF;
+ data[1] = (txbuf & 0xFF00) >> 8;
+ data[2] = (txbuf & 0xFF0000) >> 16;
+ data[3] = (txbuf & 0xFF000000) >> 24;
+ for (i = 0; i < len; i++)
+ data[i + 4] = val[i];
+
+ return spi_write(spi, &data, 4 + len);
+}
+
+static int ksz_spi_write8(struct ksz_device *dev, u32 reg, u8 value)
+{
+ struct spi_device *spi = dev->priv;
+
+ return ksz_spi_write_reg(spi, reg, &value, 1);
+}
+
+static int ksz_spi_write16(struct ksz_device *dev, u32 reg, u16 value)
+{
+ struct spi_device *spi = dev->priv;
+
+ value = cpu_to_be16(value);
+ return ksz_spi_write_reg(spi, reg, (u8 *)&value, 2);
+}
+
+static int ksz_spi_write24(struct ksz_device *dev, u32 reg, u32 value)
+{
+ struct spi_device *spi = dev->priv;
+
+ /* make it to big endian 24bit from MSB */
+ value <<= 8;
+ value = cpu_to_be32(value);
+ return ksz_spi_write_reg(spi, reg, (u8 *)&value, 3);
+}
+
+static int ksz_spi_write32(struct ksz_device *dev, u32 reg, u32 value)
+{
+ struct spi_device *spi = dev->priv;
+
+ value = cpu_to_be32(value);
+ return ksz_spi_write_reg(spi, reg, (u8 *)&value, 4);
+}
+
+static const struct ksz_io_ops ksz_spi_ops = {
+ .read8 = ksz_spi_read8,
+ .read16 = ksz_spi_read16,
+ .read24 = ksz_spi_read24,
+ .read32 = ksz_spi_read32,
+ .write8 = ksz_spi_write8,
+ .write16 = ksz_spi_write16,
+ .write24 = ksz_spi_write24,
+ .write32 = ksz_spi_write32,
+};
+
+static int ksz_spi_probe(struct spi_device *spi)
+{
+ struct ksz_device *dev;
+ int ret;
+
+ dev = ksz_switch_alloc(&spi->dev, &ksz_spi_ops, spi);
+ if (!dev)
+ return -ENOMEM;
+
+ if (spi->dev.platform_data)
+ dev->pdata = spi->dev.platform_data;
+
+ ret = ksz_switch_register(dev);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, dev);
+
+ return 0;
+}
+
+static int ksz_spi_remove(struct spi_device *spi)
+{
+ struct ksz_device *dev = spi_get_drvdata(spi);
+
+ if (dev)
+ ksz_switch_remove(dev);
+
+ return 0;
+}
+
+static const struct of_device_id ksz_dt_ids[] = {
+ { .compatible = "microchip,ksz9477" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ksz_dt_ids);
+
+static struct spi_driver ksz_spi_driver = {
+ .driver = {
+ .name = "ksz9477-switch",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ksz_dt_ids),
+ },
+ .probe = ksz_spi_probe,
+ .remove = ksz_spi_remove,
+};
+
+module_spi_driver(ksz_spi_driver);
+
+MODULE_AUTHOR("Woojung Huh <[email protected]>");
+MODULE_DESCRIPTION("Microchip KSZ Series Switch SPI access Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
new file mode 100644
index 000000000000..1e46418a3b74
--- /dev/null
+++ b/drivers/net/dsa/mt7530.c
@@ -0,0 +1,1125 @@
+/*
+ * Mediatek MT7530 DSA Switch driver
+ * Copyright (C) 2017 Sean Wang <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/iopoll.h>
+#include <linux/mdio.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_gpio.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/gpio/consumer.h>
+#include <net/dsa.h>
+
+#include "mt7530.h"
+
+/* String, offset, and register size in bytes if different from 4 bytes */
+static const struct mt7530_mib_desc mt7530_mib[] = {
+ MIB_DESC(1, 0x00, "TxDrop"),
+ MIB_DESC(1, 0x04, "TxCrcErr"),
+ MIB_DESC(1, 0x08, "TxUnicast"),
+ MIB_DESC(1, 0x0c, "TxMulticast"),
+ MIB_DESC(1, 0x10, "TxBroadcast"),
+ MIB_DESC(1, 0x14, "TxCollision"),
+ MIB_DESC(1, 0x18, "TxSingleCollision"),
+ MIB_DESC(1, 0x1c, "TxMultipleCollision"),
+ MIB_DESC(1, 0x20, "TxDeferred"),
+ MIB_DESC(1, 0x24, "TxLateCollision"),
+ MIB_DESC(1, 0x28, "TxExcessiveCollistion"),
+ MIB_DESC(1, 0x2c, "TxPause"),
+ MIB_DESC(1, 0x30, "TxPktSz64"),
+ MIB_DESC(1, 0x34, "TxPktSz65To127"),
+ MIB_DESC(1, 0x38, "TxPktSz128To255"),
+ MIB_DESC(1, 0x3c, "TxPktSz256To511"),
+ MIB_DESC(1, 0x40, "TxPktSz512To1023"),
+ MIB_DESC(1, 0x44, "Tx1024ToMax"),
+ MIB_DESC(2, 0x48, "TxBytes"),
+ MIB_DESC(1, 0x60, "RxDrop"),
+ MIB_DESC(1, 0x64, "RxFiltering"),
+ MIB_DESC(1, 0x6c, "RxMulticast"),
+ MIB_DESC(1, 0x70, "RxBroadcast"),
+ MIB_DESC(1, 0x74, "RxAlignErr"),
+ MIB_DESC(1, 0x78, "RxCrcErr"),
+ MIB_DESC(1, 0x7c, "RxUnderSizeErr"),
+ MIB_DESC(1, 0x80, "RxFragErr"),
+ MIB_DESC(1, 0x84, "RxOverSzErr"),
+ MIB_DESC(1, 0x88, "RxJabberErr"),
+ MIB_DESC(1, 0x8c, "RxPause"),
+ MIB_DESC(1, 0x90, "RxPktSz64"),
+ MIB_DESC(1, 0x94, "RxPktSz65To127"),
+ MIB_DESC(1, 0x98, "RxPktSz128To255"),
+ MIB_DESC(1, 0x9c, "RxPktSz256To511"),
+ MIB_DESC(1, 0xa0, "RxPktSz512To1023"),
+ MIB_DESC(1, 0xa4, "RxPktSz1024ToMax"),
+ MIB_DESC(2, 0xa8, "RxBytes"),
+ MIB_DESC(1, 0xb0, "RxCtrlDrop"),
+ MIB_DESC(1, 0xb4, "RxIngressDrop"),
+ MIB_DESC(1, 0xb8, "RxArlDrop"),
+};
+
+static int
+mt7623_trgmii_write(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ int ret;
+
+ ret = regmap_write(priv->ethernet, TRGMII_BASE(reg), val);
+ if (ret < 0)
+ dev_err(priv->dev,
+ "failed to priv write register\n");
+ return ret;
+}
+
+static u32
+mt7623_trgmii_read(struct mt7530_priv *priv, u32 reg)
+{
+ int ret;
+ u32 val;
+
+ ret = regmap_read(priv->ethernet, TRGMII_BASE(reg), &val);
+ if (ret < 0) {
+ dev_err(priv->dev,
+ "failed to priv read register\n");
+ return ret;
+ }
+
+ return val;
+}
+
+static void
+mt7623_trgmii_rmw(struct mt7530_priv *priv, u32 reg,
+ u32 mask, u32 set)
+{
+ u32 val;
+
+ val = mt7623_trgmii_read(priv, reg);
+ val &= ~mask;
+ val |= set;
+ mt7623_trgmii_write(priv, reg, val);
+}
+
+static void
+mt7623_trgmii_set(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ mt7623_trgmii_rmw(priv, reg, 0, val);
+}
+
+static void
+mt7623_trgmii_clear(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ mt7623_trgmii_rmw(priv, reg, val, 0);
+}
+
+static int
+core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad)
+{
+ struct mii_bus *bus = priv->bus;
+ int value, ret;
+
+ /* Write the desired MMD Devad */
+ ret = bus->write(bus, 0, MII_MMD_CTRL, devad);
+ if (ret < 0)
+ goto err;
+
+ /* Write the desired MMD register address */
+ ret = bus->write(bus, 0, MII_MMD_DATA, prtad);
+ if (ret < 0)
+ goto err;
+
+ /* Select the Function : DATA with no post increment */
+ ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
+ if (ret < 0)
+ goto err;
+
+ /* Read the content of the MMD's selected register */
+ value = bus->read(bus, 0, MII_MMD_DATA);
+
+ return value;
+err:
+ dev_err(&bus->dev, "failed to read mmd register\n");
+
+ return ret;
+}
+
+static int
+core_write_mmd_indirect(struct mt7530_priv *priv, int prtad,
+ int devad, u32 data)
+{
+ struct mii_bus *bus = priv->bus;
+ int ret;
+
+ /* Write the desired MMD Devad */
+ ret = bus->write(bus, 0, MII_MMD_CTRL, devad);
+ if (ret < 0)
+ goto err;
+
+ /* Write the desired MMD register address */
+ ret = bus->write(bus, 0, MII_MMD_DATA, prtad);
+ if (ret < 0)
+ goto err;
+
+ /* Select the Function : DATA with no post increment */
+ ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
+ if (ret < 0)
+ goto err;
+
+ /* Write the data into MMD's selected register */
+ ret = bus->write(bus, 0, MII_MMD_DATA, data);
+err:
+ if (ret < 0)
+ dev_err(&bus->dev,
+ "failed to write mmd register\n");
+ return ret;
+}
+
+static void
+core_write(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ struct mii_bus *bus = priv->bus;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
+
+ mutex_unlock(&bus->mdio_lock);
+}
+
+static void
+core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
+{
+ struct mii_bus *bus = priv->bus;
+ u32 val;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2);
+ val &= ~mask;
+ val |= set;
+ core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
+
+ mutex_unlock(&bus->mdio_lock);
+}
+
+static void
+core_set(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ core_rmw(priv, reg, 0, val);
+}
+
+static void
+core_clear(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ core_rmw(priv, reg, val, 0);
+}
+
+static int
+mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ struct mii_bus *bus = priv->bus;
+ u16 page, r, lo, hi;
+ int ret;
+
+ page = (reg >> 6) & 0x3ff;
+ r = (reg >> 2) & 0xf;
+ lo = val & 0xffff;
+ hi = val >> 16;
+
+ /* MT7530 uses 31 as the pseudo port */
+ ret = bus->write(bus, 0x1f, 0x1f, page);
+ if (ret < 0)
+ goto err;
+
+ ret = bus->write(bus, 0x1f, r, lo);
+ if (ret < 0)
+ goto err;
+
+ ret = bus->write(bus, 0x1f, 0x10, hi);
+err:
+ if (ret < 0)
+ dev_err(&bus->dev,
+ "failed to write mt7530 register\n");
+ return ret;
+}
+
+static u32
+mt7530_mii_read(struct mt7530_priv *priv, u32 reg)
+{
+ struct mii_bus *bus = priv->bus;
+ u16 page, r, lo, hi;
+ int ret;
+
+ page = (reg >> 6) & 0x3ff;
+ r = (reg >> 2) & 0xf;
+
+ /* MT7530 uses 31 as the pseudo port */
+ ret = bus->write(bus, 0x1f, 0x1f, page);
+ if (ret < 0) {
+ dev_err(&bus->dev,
+ "failed to read mt7530 register\n");
+ return ret;
+ }
+
+ lo = bus->read(bus, 0x1f, r);
+ hi = bus->read(bus, 0x1f, 0x10);
+
+ return (hi << 16) | (lo & 0xffff);
+}
+
+static void
+mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ struct mii_bus *bus = priv->bus;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ mt7530_mii_write(priv, reg, val);
+
+ mutex_unlock(&bus->mdio_lock);
+}
+
+static u32
+_mt7530_read(struct mt7530_dummy_poll *p)
+{
+ struct mii_bus *bus = p->priv->bus;
+ u32 val;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ val = mt7530_mii_read(p->priv, p->reg);
+
+ mutex_unlock(&bus->mdio_lock);
+
+ return val;
+}
+
+static u32
+mt7530_read(struct mt7530_priv *priv, u32 reg)
+{
+ struct mt7530_dummy_poll p;
+
+ INIT_MT7530_DUMMY_POLL(&p, priv, reg);
+ return _mt7530_read(&p);
+}
+
+static void
+mt7530_rmw(struct mt7530_priv *priv, u32 reg,
+ u32 mask, u32 set)
+{
+ struct mii_bus *bus = priv->bus;
+ u32 val;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ val = mt7530_mii_read(priv, reg);
+ val &= ~mask;
+ val |= set;
+ mt7530_mii_write(priv, reg, val);
+
+ mutex_unlock(&bus->mdio_lock);
+}
+
+static void
+mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ mt7530_rmw(priv, reg, 0, val);
+}
+
+static void
+mt7530_clear(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ mt7530_rmw(priv, reg, val, 0);
+}
+
+static int
+mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp)
+{
+ u32 val;
+ int ret;
+ struct mt7530_dummy_poll p;
+
+ /* Set the command operating upon the MAC address entries */
+ val = ATC_BUSY | ATC_MAT(0) | cmd;
+ mt7530_write(priv, MT7530_ATC, val);
+
+ INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_ATC);
+ ret = readx_poll_timeout(_mt7530_read, &p, val,
+ !(val & ATC_BUSY), 20, 20000);
+ if (ret < 0) {
+ dev_err(priv->dev, "reset timeout\n");
+ return ret;
+ }
+
+ /* Additional sanity for read command if the specified
+ * entry is invalid
+ */
+ val = mt7530_read(priv, MT7530_ATC);
+ if ((cmd == MT7530_FDB_READ) && (val & ATC_INVALID))
+ return -EINVAL;
+
+ if (rsp)
+ *rsp = val;
+
+ return 0;
+}
+
+static void
+mt7530_fdb_read(struct mt7530_priv *priv, struct mt7530_fdb *fdb)
+{
+ u32 reg[3];
+ int i;
+
+ /* Read from ARL table into an array */
+ for (i = 0; i < 3; i++) {
+ reg[i] = mt7530_read(priv, MT7530_TSRA1 + (i * 4));
+
+ dev_dbg(priv->dev, "%s(%d) reg[%d]=0x%x\n",
+ __func__, __LINE__, i, reg[i]);
+ }
+
+ fdb->vid = (reg[1] >> CVID) & CVID_MASK;
+ fdb->aging = (reg[2] >> AGE_TIMER) & AGE_TIMER_MASK;
+ fdb->port_mask = (reg[2] >> PORT_MAP) & PORT_MAP_MASK;
+ fdb->mac[0] = (reg[0] >> MAC_BYTE_0) & MAC_BYTE_MASK;
+ fdb->mac[1] = (reg[0] >> MAC_BYTE_1) & MAC_BYTE_MASK;
+ fdb->mac[2] = (reg[0] >> MAC_BYTE_2) & MAC_BYTE_MASK;
+ fdb->mac[3] = (reg[0] >> MAC_BYTE_3) & MAC_BYTE_MASK;
+ fdb->mac[4] = (reg[1] >> MAC_BYTE_4) & MAC_BYTE_MASK;
+ fdb->mac[5] = (reg[1] >> MAC_BYTE_5) & MAC_BYTE_MASK;
+ fdb->noarp = ((reg[2] >> ENT_STATUS) & ENT_STATUS_MASK) == STATIC_ENT;
+}
+
+static void
+mt7530_fdb_write(struct mt7530_priv *priv, u16 vid,
+ u8 port_mask, const u8 *mac,
+ u8 aging, u8 type)
+{
+ u32 reg[3] = { 0 };
+ int i;
+
+ reg[1] |= vid & CVID_MASK;
+ reg[2] |= (aging & AGE_TIMER_MASK) << AGE_TIMER;
+ reg[2] |= (port_mask & PORT_MAP_MASK) << PORT_MAP;
+ /* STATIC_ENT indicate that entry is static wouldn't
+ * be aged out and STATIC_EMP specified as erasing an
+ * entry
+ */
+ reg[2] |= (type & ENT_STATUS_MASK) << ENT_STATUS;
+ reg[1] |= mac[5] << MAC_BYTE_5;
+ reg[1] |= mac[4] << MAC_BYTE_4;
+ reg[0] |= mac[3] << MAC_BYTE_3;
+ reg[0] |= mac[2] << MAC_BYTE_2;
+ reg[0] |= mac[1] << MAC_BYTE_1;
+ reg[0] |= mac[0] << MAC_BYTE_0;
+
+ /* Write array into the ARL table */
+ for (i = 0; i < 3; i++)
+ mt7530_write(priv, MT7530_ATA1 + (i * 4), reg[i]);
+}
+
+static int
+mt7530_pad_clk_setup(struct dsa_switch *ds, int mode)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u32 ncpo1, ssc_delta, trgint, i;
+
+ switch (mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ trgint = 0;
+ ncpo1 = 0x0c80;
+ ssc_delta = 0x87;
+ break;
+ case PHY_INTERFACE_MODE_TRGMII:
+ trgint = 1;
+ ncpo1 = 0x1400;
+ ssc_delta = 0x57;
+ break;
+ default:
+ dev_err(priv->dev, "xMII mode %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,
+ P6_INTF_MODE(trgint));
+
+ /* Lower Tx Driving for TRGMII path */
+ for (i = 0 ; i < NUM_TRGMII_CTRL ; i++)
+ mt7530_write(priv, MT7530_TRGMII_TD_ODT(i),
+ TD_DM_DRVP(8) | TD_DM_DRVN(8));
+
+ /* Setup core clock for MT7530 */
+ if (!trgint) {
+ /* Disable MT7530 core clock */
+ core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+
+ /* Disable PLL, since phy_device has not yet been created
+ * provided for phy_[read,write]_mmd_indirect is called, we
+ * provide our own core_write_mmd_indirect to complete this
+ * function.
+ */
+ core_write_mmd_indirect(priv,
+ CORE_GSWPLL_GRP1,
+ MDIO_MMD_VEND2,
+ 0);
+
+ /* Set core clock into 500Mhz */
+ core_write(priv, CORE_GSWPLL_GRP2,
+ RG_GSWPLL_POSDIV_500M(1) |
+ RG_GSWPLL_FBKDIV_500M(25));
+
+ /* Enable PLL */
+ core_write(priv, CORE_GSWPLL_GRP1,
+ RG_GSWPLL_EN_PRE |
+ RG_GSWPLL_POSDIV_200M(2) |
+ RG_GSWPLL_FBKDIV_200M(32));
+
+ /* Enable MT7530 core clock */
+ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+ }
+
+ /* Setup the MT7530 TRGMII Tx Clock */
+ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+ core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1));
+ core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0));
+ core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta));
+ core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta));
+ core_write(priv, CORE_PLL_GROUP4,
+ RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN |
+ RG_SYSPLL_BIAS_LPF_EN);
+ core_write(priv, CORE_PLL_GROUP2,
+ RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN |
+ RG_SYSPLL_POSDIV(1));
+ core_write(priv, CORE_PLL_GROUP7,
+ RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) |
+ RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
+ core_set(priv, CORE_TRGMII_GSW_CLK_CG,
+ REG_GSWCK_EN | REG_TRGMIICK_EN);
+
+ if (!trgint)
+ for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
+ mt7530_rmw(priv, MT7530_TRGMII_RD(i),
+ RD_TAP_MASK, RD_TAP(16));
+ else
+ mt7623_trgmii_set(priv, GSW_INTF_MODE, INTF_MODE_TRGMII);
+
+ return 0;
+}
+
+static int
+mt7623_pad_clk_setup(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int i;
+
+ for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
+ mt7623_trgmii_write(priv, GSW_TRGMII_TD_ODT(i),
+ TD_DM_DRVP(8) | TD_DM_DRVN(8));
+
+ mt7623_trgmii_set(priv, GSW_TRGMII_RCK_CTRL, RX_RST | RXC_DQSISEL);
+ mt7623_trgmii_clear(priv, GSW_TRGMII_RCK_CTRL, RX_RST);
+
+ return 0;
+}
+
+static void
+mt7530_mib_reset(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_FLUSH);
+ mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE);
+}
+
+static void
+mt7530_port_set_status(struct mt7530_priv *priv, int port, int enable)
+{
+ u32 mask = PMCR_TX_EN | PMCR_RX_EN;
+
+ if (enable)
+ mt7530_set(priv, MT7530_PMCR_P(port), mask);
+ else
+ mt7530_clear(priv, MT7530_PMCR_P(port), mask);
+}
+
+static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ return mdiobus_read_nested(priv->bus, port, regnum);
+}
+
+int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ return mdiobus_write_nested(priv->bus, port, regnum, val);
+}
+
+static void
+mt7530_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++)
+ strncpy(data + i * ETH_GSTRING_LEN, mt7530_mib[i].name,
+ ETH_GSTRING_LEN);
+}
+
+static void
+mt7530_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *data)
+{
+ struct mt7530_priv *priv = ds->priv;
+ const struct mt7530_mib_desc *mib;
+ u32 reg, i;
+ u64 hi;
+
+ for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++) {
+ mib = &mt7530_mib[i];
+ reg = MT7530_PORT_MIB_COUNTER(port) + mib->offset;
+
+ data[i] = mt7530_read(priv, reg);
+ if (mib->size == 2) {
+ hi = mt7530_read(priv, reg + 4);
+ data[i] |= hi << 32;
+ }
+ }
+}
+
+static int
+mt7530_get_sset_count(struct dsa_switch *ds)
+{
+ return ARRAY_SIZE(mt7530_mib);
+}
+
+static void mt7530_adjust_link(struct dsa_switch *ds, int port,
+ struct phy_device *phydev)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ if (phy_is_pseudo_fixed_link(phydev)) {
+ dev_dbg(priv->dev, "phy-mode for master device = %x\n",
+ phydev->interface);
+
+ /* Setup TX circuit incluing relevant PAD and driving */
+ mt7530_pad_clk_setup(ds, phydev->interface);
+
+ /* Setup RX circuit, relevant PAD and driving on the host
+ * which must be placed after the setup on the device side is
+ * all finished.
+ */
+ mt7623_pad_clk_setup(ds);
+ }
+}
+
+static int
+mt7530_cpu_port_enable(struct mt7530_priv *priv,
+ int port)
+{
+ /* Enable Mediatek header mode on the cpu port */
+ mt7530_write(priv, MT7530_PVC_P(port),
+ PORT_SPEC_TAG);
+
+ /* Setup the MAC by default for the cpu port */
+ mt7530_write(priv, MT7530_PMCR_P(port), PMCR_CPUP_LINK);
+
+ /* Disable auto learning on the cpu port */
+ mt7530_set(priv, MT7530_PSC_P(port), SA_DIS);
+
+ /* Unknown unicast frame fordwarding to the cpu port */
+ mt7530_set(priv, MT7530_MFC, UNU_FFP(BIT(port)));
+
+ /* CPU port gets connected to all user ports of
+ * the switch
+ */
+ mt7530_write(priv, MT7530_PCR_P(port),
+ PCR_MATRIX(priv->ds->enabled_port_mask));
+
+ return 0;
+}
+
+static int
+mt7530_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+
+ /* Setup the MAC for the user port */
+ mt7530_write(priv, MT7530_PMCR_P(port), PMCR_USERP_LINK);
+
+ /* Allow the user port gets connected to the cpu port and also
+ * restore the port matrix if the port is the member of a certain
+ * bridge.
+ */
+ priv->ports[port].pm |= PCR_MATRIX(BIT(MT7530_CPU_PORT));
+ priv->ports[port].enable = true;
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+ priv->ports[port].pm);
+ mt7530_port_set_status(priv, port, 1);
+
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static void
+mt7530_port_disable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+
+ /* Clear up all port matrix which could be restored in the next
+ * enablement for the port.
+ */
+ priv->ports[port].enable = false;
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+ PCR_MATRIX_CLR);
+ mt7530_port_set_status(priv, port, 0);
+
+ mutex_unlock(&priv->reg_mutex);
+}
+
+static void
+mt7530_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u32 stp_state;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ stp_state = MT7530_STP_DISABLED;
+ break;
+ case BR_STATE_BLOCKING:
+ stp_state = MT7530_STP_BLOCKING;
+ break;
+ case BR_STATE_LISTENING:
+ stp_state = MT7530_STP_LISTENING;
+ break;
+ case BR_STATE_LEARNING:
+ stp_state = MT7530_STP_LEARNING;
+ break;
+ case BR_STATE_FORWARDING:
+ default:
+ stp_state = MT7530_STP_FORWARDING;
+ break;
+ }
+
+ mt7530_rmw(priv, MT7530_SSP_P(port), FID_PST_MASK, stp_state);
+}
+
+static int
+mt7530_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u32 port_bitmap = BIT(MT7530_CPU_PORT);
+ int i;
+
+ mutex_lock(&priv->reg_mutex);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ /* Add this port to the port matrix of the other ports 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)
+ continue;
+ if (priv->ports[i].enable)
+ mt7530_set(priv, MT7530_PCR_P(i),
+ PCR_MATRIX(BIT(port)));
+ priv->ports[i].pm |= PCR_MATRIX(BIT(port));
+
+ port_bitmap |= BIT(i);
+ }
+ }
+
+ /* Add the all other ports to this port matrix. */
+ if (priv->ports[port].enable)
+ mt7530_rmw(priv, MT7530_PCR_P(port),
+ PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap));
+ priv->ports[port].pm |= PCR_MATRIX(port_bitmap);
+
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static void
+mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int i;
+
+ mutex_lock(&priv->reg_mutex);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ /* Remove this port from the port matrix of the other ports
+ * 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)
+ continue;
+ if (priv->ports[i].enable)
+ mt7530_clear(priv, MT7530_PCR_P(i),
+ PCR_MATRIX(BIT(port)));
+ priv->ports[i].pm &= ~PCR_MATRIX(BIT(port));
+ }
+ }
+
+ /* Set the cpu port to be the only one in the port matrix of
+ * this port.
+ */
+ if (priv->ports[port].enable)
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+ PCR_MATRIX(BIT(MT7530_CPU_PORT)));
+ priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT));
+
+ mutex_unlock(&priv->reg_mutex);
+}
+
+static int
+mt7530_port_fdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int ret;
+
+ /* Because auto-learned entrie shares the same FDB table.
+ * an entry is reserved with no port_mask to make sure fdb_add
+ * is called while the entry is still available.
+ */
+ mutex_lock(&priv->reg_mutex);
+ mt7530_fdb_write(priv, fdb->vid, 0, fdb->addr, -1, STATIC_ENT);
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
+ mutex_unlock(&priv->reg_mutex);
+
+ return ret;
+}
+
+static void
+mt7530_port_fdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u8 port_mask = BIT(port);
+
+ mutex_lock(&priv->reg_mutex);
+ mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_ENT);
+ mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
+ mutex_unlock(&priv->reg_mutex);
+}
+
+static int
+mt7530_port_fdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int ret;
+ u8 port_mask = BIT(port);
+
+ mutex_lock(&priv->reg_mutex);
+ mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_EMP);
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
+ mutex_unlock(&priv->reg_mutex);
+
+ return ret;
+}
+
+static int
+mt7530_port_fdb_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_fdb *fdb,
+ switchdev_obj_dump_cb_t *cb)
+{
+ struct mt7530_priv *priv = ds->priv;
+ struct mt7530_fdb _fdb = { 0 };
+ int cnt = MT7530_NUM_FDB_RECORDS;
+ int ret = 0;
+ u32 rsp = 0;
+
+ mutex_lock(&priv->reg_mutex);
+
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_START, &rsp);
+ if (ret < 0)
+ goto err;
+
+ do {
+ if (rsp & ATC_SRCH_HIT) {
+ mt7530_fdb_read(priv, &_fdb);
+ if (_fdb.port_mask & BIT(port)) {
+ ether_addr_copy(fdb->addr, _fdb.mac);
+ fdb->vid = _fdb.vid;
+ fdb->ndm_state = _fdb.noarp ?
+ NUD_NOARP : NUD_REACHABLE;
+ ret = cb(&fdb->obj);
+ if (ret < 0)
+ break;
+ }
+ }
+ } while (--cnt &&
+ !(rsp & ATC_SRCH_END) &&
+ !mt7530_fdb_cmd(priv, MT7530_FDB_NEXT, &rsp));
+err:
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static enum dsa_tag_protocol
+mtk_get_tag_protocol(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ if (!dsa_is_cpu_port(ds, MT7530_CPU_PORT)) {
+ dev_warn(priv->dev,
+ "port not matched with tagging CPU port\n");
+ return DSA_TAG_PROTO_NONE;
+ } else {
+ return DSA_TAG_PROTO_MTK;
+ }
+}
+
+static int
+mt7530_setup(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int ret, i;
+ u32 id, val;
+ struct device_node *dn;
+ struct mt7530_dummy_poll p;
+
+ /* The parent node of cpu_dp->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;
+ priv->ethernet = syscon_node_to_regmap(dn);
+ if (IS_ERR(priv->ethernet))
+ return PTR_ERR(priv->ethernet);
+
+ regulator_set_voltage(priv->core_pwr, 1000000, 1000000);
+ ret = regulator_enable(priv->core_pwr);
+ if (ret < 0) {
+ dev_err(priv->dev,
+ "Failed to enable core power: %d\n", ret);
+ return ret;
+ }
+
+ regulator_set_voltage(priv->io_pwr, 3300000, 3300000);
+ ret = regulator_enable(priv->io_pwr);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to enable io pwr: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* Reset whole chip through gpio pin or memory-mapped registers for
+ * different type of hardware
+ */
+ if (priv->mcm) {
+ reset_control_assert(priv->rstc);
+ usleep_range(1000, 1100);
+ reset_control_deassert(priv->rstc);
+ } else {
+ gpiod_set_value_cansleep(priv->reset, 0);
+ usleep_range(1000, 1100);
+ gpiod_set_value_cansleep(priv->reset, 1);
+ }
+
+ /* Waiting for MT7530 got to stable */
+ INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP);
+ ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0,
+ 20, 1000000);
+ if (ret < 0) {
+ dev_err(priv->dev, "reset timeout\n");
+ return ret;
+ }
+
+ id = mt7530_read(priv, MT7530_CREV);
+ id >>= CHIP_NAME_SHIFT;
+ if (id != MT7530_ID) {
+ dev_err(priv->dev, "chip %x can't be supported\n", id);
+ return -ENODEV;
+ }
+
+ /* Reset the switch through internal reset */
+ mt7530_write(priv, MT7530_SYS_CTRL,
+ SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
+ SYS_CTRL_REG_RST);
+
+ /* Enable Port 6 only; P5 as GMAC5 which currently is not supported */
+ val = mt7530_read(priv, MT7530_MHWTRAP);
+ val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS;
+ val |= MHWTRAP_MANUAL;
+ mt7530_write(priv, MT7530_MHWTRAP, val);
+
+ /* Enable and reset MIB counters */
+ mt7530_mib_reset(ds);
+
+ mt7530_clear(priv, MT7530_MFC, UNU_FFP_MASK);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ /* Disable forwarding by default on all ports */
+ mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
+ PCR_MATRIX_CLR);
+
+ if (dsa_is_cpu_port(ds, i))
+ mt7530_cpu_port_enable(priv, i);
+ else
+ mt7530_port_disable(ds, i, NULL);
+ }
+
+ /* Flush the FDB table */
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct dsa_switch_ops mt7530_switch_ops = {
+ .get_tag_protocol = mtk_get_tag_protocol,
+ .setup = mt7530_setup,
+ .get_strings = mt7530_get_strings,
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .get_ethtool_stats = mt7530_get_ethtool_stats,
+ .get_sset_count = mt7530_get_sset_count,
+ .adjust_link = mt7530_adjust_link,
+ .port_enable = mt7530_port_enable,
+ .port_disable = mt7530_port_disable,
+ .port_stp_state_set = mt7530_stp_state_set,
+ .port_bridge_join = mt7530_port_bridge_join,
+ .port_bridge_leave = mt7530_port_bridge_leave,
+ .port_fdb_prepare = mt7530_port_fdb_prepare,
+ .port_fdb_add = mt7530_port_fdb_add,
+ .port_fdb_del = mt7530_port_fdb_del,
+ .port_fdb_dump = mt7530_port_fdb_dump,
+};
+
+static int
+mt7530_probe(struct mdio_device *mdiodev)
+{
+ struct mt7530_priv *priv;
+ struct device_node *dn;
+
+ dn = mdiodev->dev.of_node;
+
+ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS);
+ if (!priv->ds)
+ return -ENOMEM;
+
+ /* Use medatek,mcm property to distinguish hardware type that would
+ * casues a little bit differences on power-on sequence.
+ */
+ priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
+ if (priv->mcm) {
+ dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");
+
+ priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
+ if (IS_ERR(priv->rstc)) {
+ dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
+ return PTR_ERR(priv->rstc);
+ }
+ }
+
+ priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
+ if (IS_ERR(priv->core_pwr))
+ return PTR_ERR(priv->core_pwr);
+
+ priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
+ if (IS_ERR(priv->io_pwr))
+ return PTR_ERR(priv->io_pwr);
+
+ /* Not MCM that indicates switch works as the remote standalone
+ * integrated circuit so the GPIO pin would be used to complete
+ * the reset, otherwise memory-mapped register accessing used
+ * through syscon provides in the case of MCM.
+ */
+ if (!priv->mcm) {
+ priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset)) {
+ dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
+ return PTR_ERR(priv->reset);
+ }
+ }
+
+ priv->bus = mdiodev->bus;
+ priv->dev = &mdiodev->dev;
+ priv->ds->priv = priv;
+ priv->ds->ops = &mt7530_switch_ops;
+ mutex_init(&priv->reg_mutex);
+ dev_set_drvdata(&mdiodev->dev, priv);
+
+ return dsa_register_switch(priv->ds);
+}
+
+static void
+mt7530_remove(struct mdio_device *mdiodev)
+{
+ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
+ int ret = 0;
+
+ ret = regulator_disable(priv->core_pwr);
+ if (ret < 0)
+ dev_err(priv->dev,
+ "Failed to disable core power: %d\n", ret);
+
+ ret = regulator_disable(priv->io_pwr);
+ if (ret < 0)
+ dev_err(priv->dev, "Failed to disable io pwr: %d\n",
+ ret);
+
+ dsa_unregister_switch(priv->ds);
+ mutex_destroy(&priv->reg_mutex);
+}
+
+static const struct of_device_id mt7530_of_match[] = {
+ { .compatible = "mediatek,mt7530" },
+ { /* sentinel */ },
+};
+
+static struct mdio_driver mt7530_mdio_driver = {
+ .probe = mt7530_probe,
+ .remove = mt7530_remove,
+ .mdiodrv.driver = {
+ .name = "mt7530",
+ .of_match_table = mt7530_of_match,
+ },
+};
+
+mdio_module_driver(mt7530_mdio_driver);
+
+MODULE_AUTHOR("Sean Wang <[email protected]>");
+MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mediatek-mt7530");
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
new file mode 100644
index 000000000000..b83d76b99802
--- /dev/null
+++ b/drivers/net/dsa/mt7530.h
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2017 Sean Wang <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT7530_H
+#define __MT7530_H
+
+#define MT7530_NUM_PORTS 7
+#define MT7530_CPU_PORT 6
+#define MT7530_NUM_FDB_RECORDS 2048
+
+#define NUM_TRGMII_CTRL 5
+
+#define TRGMII_BASE(x) (0x10000 + (x))
+
+/* Registers to ethsys access */
+#define ETHSYS_CLKCFG0 0x2c
+#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
+
+#define SYSC_REG_RSTCTRL 0x34
+#define RESET_MCM BIT(2)
+
+/* Registers to mac forward control for unknown frames */
+#define MT7530_MFC 0x10
+#define BC_FFP(x) (((x) & 0xff) << 24)
+#define UNM_FFP(x) (((x) & 0xff) << 16)
+#define UNU_FFP(x) (((x) & 0xff) << 8)
+#define UNU_FFP_MASK UNU_FFP(~0)
+
+/* Registers for address table access */
+#define MT7530_ATA1 0x74
+#define STATIC_EMP 0
+#define STATIC_ENT 3
+#define MT7530_ATA2 0x78
+
+/* Register for address table write data */
+#define MT7530_ATWD 0x7c
+
+/* Register for address table control */
+#define MT7530_ATC 0x80
+#define ATC_HASH (((x) & 0xfff) << 16)
+#define ATC_BUSY BIT(15)
+#define ATC_SRCH_END BIT(14)
+#define ATC_SRCH_HIT BIT(13)
+#define ATC_INVALID BIT(12)
+#define ATC_MAT(x) (((x) & 0xf) << 8)
+#define ATC_MAT_MACTAB ATC_MAT(0)
+
+enum mt7530_fdb_cmd {
+ MT7530_FDB_READ = 0,
+ MT7530_FDB_WRITE = 1,
+ MT7530_FDB_FLUSH = 2,
+ MT7530_FDB_START = 4,
+ MT7530_FDB_NEXT = 5,
+};
+
+/* Registers for table search read address */
+#define MT7530_TSRA1 0x84
+#define MAC_BYTE_0 24
+#define MAC_BYTE_1 16
+#define MAC_BYTE_2 8
+#define MAC_BYTE_3 0
+#define MAC_BYTE_MASK 0xff
+
+#define MT7530_TSRA2 0x88
+#define MAC_BYTE_4 24
+#define MAC_BYTE_5 16
+#define CVID 0
+#define CVID_MASK 0xfff
+
+#define MT7530_ATRD 0x8C
+#define AGE_TIMER 24
+#define AGE_TIMER_MASK 0xff
+#define PORT_MAP 4
+#define PORT_MAP_MASK 0xff
+#define ENT_STATUS 2
+#define ENT_STATUS_MASK 0x3
+
+/* Register for vlan table control */
+#define MT7530_VTCR 0x90
+#define VTCR_BUSY BIT(31)
+#define VTCR_FUNC (((x) & 0xf) << 12)
+#define VTCR_FUNC_RD_VID 0x1
+#define VTCR_FUNC_WR_VID 0x2
+#define VTCR_FUNC_INV_VID 0x3
+#define VTCR_FUNC_VAL_VID 0x4
+#define VTCR_VID ((x) & 0xfff)
+
+/* Register for setup vlan and acl write data */
+#define MT7530_VAWD1 0x94
+#define PORT_STAG BIT(31)
+#define IVL_MAC BIT(30)
+#define PORT_MEM(x) (((x) & 0xff) << 16)
+#define VALID BIT(1)
+
+#define MT7530_VAWD2 0x98
+
+/* Register for port STP state control */
+#define MT7530_SSP_P(x) (0x2000 + ((x) * 0x100))
+#define FID_PST(x) ((x) & 0x3)
+#define FID_PST_MASK FID_PST(0x3)
+
+enum mt7530_stp_state {
+ MT7530_STP_DISABLED = 0,
+ MT7530_STP_BLOCKING = 1,
+ MT7530_STP_LISTENING = 1,
+ MT7530_STP_LEARNING = 2,
+ MT7530_STP_FORWARDING = 3
+};
+
+/* Register for port control */
+#define MT7530_PCR_P(x) (0x2004 + ((x) * 0x100))
+#define PORT_VLAN(x) ((x) & 0x3)
+#define PCR_MATRIX(x) (((x) & 0xff) << 16)
+#define PORT_PRI(x) (((x) & 0x7) << 24)
+#define EG_TAG(x) (((x) & 0x3) << 28)
+#define PCR_MATRIX_MASK PCR_MATRIX(0xff)
+#define PCR_MATRIX_CLR PCR_MATRIX(0)
+
+/* Register for port security control */
+#define MT7530_PSC_P(x) (0x200c + ((x) * 0x100))
+#define SA_DIS BIT(4)
+
+/* Register for port vlan control */
+#define MT7530_PVC_P(x) (0x2010 + ((x) * 0x100))
+#define PORT_SPEC_TAG BIT(5)
+#define VLAN_ATTR(x) (((x) & 0x3) << 6)
+#define STAG_VPID (((x) & 0xffff) << 16)
+
+/* Register for port port-and-protocol based vlan 1 control */
+#define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100))
+
+/* Register for port MAC control register */
+#define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100))
+#define PMCR_IFG_XMIT(x) (((x) & 0x3) << 18)
+#define PMCR_MAC_MODE BIT(16)
+#define PMCR_FORCE_MODE BIT(15)
+#define PMCR_TX_EN BIT(14)
+#define PMCR_RX_EN BIT(13)
+#define PMCR_BACKOFF_EN BIT(9)
+#define PMCR_BACKPR_EN BIT(8)
+#define PMCR_TX_FC_EN BIT(5)
+#define PMCR_RX_FC_EN BIT(4)
+#define PMCR_FORCE_SPEED_1000 BIT(3)
+#define PMCR_FORCE_FDX BIT(1)
+#define PMCR_FORCE_LNK BIT(0)
+#define PMCR_COMMON_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
+ PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \
+ PMCR_TX_EN | PMCR_RX_EN | \
+ PMCR_TX_FC_EN | PMCR_RX_FC_EN)
+#define PMCR_CPUP_LINK (PMCR_COMMON_LINK | PMCR_FORCE_MODE | \
+ PMCR_FORCE_SPEED_1000 | \
+ PMCR_FORCE_FDX | \
+ PMCR_FORCE_LNK)
+#define PMCR_USERP_LINK PMCR_COMMON_LINK
+#define PMCR_FIXED_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
+ PMCR_FORCE_MODE | PMCR_TX_EN | \
+ PMCR_RX_EN | PMCR_BACKPR_EN | \
+ PMCR_BACKOFF_EN | \
+ PMCR_FORCE_SPEED_1000 | \
+ PMCR_FORCE_FDX | \
+ PMCR_FORCE_LNK)
+#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \
+ PMCR_TX_FC_EN | PMCR_RX_FC_EN)
+
+#define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100)
+
+/* Register for MIB */
+#define MT7530_PORT_MIB_COUNTER(x) (0x4000 + (x) * 0x100)
+#define MT7530_MIB_CCR 0x4fe0
+#define CCR_MIB_ENABLE BIT(31)
+#define CCR_RX_OCT_CNT_GOOD BIT(7)
+#define CCR_RX_OCT_CNT_BAD BIT(6)
+#define CCR_TX_OCT_CNT_GOOD BIT(5)
+#define CCR_TX_OCT_CNT_BAD BIT(4)
+#define CCR_MIB_FLUSH (CCR_RX_OCT_CNT_GOOD | \
+ CCR_RX_OCT_CNT_BAD | \
+ CCR_TX_OCT_CNT_GOOD | \
+ CCR_TX_OCT_CNT_BAD)
+#define CCR_MIB_ACTIVATE (CCR_MIB_ENABLE | \
+ CCR_RX_OCT_CNT_GOOD | \
+ CCR_RX_OCT_CNT_BAD | \
+ CCR_TX_OCT_CNT_GOOD | \
+ CCR_TX_OCT_CNT_BAD)
+/* Register for system reset */
+#define MT7530_SYS_CTRL 0x7000
+#define SYS_CTRL_PHY_RST BIT(2)
+#define SYS_CTRL_SW_RST BIT(1)
+#define SYS_CTRL_REG_RST BIT(0)
+
+/* Register for hw trap status */
+#define MT7530_HWTRAP 0x7800
+
+/* Register for hw trap modification */
+#define MT7530_MHWTRAP 0x7804
+#define MHWTRAP_MANUAL BIT(16)
+#define MHWTRAP_P5_MAC_SEL BIT(13)
+#define MHWTRAP_P6_DIS BIT(8)
+#define MHWTRAP_P5_RGMII_MODE BIT(7)
+#define MHWTRAP_P5_DIS BIT(6)
+#define MHWTRAP_PHY_ACCESS BIT(5)
+
+/* Register for TOP signal control */
+#define MT7530_TOP_SIG_CTRL 0x7808
+#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16))
+
+#define MT7530_IO_DRV_CR 0x7810
+#define P5_IO_CLK_DRV(x) ((x) & 0x3)
+#define P5_IO_DATA_DRV(x) (((x) & 0x3) << 4)
+
+#define MT7530_P6ECR 0x7830
+#define P6_INTF_MODE_MASK 0x3
+#define P6_INTF_MODE(x) ((x) & 0x3)
+
+/* Registers for TRGMII on the both side */
+#define MT7530_TRGMII_RCK_CTRL 0x7a00
+#define GSW_TRGMII_RCK_CTRL 0x300
+#define RX_RST BIT(31)
+#define RXC_DQSISEL BIT(30)
+#define DQSI1_TAP_MASK (0x7f << 8)
+#define DQSI0_TAP_MASK 0x7f
+#define DQSI1_TAP(x) (((x) & 0x7f) << 8)
+#define DQSI0_TAP(x) ((x) & 0x7f)
+
+#define MT7530_TRGMII_RCK_RTT 0x7a04
+#define GSW_TRGMII_RCK_RTT 0x304
+#define DQS1_GATE BIT(31)
+#define DQS0_GATE BIT(30)
+
+#define MT7530_TRGMII_RD(x) (0x7a10 + (x) * 8)
+#define GSW_TRGMII_RD(x) (0x310 + (x) * 8)
+#define BSLIP_EN BIT(31)
+#define EDGE_CHK BIT(30)
+#define RD_TAP_MASK 0x7f
+#define RD_TAP(x) ((x) & 0x7f)
+
+#define GSW_TRGMII_TXCTRL 0x340
+#define MT7530_TRGMII_TXCTRL 0x7a40
+#define TRAIN_TXEN BIT(31)
+#define TXC_INV BIT(30)
+#define TX_RST BIT(28)
+
+#define MT7530_TRGMII_TD_ODT(i) (0x7a54 + 8 * (i))
+#define GSW_TRGMII_TD_ODT(i) (0x354 + 8 * (i))
+#define TD_DM_DRVP(x) ((x) & 0xf)
+#define TD_DM_DRVN(x) (((x) & 0xf) << 4)
+
+#define GSW_INTF_MODE 0x390
+#define INTF_MODE_TRGMII BIT(1)
+
+#define MT7530_TRGMII_TCK_CTRL 0x7a78
+#define TCK_TAP(x) (((x) & 0xf) << 8)
+
+#define MT7530_P5RGMIIRXCR 0x7b00
+#define CSR_RGMII_EDGE_ALIGN BIT(8)
+#define CSR_RGMII_RXC_0DEG_CFG(x) ((x) & 0xf)
+
+#define MT7530_P5RGMIITXCR 0x7b04
+#define CSR_RGMII_TXC_CFG(x) ((x) & 0x1f)
+
+#define MT7530_CREV 0x7ffc
+#define CHIP_NAME_SHIFT 16
+#define MT7530_ID 0x7530
+
+/* Registers for core PLL access through mmd indirect */
+#define CORE_PLL_GROUP2 0x401
+#define RG_SYSPLL_EN_NORMAL BIT(15)
+#define RG_SYSPLL_VODEN BIT(14)
+#define RG_SYSPLL_LF BIT(13)
+#define RG_SYSPLL_RST_DLY(x) (((x) & 0x3) << 12)
+#define RG_SYSPLL_LVROD_EN BIT(10)
+#define RG_SYSPLL_PREDIV(x) (((x) & 0x3) << 8)
+#define RG_SYSPLL_POSDIV(x) (((x) & 0x3) << 5)
+#define RG_SYSPLL_FBKSEL BIT(4)
+#define RT_SYSPLL_EN_AFE_OLT BIT(0)
+
+#define CORE_PLL_GROUP4 0x403
+#define RG_SYSPLL_DDSFBK_EN BIT(12)
+#define RG_SYSPLL_BIAS_EN BIT(11)
+#define RG_SYSPLL_BIAS_LPF_EN BIT(10)
+
+#define CORE_PLL_GROUP5 0x404
+#define RG_LCDDS_PCW_NCPO1(x) ((x) & 0xffff)
+
+#define CORE_PLL_GROUP6 0x405
+#define RG_LCDDS_PCW_NCPO0(x) ((x) & 0xffff)
+
+#define CORE_PLL_GROUP7 0x406
+#define RG_LCDDS_PWDB BIT(15)
+#define RG_LCDDS_ISO_EN BIT(13)
+#define RG_LCCDS_C(x) (((x) & 0x7) << 4)
+#define RG_LCDDS_PCW_NCPO_CHG BIT(3)
+
+#define CORE_PLL_GROUP10 0x409
+#define RG_LCDDS_SSC_DELTA(x) ((x) & 0xfff)
+
+#define CORE_PLL_GROUP11 0x40a
+#define RG_LCDDS_SSC_DELTA1(x) ((x) & 0xfff)
+
+#define CORE_GSWPLL_GRP1 0x40d
+#define RG_GSWPLL_PREDIV(x) (((x) & 0x3) << 14)
+#define RG_GSWPLL_POSDIV_200M(x) (((x) & 0x3) << 12)
+#define RG_GSWPLL_EN_PRE BIT(11)
+#define RG_GSWPLL_FBKSEL BIT(10)
+#define RG_GSWPLL_BP BIT(9)
+#define RG_GSWPLL_BR BIT(8)
+#define RG_GSWPLL_FBKDIV_200M(x) ((x) & 0xff)
+
+#define CORE_GSWPLL_GRP2 0x40e
+#define RG_GSWPLL_POSDIV_500M(x) (((x) & 0x3) << 8)
+#define RG_GSWPLL_FBKDIV_500M(x) ((x) & 0xff)
+
+#define CORE_TRGMII_GSW_CLK_CG 0x410
+#define REG_GSWCK_EN BIT(0)
+#define REG_TRGMIICK_EN BIT(1)
+
+#define MIB_DESC(_s, _o, _n) \
+ { \
+ .size = (_s), \
+ .offset = (_o), \
+ .name = (_n), \
+ }
+
+struct mt7530_mib_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+};
+
+struct mt7530_fdb {
+ u16 vid;
+ u8 port_mask;
+ u8 aging;
+ u8 mac[6];
+ bool noarp;
+};
+
+struct mt7530_port {
+ bool enable;
+ u32 pm;
+};
+
+/* struct mt7530_priv - This is the main data structure for holding the state
+ * of the driver
+ * @dev: The device pointer
+ * @ds: The pointer to the dsa core structure
+ * @bus: The bus used for the device and built-in PHY
+ * @rstc: The pointer to reset control used by MCM
+ * @ethernet: The regmap used for access TRGMII-based registers
+ * @core_pwr: The power supplied into the core
+ * @io_pwr: The power supplied into the I/O
+ * @reset: The descriptor for GPIO line tied to its reset pin
+ * @mcm: Flag for distinguishing if standalone IC or module
+ * coupling
+ * @ports: Holding the state among ports
+ * @reg_mutex: The lock for protecting among process accessing
+ * registers
+ */
+struct mt7530_priv {
+ struct device *dev;
+ struct dsa_switch *ds;
+ struct mii_bus *bus;
+ struct reset_control *rstc;
+ struct regmap *ethernet;
+ struct regulator *core_pwr;
+ struct regulator *io_pwr;
+ struct gpio_desc *reset;
+ bool mcm;
+
+ struct mt7530_port ports[MT7530_NUM_PORTS];
+ /* protect among processes for registers access*/
+ struct mutex reg_mutex;
+};
+
+struct mt7530_hw_stats {
+ const char *string;
+ u16 reg;
+ u8 sizeof_stat;
+};
+
+struct mt7530_dummy_poll {
+ struct mt7530_priv *priv;
+ u32 reg;
+};
+
+static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p,
+ struct mt7530_priv *priv, u32 reg)
+{
+ p->priv = priv;
+ p->reg = reg;
+}
+
+#endif /* __MT7530_H */
diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c
index 7ce36dbd9b62..dce7fa57eb55 100644
--- a/drivers/net/dsa/mv88e6060.c
+++ b/drivers/net/dsa/mv88e6060.c
@@ -176,7 +176,7 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p)
((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) |
(dsa_is_cpu_port(ds, p) ?
ds->enabled_port_mask :
- BIT(ds->dst->cpu_port)));
+ BIT(ds->dst->cpu_dp->index)));
/* Port Association Vector: when learning source addresses
* of packets, add the address to the address database using
@@ -252,7 +252,7 @@ mv88e6060_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
return reg_write(ds, addr, regnum, val);
}
-static struct dsa_switch_ops mv88e6060_switch_ops = {
+static const struct dsa_switch_ops mv88e6060_switch_ops = {
.get_tag_protocol = mv88e6060_get_tag_protocol,
.probe = mv88e6060_drv_probe,
.setup = mv88e6060_setup,
@@ -261,16 +261,20 @@ static struct dsa_switch_ops mv88e6060_switch_ops = {
.phy_write = mv88e6060_phy_write,
};
+static struct dsa_switch_driver mv88e6060_switch_drv = {
+ .ops = &mv88e6060_switch_ops,
+};
+
static int __init mv88e6060_init(void)
{
- register_switch_driver(&mv88e6060_switch_ops);
+ register_switch_driver(&mv88e6060_switch_drv);
return 0;
}
module_init(mv88e6060_init);
static void __exit mv88e6060_cleanup(void)
{
- unregister_switch_driver(&mv88e6060_switch_ops);
+ unregister_switch_driver(&mv88e6060_switch_drv);
}
module_exit(mv88e6060_cleanup);
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
index c36be318de1a..5cd5551461e3 100644
--- a/drivers/net/dsa/mv88e6xxx/Makefile
+++ b/drivers/net/dsa/mv88e6xxx/Makefile
@@ -1,5 +1,9 @@
obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
mv88e6xxx-objs := chip.o
mv88e6xxx-objs += global1.o
+mv88e6xxx-objs += global1_atu.o
+mv88e6xxx-objs += global1_vtu.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o
+mv88e6xxx-objs += phy.o
mv88e6xxx-objs += port.o
+mv88e6xxx-objs += serdes.o
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index f7222dc6581d..5bcdd33101b0 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -3,11 +3,11 @@
*
* Copyright (c) 2008 Marvell Semiconductor
*
- * Copyright (c) 2015 CMC Electronics, Inc.
- * Added support for VLAN Table Unit operations
- *
* Copyright (c) 2016 Andrew Lunn <[email protected]>
*
+ * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
+ * Vivien Didelot <[email protected]>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -32,12 +32,13 @@
#include <linux/gpio/consumer.h>
#include <linux/phy.h>
#include <net/dsa.h>
-#include <net/switchdev.h>
-#include "mv88e6xxx.h"
+#include "chip.h"
#include "global1.h"
#include "global2.h"
+#include "phy.h"
#include "port.h"
+#include "serdes.h"
static void assert_reg_lock(struct mv88e6xxx_chip *chip)
{
@@ -222,94 +223,16 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
return 0;
}
-static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
- int reg, u16 *val)
-{
- int addr = phy; /* PHY devices addresses start at 0x0 */
-
- if (!chip->info->ops->phy_read)
- return -EOPNOTSUPP;
-
- return chip->info->ops->phy_read(chip, addr, reg, val);
-}
-
-static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
- int reg, u16 val)
-{
- int addr = phy; /* PHY devices addresses start at 0x0 */
-
- if (!chip->info->ops->phy_write)
- return -EOPNOTSUPP;
-
- return chip->info->ops->phy_write(chip, addr, reg, val);
-}
-
-static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
-{
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PHY_PAGE))
- return -EOPNOTSUPP;
-
- return mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
-}
-
-static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
-{
- int err;
-
- /* Restore PHY page Copper 0x0 for access via the registered MDIO bus */
- err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, PHY_PAGE_COPPER);
- if (unlikely(err)) {
- dev_err(chip->dev, "failed to restore PHY %d page Copper (%d)\n",
- phy, err);
- }
-}
-
-static int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
- u8 page, int reg, u16 *val)
-{
- int err;
-
- /* There is no paging for registers 22 */
- if (reg == PHY_PAGE)
- return -EINVAL;
-
- err = mv88e6xxx_phy_page_get(chip, phy, page);
- if (!err) {
- err = mv88e6xxx_phy_read(chip, phy, reg, val);
- mv88e6xxx_phy_page_put(chip, phy);
- }
-
- return err;
-}
-
-static int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
- u8 page, int reg, u16 val)
+struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip)
{
- int err;
-
- /* There is no paging for registers 22 */
- if (reg == PHY_PAGE)
- return -EINVAL;
+ struct mv88e6xxx_mdio_bus *mdio_bus;
- err = mv88e6xxx_phy_page_get(chip, phy, page);
- if (!err) {
- err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
- mv88e6xxx_phy_page_put(chip, phy);
- }
-
- return err;
-}
+ mdio_bus = list_first_entry(&chip->mdios, struct mv88e6xxx_mdio_bus,
+ list);
+ if (!mdio_bus)
+ return NULL;
-static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
-{
- return mv88e6xxx_phy_page_read(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
- reg, val);
-}
-
-static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
-{
- return mv88e6xxx_phy_page_write(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
- reg, val);
+ return mdio_bus->bus;
}
static void mv88e6xxx_g1_irq_mask(struct irq_data *d)
@@ -338,7 +261,7 @@ static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
int err;
mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &reg);
mutex_unlock(&chip->reg_lock);
if (err)
@@ -369,14 +292,14 @@ static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d)
u16 reg;
int err;
- err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &reg);
if (err)
goto out;
reg &= ~mask;
reg |= (~chip->g1_irq.masked & mask);
- err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, reg);
if (err)
goto out;
@@ -415,9 +338,9 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
int irq, virq;
u16 mask;
- mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask);
+ mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &mask);
mask |= GENMASK(chip->g1_irq.nirqs, 0);
- mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
+ mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, mask);
free_irq(chip->irq, chip);
@@ -447,18 +370,18 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
chip->g1_irq.chip = mv88e6xxx_g1_irq_chip;
chip->g1_irq.masked = ~0;
- err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &mask);
if (err)
goto out_mapping;
mask &= ~GENMASK(chip->g1_irq.nirqs, 0);
- err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, mask);
if (err)
goto out_disable;
/* Reading the interrupt status clears (most of) them */
- err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &reg);
if (err)
goto out_disable;
@@ -473,7 +396,7 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
out_disable:
mask |= GENMASK(chip->g1_irq.nirqs, 0);
- mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
+ mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, mask);
out_mapping:
for (irq = 0; irq < 16; irq++) {
@@ -525,155 +448,6 @@ int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)
return mv88e6xxx_write(chip, addr, reg, val);
}
-static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
-{
- if (!chip->info->ops->ppu_disable)
- return 0;
-
- return chip->info->ops->ppu_disable(chip);
-}
-
-static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
-{
- if (!chip->info->ops->ppu_enable)
- return 0;
-
- return chip->info->ops->ppu_enable(chip);
-}
-
-static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
-{
- struct mv88e6xxx_chip *chip;
-
- chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
-
- mutex_lock(&chip->reg_lock);
-
- if (mutex_trylock(&chip->ppu_mutex)) {
- if (mv88e6xxx_ppu_enable(chip) == 0)
- chip->ppu_disabled = 0;
- mutex_unlock(&chip->ppu_mutex);
- }
-
- mutex_unlock(&chip->reg_lock);
-}
-
-static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
-{
- struct mv88e6xxx_chip *chip = (void *)_ps;
-
- schedule_work(&chip->ppu_work);
-}
-
-static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
-{
- int ret;
-
- mutex_lock(&chip->ppu_mutex);
-
- /* If the PHY polling unit is enabled, disable it so that
- * we can access the PHY registers. If it was already
- * disabled, cancel the timer that is going to re-enable
- * it.
- */
- if (!chip->ppu_disabled) {
- ret = mv88e6xxx_ppu_disable(chip);
- if (ret < 0) {
- mutex_unlock(&chip->ppu_mutex);
- return ret;
- }
- chip->ppu_disabled = 1;
- } else {
- del_timer(&chip->ppu_timer);
- ret = 0;
- }
-
- return ret;
-}
-
-static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
-{
- /* Schedule a timer to re-enable the PHY polling unit. */
- mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
- mutex_unlock(&chip->ppu_mutex);
-}
-
-static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
-{
- mutex_init(&chip->ppu_mutex);
- INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
- setup_timer(&chip->ppu_timer, mv88e6xxx_ppu_reenable_timer,
- (unsigned long)chip);
-}
-
-static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip)
-{
- del_timer_sync(&chip->ppu_timer);
-}
-
-static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr,
- int reg, u16 *val)
-{
- int err;
-
- err = mv88e6xxx_ppu_access_get(chip);
- if (!err) {
- err = mv88e6xxx_read(chip, addr, reg, val);
- mv88e6xxx_ppu_access_put(chip);
- }
-
- return err;
-}
-
-static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,
- int reg, u16 val)
-{
- int err;
-
- err = mv88e6xxx_ppu_access_get(chip);
- if (!err) {
- err = mv88e6xxx_write(chip, addr, reg, val);
- mv88e6xxx_ppu_access_put(chip);
- }
-
- return err;
-}
-
-static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)
-{
- return chip->info->family == MV88E6XXX_FAMILY_6095;
-}
-
-static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
-{
- return chip->info->family == MV88E6XXX_FAMILY_6097;
-}
-
-static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
-{
- return chip->info->family == MV88E6XXX_FAMILY_6165;
-}
-
-static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
-{
- return chip->info->family == MV88E6XXX_FAMILY_6185;
-}
-
-static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
-{
- return chip->info->family == MV88E6XXX_FAMILY_6320;
-}
-
-static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
-{
- return chip->info->family == MV88E6XXX_FAMILY_6351;
-}
-
-static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
-{
- return chip->info->family == MV88E6XXX_FAMILY_6352;
-}
-
static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port,
int link, int speed, int duplex,
phy_interface_t mode)
@@ -706,11 +480,16 @@ static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port,
goto restore_link;
}
+ if (chip->info->ops->port_set_cmode) {
+ err = chip->info->ops->port_set_cmode(chip, port, mode);
+ if (err && err != -EOPNOTSUPP)
+ goto restore_link;
+ }
+
err = 0;
restore_link:
if (chip->info->ops->port_set_link(chip, port, link))
- netdev_err(chip->ds->ports[port].netdev,
- "failed to restore MAC's link\n");
+ dev_err(chip->dev, "p%d: failed to restore MAC's link\n", port);
return err;
}
@@ -734,7 +513,7 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
mutex_unlock(&chip->reg_lock);
if (err && err != -EOPNOTSUPP)
- netdev_err(ds->ports[port].netdev, "failed to configure MAC\n");
+ dev_err(ds->dev, "p%d: failed to configure MAC\n", port);
}
static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
@@ -840,6 +619,9 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
mv88e6xxx_g1_stats_read(chip, reg, &low);
if (s->sizeof_stat == 8)
mv88e6xxx_g1_stats_read(chip, reg + 1, &high);
+ break;
+ default:
+ return UINT64_MAX;
}
value = (((u64)high) << 16) | low;
return value;
@@ -943,7 +725,7 @@ static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
{
return mv88e6xxx_stats_get_stats(chip, port, data,
STATS_TYPE_BANK0 | STATS_TYPE_PORT,
- 0, GLOBAL_STATS_OP_HIST_RX_TX);
+ 0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX);
}
static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
@@ -951,8 +733,8 @@ static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
{
return mv88e6xxx_stats_get_stats(chip, port, data,
STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
- GLOBAL_STATS_OP_BANK_1_BIT_9,
- GLOBAL_STATS_OP_HIST_RX_TX);
+ MV88E6XXX_G1_STATS_OP_BANK_1_BIT_9,
+ MV88E6XXX_G1_STATS_OP_HIST_RX_TX);
}
static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
@@ -960,7 +742,8 @@ static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
{
return mv88e6xxx_stats_get_stats(chip, port, data,
STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
- GLOBAL_STATS_OP_BANK_1_BIT_10, 0);
+ MV88E6XXX_G1_STATS_OP_BANK_1_BIT_10,
+ 0);
}
static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port,
@@ -1027,11 +810,6 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
mutex_unlock(&chip->reg_lock);
}
-static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
-{
- return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY);
-}
-
static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
struct ethtool_eee *e)
{
@@ -1051,11 +829,11 @@ static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
e->eee_enabled = !!(reg & 0x0200);
e->tx_lpi_enabled = !!(reg & 0x0100);
- err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
if (err)
goto out;
- e->eee_active = !!(reg & PORT_STATUS_EEE);
+ e->eee_active = !!(reg & MV88E6352_PORT_STS_EEE);
out:
mutex_unlock(&chip->reg_lock);
@@ -1091,143 +869,42 @@ out:
return err;
}
-static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
-{
- u16 val;
- int err;
-
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_ATU_FID)) {
- err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid);
- if (err)
- return err;
- } else if (mv88e6xxx_num_databases(chip) == 256) {
- /* ATU DBNum[7:4] are located in ATU Control 15:12 */
- err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
- if (err)
- return err;
-
- err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
- (val & 0xfff) | ((fid << 8) & 0xf000));
- if (err)
- return err;
-
- /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
- cmd |= fid & 0xf;
- }
-
- err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, cmd);
- if (err)
- return err;
-
- return _mv88e6xxx_atu_wait(chip);
-}
-
-static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_atu_entry *entry)
-{
- u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
-
- if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
- unsigned int mask, shift;
-
- if (entry->trunk) {
- data |= GLOBAL_ATU_DATA_TRUNK;
- mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
- shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
- } else {
- mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
- shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
- }
-
- data |= (entry->portv_trunkid << shift) & mask;
- }
-
- return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
-}
-
-static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_atu_entry *entry,
- bool static_too)
-{
- int op;
- int err;
-
- err = _mv88e6xxx_atu_wait(chip);
- if (err)
- return err;
-
- err = _mv88e6xxx_atu_data_write(chip, entry);
- if (err)
- return err;
-
- if (entry->fid) {
- op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
- GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
- } else {
- op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
- GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
- }
-
- return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
-}
-
-static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
- u16 fid, bool static_too)
+static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)
{
- struct mv88e6xxx_atu_entry entry = {
- .fid = fid,
- .state = 0, /* EntryState bits must be 0 */
- };
+ struct dsa_switch *ds = NULL;
+ struct net_device *br;
+ u16 pvlan;
+ int i;
- return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
-}
+ if (dev < DSA_MAX_SWITCHES)
+ ds = chip->ds->dst->ds[dev];
-static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
- int from_port, int to_port, bool static_too)
-{
- struct mv88e6xxx_atu_entry entry = {
- .trunk = false,
- .fid = fid,
- };
+ /* Prevent frames from unknown switch or port */
+ if (!ds || port >= ds->num_ports)
+ return 0;
- /* EntryState bits must be 0xF */
- entry.state = GLOBAL_ATU_DATA_STATE_MASK;
+ /* Frames from DSA links and CPU ports can egress any local port */
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return mv88e6xxx_port_mask(chip);
- /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
- entry.portv_trunkid = (to_port & 0x0f) << 4;
- entry.portv_trunkid |= from_port & 0x0f;
+ br = ds->ports[port].bridge_dev;
+ pvlan = 0;
- return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
-}
+ /* Frames from user ports can egress any local DSA links and CPU ports,
+ * as well as any local member of their bridge group.
+ */
+ 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))
+ pvlan |= BIT(i);
-static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
- int port, bool static_too)
-{
- /* Destination port 0xF means remove the entries */
- return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
+ return pvlan;
}
-static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
+static int mv88e6xxx_port_vlan_map(struct mv88e6xxx_chip *chip, int port)
{
- struct net_device *bridge = chip->ports[port].bridge_dev;
- struct dsa_switch *ds = chip->ds;
- u16 output_ports = 0;
- int i;
-
- /* allow CPU port or DSA link(s) to send frames to every port */
- if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
- output_ports = ~0;
- } else {
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
- /* allow sending frames to every group member */
- if (bridge && chip->ports[i].bridge_dev == bridge)
- output_ports |= BIT(i);
-
- /* allow sending frames to CPU port and DSA link(s) */
- if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
- output_ports |= BIT(i);
- }
- }
+ u16 output_ports = mv88e6xxx_port_vlan(chip, chip->ds->index, port);
/* prevent frames from going back out of the port they came in on */
output_ports &= ~BIT(port);
@@ -1239,222 +916,142 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
struct mv88e6xxx_chip *chip = ds->priv;
- int stp_state;
int err;
- switch (state) {
- case BR_STATE_DISABLED:
- stp_state = PORT_CONTROL_STATE_DISABLED;
- break;
- case BR_STATE_BLOCKING:
- case BR_STATE_LISTENING:
- stp_state = PORT_CONTROL_STATE_BLOCKING;
- break;
- case BR_STATE_LEARNING:
- stp_state = PORT_CONTROL_STATE_LEARNING;
- break;
- case BR_STATE_FORWARDING:
- default:
- stp_state = PORT_CONTROL_STATE_FORWARDING;
- break;
- }
-
mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_port_set_state(chip, port, stp_state);
+ err = mv88e6xxx_port_set_state(chip, port, state);
mutex_unlock(&chip->reg_lock);
if (err)
- netdev_err(ds->ports[port].netdev, "failed to update state\n");
+ dev_err(ds->dev, "p%d: failed to update state\n", port);
}
-static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
+static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip)
{
- struct mv88e6xxx_chip *chip = ds->priv;
int err;
- mutex_lock(&chip->reg_lock);
- err = _mv88e6xxx_atu_remove(chip, 0, port, false);
- mutex_unlock(&chip->reg_lock);
-
+ err = mv88e6xxx_g1_atu_flush(chip, 0, true);
if (err)
- netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");
-}
-
-static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
-{
- return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
-}
-
-static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
-{
- int err;
+ return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op);
+ err = mv88e6xxx_g1_atu_set_learn2all(chip, true);
if (err)
return err;
- return _mv88e6xxx_vtu_wait(chip);
-}
-
-static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
-{
- int ret;
-
- ret = _mv88e6xxx_vtu_wait(chip);
- if (ret < 0)
- return ret;
-
- return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
+ return mv88e6xxx_g1_atu_set_age_time(chip, 300000);
}
-static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry,
- unsigned int nibble_offset)
+static int mv88e6xxx_irl_setup(struct mv88e6xxx_chip *chip)
{
- u16 regs[3];
- int i, err;
+ int port;
+ int err;
- for (i = 0; i < 3; ++i) {
- u16 *reg = &regs[i];
+ if (!chip->info->ops->irl_init_all)
+ return 0;
- err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
+ for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
+ /* Disable ingress rate limiting by resetting all per port
+ * ingress rate limit resources to their initial state.
+ */
+ err = chip->info->ops->irl_init_all(chip, port);
if (err)
return err;
}
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
- unsigned int shift = (i % 4) * 4 + nibble_offset;
- u16 reg = regs[i / 4];
-
- entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
- }
-
return 0;
}
-static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry)
+static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
{
- return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
-}
+ u16 pvlan = 0;
-static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry)
-{
- return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
+ if (!mv88e6xxx_has_pvt(chip))
+ return -EOPNOTSUPP;
+
+ /* Skip the local source device, which uses in-chip port VLAN */
+ if (dev != chip->ds->index)
+ pvlan = mv88e6xxx_port_vlan(chip, dev, port);
+
+ return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan);
}
-static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry,
- unsigned int nibble_offset)
+static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip)
{
- u16 regs[3] = { 0 };
- int i, err;
-
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
- unsigned int shift = (i % 4) * 4 + nibble_offset;
- u8 data = entry->data[i];
+ int dev, port;
+ int err;
- regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
- }
+ if (!mv88e6xxx_has_pvt(chip))
+ return 0;
- for (i = 0; i < 3; ++i) {
- u16 reg = regs[i];
+ /* Clear 5 Bit Port for usage with Marvell Link Street devices:
+ * use 4 bits for the Src_Port/Src_Trunk and 5 bits for the Src_Dev.
+ */
+ err = mv88e6xxx_g2_misc_4_bit_port(chip);
+ if (err)
+ return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
- if (err)
- return err;
+ for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; ++dev) {
+ for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; ++port) {
+ err = mv88e6xxx_pvt_map(chip, dev, port);
+ if (err)
+ return err;
+ }
}
return 0;
}
-static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry)
-{
- return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
-}
-
-static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry)
-{
- return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
-}
-
-static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
-{
- return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID,
- vid & GLOBAL_VTU_VID_MASK);
-}
-
-static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry)
+static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
{
- struct mv88e6xxx_vtu_entry next = { 0 };
- u16 val;
+ struct mv88e6xxx_chip *chip = ds->priv;
int err;
- err = _mv88e6xxx_vtu_wait(chip);
- if (err)
- return err;
-
- err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
- if (err)
- return err;
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_g1_atu_remove(chip, 0, port, false);
+ mutex_unlock(&chip->reg_lock);
- err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
if (err)
- return err;
-
- next.vid = val & GLOBAL_VTU_VID_MASK;
- next.valid = !!(val & GLOBAL_VTU_VID_VALID);
-
- if (next.valid) {
- err = mv88e6xxx_vtu_data_read(chip, &next);
- if (err)
- return err;
+ dev_err(ds->dev, "p%d: failed to flush ATU\n", port);
+}
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
- err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val);
- if (err)
- return err;
+static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip)
+{
+ if (!chip->info->max_vid)
+ return 0;
- next.fid = val & GLOBAL_VTU_FID_MASK;
- } else if (mv88e6xxx_num_databases(chip) == 256) {
- /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
- * VTU DBNum[3:0] are located in VTU Operation 3:0
- */
- err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val);
- if (err)
- return err;
+ return mv88e6xxx_g1_vtu_flush(chip);
+}
- next.fid = (val & 0xf00) >> 4;
- next.fid |= val & 0xf;
- }
+static int mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ if (!chip->info->ops->vtu_getnext)
+ return -EOPNOTSUPP;
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
- err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
- if (err)
- return err;
+ return chip->info->ops->vtu_getnext(chip, entry);
+}
- next.sid = val & GLOBAL_VTU_SID_MASK;
- }
- }
+static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ if (!chip->info->ops->vtu_loadpurge)
+ return -EOPNOTSUPP;
- *entry = next;
- return 0;
+ return chip->info->ops->vtu_loadpurge(chip, entry);
}
static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan,
- int (*cb)(struct switchdev_obj *obj))
+ switchdev_obj_dump_cb_t *cb)
{
struct mv88e6xxx_chip *chip = ds->priv;
- struct mv88e6xxx_vtu_entry next;
+ struct mv88e6xxx_vtu_entry next = {
+ .vid = chip->info->max_vid,
+ };
u16 pvid;
int err;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
+ if (!chip->info->max_vid)
return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock);
@@ -1463,19 +1060,16 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
if (err)
goto unlock;
- err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
- if (err)
- goto unlock;
-
do {
- err = _mv88e6xxx_vtu_getnext(chip, &next);
+ err = mv88e6xxx_vtu_getnext(chip, &next);
if (err)
break;
if (!next.valid)
break;
- if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
+ if (next.member[port] ==
+ MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
continue;
/* reinit and dump this VLAN obj */
@@ -1483,7 +1077,8 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
vlan->vid_end = next.vid;
vlan->flags = 0;
- if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
+ if (next.member[port] ==
+ MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED)
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
if (next.vid == pvid)
@@ -1492,7 +1087,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
err = cb(&vlan->obj);
if (err)
break;
- } while (next.vid < GLOBAL_VTU_VID_MASK);
+ } while (next.vid < chip->info->max_vid);
unlock:
mutex_unlock(&chip->reg_lock);
@@ -1500,133 +1095,12 @@ unlock:
return err;
}
-static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry)
-{
- u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
- u16 reg = 0;
- int err;
-
- err = _mv88e6xxx_vtu_wait(chip);
- if (err)
- return err;
-
- if (!entry->valid)
- goto loadpurge;
-
- /* Write port member tags */
- err = mv88e6xxx_vtu_data_write(chip, entry);
- if (err)
- return err;
-
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
- reg = entry->sid & GLOBAL_VTU_SID_MASK;
- err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
- if (err)
- return err;
- }
-
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
- reg = entry->fid & GLOBAL_VTU_FID_MASK;
- err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, reg);
- if (err)
- return err;
- } else if (mv88e6xxx_num_databases(chip) == 256) {
- /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
- * VTU DBNum[3:0] are located in VTU Operation 3:0
- */
- op |= (entry->fid & 0xf0) << 8;
- op |= entry->fid & 0xf;
- }
-
- reg = GLOBAL_VTU_VID_VALID;
-loadpurge:
- reg |= entry->vid & GLOBAL_VTU_VID_MASK;
- err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
- if (err)
- return err;
-
- return _mv88e6xxx_vtu_cmd(chip, op);
-}
-
-static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
- struct mv88e6xxx_vtu_entry *entry)
-{
- struct mv88e6xxx_vtu_entry next = { 0 };
- u16 val;
- int err;
-
- err = _mv88e6xxx_vtu_wait(chip);
- if (err)
- return err;
-
- err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID,
- sid & GLOBAL_VTU_SID_MASK);
- if (err)
- return err;
-
- err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
- if (err)
- return err;
-
- err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
- if (err)
- return err;
-
- next.sid = val & GLOBAL_VTU_SID_MASK;
-
- err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
- if (err)
- return err;
-
- next.valid = !!(val & GLOBAL_VTU_VID_VALID);
-
- if (next.valid) {
- err = mv88e6xxx_stu_data_read(chip, &next);
- if (err)
- return err;
- }
-
- *entry = next;
- return 0;
-}
-
-static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_vtu_entry *entry)
-{
- u16 reg = 0;
- int err;
-
- err = _mv88e6xxx_vtu_wait(chip);
- if (err)
- return err;
-
- if (!entry->valid)
- goto loadpurge;
-
- /* Write port states */
- err = mv88e6xxx_stu_data_write(chip, entry);
- if (err)
- return err;
-
- reg = GLOBAL_VTU_VID_VALID;
-loadpurge:
- err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
- if (err)
- return err;
-
- reg = entry->sid & GLOBAL_VTU_SID_MASK;
- err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
- if (err)
- return err;
-
- return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
-}
-
-static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
+static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
{
DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
- struct mv88e6xxx_vtu_entry vlan;
+ struct mv88e6xxx_vtu_entry vlan = {
+ .vid = chip->info->max_vid,
+ };
int i, err;
bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
@@ -1641,12 +1115,8 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
}
/* Set every FID bit used by the VLAN entries */
- err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
- if (err)
- return err;
-
do {
- err = _mv88e6xxx_vtu_getnext(chip, &vlan);
+ err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
return err;
@@ -1654,7 +1124,7 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
break;
set_bit(vlan.fid, fid_bitmap);
- } while (vlan.vid < GLOBAL_VTU_VID_MASK);
+ } while (vlan.vid < chip->info->max_vid);
/* The reset value 0x000 is used to indicate that multiple address
* databases are not needed. Return the next positive available.
@@ -1664,91 +1134,54 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
return -ENOSPC;
/* Clear the database */
- return _mv88e6xxx_atu_flush(chip, *fid, true);
-}
-
-static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
- struct mv88e6xxx_vtu_entry *entry)
-{
- struct dsa_switch *ds = chip->ds;
- struct mv88e6xxx_vtu_entry vlan = {
- .valid = true,
- .vid = vid,
- };
- int i, err;
-
- err = _mv88e6xxx_fid_new(chip, &vlan.fid);
- if (err)
- return err;
-
- /* exclude all ports except the CPU and DSA ports */
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
- vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
- ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
- : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
-
- if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
- mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
- struct mv88e6xxx_vtu_entry vstp;
-
- /* Adding a VTU entry requires a valid STU entry. As VSTP is not
- * implemented, only one STU entry is needed to cover all VTU
- * entries. Thus, validate the SID 0.
- */
- vlan.sid = 0;
- err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
- if (err)
- return err;
-
- if (vstp.sid != vlan.sid || !vstp.valid) {
- memset(&vstp, 0, sizeof(vstp));
- vstp.valid = true;
- vstp.sid = vlan.sid;
-
- err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
- if (err)
- return err;
- }
- }
-
- *entry = vlan;
- return 0;
+ return mv88e6xxx_g1_atu_flush(chip, *fid, true);
}
-static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
- struct mv88e6xxx_vtu_entry *entry, bool creat)
+static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
+ struct mv88e6xxx_vtu_entry *entry, bool new)
{
int err;
if (!vid)
return -EINVAL;
- err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
- if (err)
- return err;
+ entry->vid = vid - 1;
+ entry->valid = false;
- err = _mv88e6xxx_vtu_getnext(chip, entry);
+ err = mv88e6xxx_vtu_getnext(chip, entry);
if (err)
return err;
- if (entry->vid != vid || !entry->valid) {
- if (!creat)
- return -EOPNOTSUPP;
- /* -ENOENT would've been more appropriate, but switchdev expects
- * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
- */
+ if (entry->vid == vid && entry->valid)
+ return 0;
- err = _mv88e6xxx_vtu_new(chip, vid, entry);
+ if (new) {
+ int i;
+
+ /* Initialize a fresh VLAN entry */
+ memset(entry, 0, sizeof(*entry));
+ entry->valid = true;
+ entry->vid = vid;
+
+ /* Exclude all ports */
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
+ entry->member[i] =
+ MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER;
+
+ return mv88e6xxx_atu_new(chip, &entry->fid);
}
- return err;
+ /* switchdev expects -EOPNOTSUPP to honor software VLANs */
+ return -EOPNOTSUPP;
}
static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
u16 vid_begin, u16 vid_end)
{
struct mv88e6xxx_chip *chip = ds->priv;
- struct mv88e6xxx_vtu_entry vlan;
+ struct mv88e6xxx_vtu_entry vlan = {
+ .vid = vid_begin - 1,
+ };
int i, err;
if (!vid_begin)
@@ -1756,12 +1189,8 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
mutex_lock(&chip->reg_lock);
- err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
- if (err)
- goto unlock;
-
do {
- err = _mv88e6xxx_vtu_getnext(chip, &vlan);
+ err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
goto unlock;
@@ -1778,21 +1207,20 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
if (!ds->ports[port].netdev)
continue;
- if (vlan.data[i] ==
- GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
+ if (vlan.member[i] ==
+ MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
continue;
- if (chip->ports[i].bridge_dev ==
- chip->ports[port].bridge_dev)
+ if (ds->ports[i].bridge_dev ==
+ ds->ports[port].bridge_dev)
break; /* same bridge, check next VLAN */
- if (!chip->ports[i].bridge_dev)
+ if (!ds->ports[i].bridge_dev)
continue;
- netdev_warn(ds->ports[port].netdev,
- "hardware VLAN %d already used by %s\n",
- vlan.vid,
- netdev_name(chip->ports[i].bridge_dev));
+ dev_err(ds->dev, "p%d: hw VLAN %d already used by %s\n",
+ port, vlan.vid,
+ netdev_name(ds->ports[i].bridge_dev));
err = -EOPNOTSUPP;
goto unlock;
}
@@ -1808,11 +1236,11 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering)
{
struct mv88e6xxx_chip *chip = ds->priv;
- u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
- PORT_CONTROL_2_8021Q_DISABLED;
+ u16 mode = vlan_filtering ? MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE :
+ MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED;
int err;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
+ if (!chip->info->max_vid)
return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock);
@@ -1830,7 +1258,7 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
struct mv88e6xxx_chip *chip = ds->priv;
int err;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
+ if (!chip->info->max_vid)
return -EOPNOTSUPP;
/* If the requested port doesn't belong to the same bridge as the VLAN
@@ -1848,20 +1276,18 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
}
static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
- u16 vid, bool untagged)
+ u16 vid, u8 member)
{
struct mv88e6xxx_vtu_entry vlan;
int err;
- err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
+ err = mv88e6xxx_vtu_get(chip, vid, &vlan, true);
if (err)
return err;
- vlan.data[port] = untagged ?
- GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
- GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
+ vlan.member[port] = member;
- return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
+ return mv88e6xxx_vtu_loadpurge(chip, &vlan);
}
static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
@@ -1871,22 +1297,29 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
struct mv88e6xxx_chip *chip = ds->priv;
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+ u8 member;
u16 vid;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
+ if (!chip->info->max_vid)
return;
+ if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
+ member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED;
+ else if (untagged)
+ member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED;
+ else
+ member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED;
+
mutex_lock(&chip->reg_lock);
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
- if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
- netdev_err(ds->ports[port].netdev,
- "failed to add VLAN %d%c\n",
- vid, untagged ? 'u' : 't');
+ if (_mv88e6xxx_port_vlan_add(chip, port, vid, member))
+ dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port,
+ vid, untagged ? 'u' : 't');
if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end))
- netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
- vlan->vid_end);
+ dev_err(ds->dev, "p%d: failed to set PVID %d\n", port,
+ vlan->vid_end);
mutex_unlock(&chip->reg_lock);
}
@@ -1894,37 +1327,34 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
int port, u16 vid)
{
- struct dsa_switch *ds = chip->ds;
struct mv88e6xxx_vtu_entry vlan;
int i, err;
- err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
+ err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
if (err)
return err;
/* Tell switchdev if this VLAN is handled in software */
- if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
+ if (vlan.member[port] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
return -EOPNOTSUPP;
- vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
+ vlan.member[port] = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER;
/* keep the VLAN unless all ports are excluded */
vlan.valid = false;
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
- if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
- continue;
-
- if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
+ if (vlan.member[i] !=
+ MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
vlan.valid = true;
break;
}
}
- err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
+ err = mv88e6xxx_vtu_loadpurge(chip, &vlan);
if (err)
return err;
- return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
+ return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false);
}
static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
@@ -1934,7 +1364,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
u16 pvid, vid;
int err = 0;
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
+ if (!chip->info->max_vid)
return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock);
@@ -1961,95 +1391,6 @@ unlock:
return err;
}
-static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
- const unsigned char *addr)
-{
- int i, err;
-
- for (i = 0; i < 3; i++) {
- err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i,
- (addr[i * 2] << 8) | addr[i * 2 + 1]);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
- unsigned char *addr)
-{
- u16 val;
- int i, err;
-
- for (i = 0; i < 3; i++) {
- err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val);
- if (err)
- return err;
-
- addr[i * 2] = val >> 8;
- addr[i * 2 + 1] = val & 0xff;
- }
-
- return 0;
-}
-
-static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
- struct mv88e6xxx_atu_entry *entry)
-{
- int ret;
-
- ret = _mv88e6xxx_atu_wait(chip);
- if (ret < 0)
- return ret;
-
- ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
- if (ret < 0)
- return ret;
-
- ret = _mv88e6xxx_atu_data_write(chip, entry);
- if (ret < 0)
- return ret;
-
- return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
-}
-
-static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
- struct mv88e6xxx_atu_entry *entry);
-
-static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid,
- const u8 *addr, struct mv88e6xxx_atu_entry *entry)
-{
- struct mv88e6xxx_atu_entry next;
- int err;
-
- eth_broadcast_addr(next.mac);
-
- err = _mv88e6xxx_atu_mac_write(chip, next.mac);
- if (err)
- return err;
-
- do {
- err = _mv88e6xxx_atu_getnext(chip, fid, &next);
- if (err)
- return err;
-
- if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
- break;
-
- if (ether_addr_equal(next.mac, addr)) {
- *entry = next;
- return 0;
- }
- } while (!is_broadcast_ether_addr(next.mac));
-
- memset(entry, 0, sizeof(*entry));
- entry->fid = fid;
- ether_addr_copy(entry->mac, addr);
-
- return 0;
-}
-
static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
const unsigned char *addr, u16 vid,
u8 state)
@@ -2062,25 +1403,36 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
if (vid == 0)
err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
else
- err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
+ err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
if (err)
return err;
- err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry);
+ 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 == GLOBAL_ATU_DATA_STATE_UNUSED) {
- entry.portv_trunkid &= ~BIT(port);
- if (!entry.portv_trunkid)
- entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
+ 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.portv_trunkid |= BIT(port);
+ entry.portvec |= BIT(port);
entry.state = state;
}
- return _mv88e6xxx_atu_load(chip, &entry);
+ return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry);
}
static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
@@ -2101,8 +1453,9 @@ static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
mutex_lock(&chip->reg_lock);
if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
- GLOBAL_ATU_DATA_STATE_UC_STATIC))
- netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n");
+ MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC))
+ dev_err(ds->dev, "p%d: failed to load unicast MAC address\n",
+ port);
mutex_unlock(&chip->reg_lock);
}
@@ -2114,81 +1467,32 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
- GLOBAL_ATU_DATA_STATE_UNUSED);
+ MV88E6XXX_G1_ATU_DATA_STATE_UNUSED);
mutex_unlock(&chip->reg_lock);
return err;
}
-static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
- struct mv88e6xxx_atu_entry *entry)
-{
- struct mv88e6xxx_atu_entry next = { 0 };
- u16 val;
- int err;
-
- next.fid = fid;
-
- err = _mv88e6xxx_atu_wait(chip);
- if (err)
- return err;
-
- err = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
- if (err)
- return err;
-
- err = _mv88e6xxx_atu_mac_read(chip, next.mac);
- if (err)
- return err;
-
- err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val);
- if (err)
- return err;
-
- next.state = val & GLOBAL_ATU_DATA_STATE_MASK;
- if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
- unsigned int mask, shift;
-
- if (val & GLOBAL_ATU_DATA_TRUNK) {
- next.trunk = true;
- mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
- shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
- } else {
- next.trunk = false;
- mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
- shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
- }
-
- next.portv_trunkid = (val & mask) >> shift;
- }
-
- *entry = next;
- return 0;
-}
-
static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
u16 fid, u16 vid, int port,
struct switchdev_obj *obj,
- int (*cb)(struct switchdev_obj *obj))
+ switchdev_obj_dump_cb_t *cb)
{
- struct mv88e6xxx_atu_entry addr = {
- .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
- };
+ struct mv88e6xxx_atu_entry addr;
int err;
- err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
- if (err)
- return err;
+ addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
+ eth_broadcast_addr(addr.mac);
do {
- err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
+ err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
if (err)
return err;
- if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
+ if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED)
break;
- if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0)
+ if (addr.trunk || (addr.portvec & BIT(port)) == 0)
continue;
if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
@@ -2200,7 +1504,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
fdb->vid = vid;
ether_addr_copy(fdb->addr, addr.mac);
- if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
+ if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC)
fdb->ndm_state = NUD_NOARP;
else
fdb->ndm_state = NUD_REACHABLE;
@@ -2227,10 +1531,10 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
struct switchdev_obj *obj,
- int (*cb)(struct switchdev_obj *obj))
+ switchdev_obj_dump_cb_t *cb)
{
struct mv88e6xxx_vtu_entry vlan = {
- .vid = GLOBAL_VTU_VID_MASK, /* all ones */
+ .vid = chip->info->max_vid,
};
u16 fid;
int err;
@@ -2245,12 +1549,8 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
return err;
/* Dump VLANs' Filtering Information Databases */
- err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
- if (err)
- return err;
-
do {
- err = _mv88e6xxx_vtu_getnext(chip, &vlan);
+ err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
return err;
@@ -2261,14 +1561,14 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
obj, cb);
if (err)
return err;
- } while (vlan.vid < GLOBAL_VTU_VID_MASK);
+ } while (vlan.vid < chip->info->max_vid);
return err;
}
static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb,
- int (*cb)(struct switchdev_obj *obj))
+ switchdev_obj_dump_cb_t *cb)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
@@ -2280,47 +1580,96 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
return err;
}
-static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
- struct net_device *bridge)
+static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
+ struct net_device *br)
{
- struct mv88e6xxx_chip *chip = ds->priv;
- int i, err = 0;
+ struct dsa_switch *ds;
+ int port;
+ int dev;
+ int err;
- mutex_lock(&chip->reg_lock);
+ /* Remap the Port VLAN of each local bridge group member */
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
+ if (chip->ds->ports[port].bridge_dev == br) {
+ err = mv88e6xxx_port_vlan_map(chip, port);
+ if (err)
+ return err;
+ }
+ }
- /* Assign the bridge and remap each port's VLANTable */
- chip->ports[port].bridge_dev = bridge;
+ if (!mv88e6xxx_has_pvt(chip))
+ return 0;
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
- if (chip->ports[i].bridge_dev == bridge) {
- err = _mv88e6xxx_port_based_vlan_map(chip, i);
- if (err)
- break;
+ /* Remap the Port VLAN of each cross-chip bridge group member */
+ for (dev = 0; dev < DSA_MAX_SWITCHES; ++dev) {
+ ds = chip->ds->dst->ds[dev];
+ if (!ds)
+ break;
+
+ for (port = 0; port < ds->num_ports; ++port) {
+ if (ds->ports[port].bridge_dev == br) {
+ err = mv88e6xxx_pvt_map(chip, dev, port);
+ if (err)
+ return err;
+ }
}
}
+ return 0;
+}
+
+static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *br)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_bridge_map(chip, br);
mutex_unlock(&chip->reg_lock);
return err;
}
-static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
+static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct net_device *br)
{
struct mv88e6xxx_chip *chip = ds->priv;
- struct net_device *bridge = chip->ports[port].bridge_dev;
- int i;
mutex_lock(&chip->reg_lock);
+ if (mv88e6xxx_bridge_map(chip, br) ||
+ mv88e6xxx_port_vlan_map(chip, port))
+ dev_err(ds->dev, "failed to remap in-chip Port VLAN\n");
+ mutex_unlock(&chip->reg_lock);
+}
+
+static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int dev,
+ int port, struct net_device *br)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
- /* Unassign the bridge and remap each port's VLANTable */
- chip->ports[port].bridge_dev = NULL;
+ if (!mv88e6xxx_has_pvt(chip))
+ return 0;
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
- if (i == port || chip->ports[i].bridge_dev == bridge)
- if (_mv88e6xxx_port_based_vlan_map(chip, i))
- netdev_warn(ds->ports[i].netdev,
- "failed to remap\n");
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_pvt_map(chip, dev, port);
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int dev,
+ int port, struct net_device *br)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ if (!mv88e6xxx_has_pvt(chip))
+ return;
+
+ mutex_lock(&chip->reg_lock);
+ if (mv88e6xxx_pvt_map(chip, dev, port))
+ dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n");
mutex_unlock(&chip->reg_lock);
}
@@ -2351,8 +1700,7 @@ static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip)
/* Set all ports to the Disabled state */
for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
- err = mv88e6xxx_port_set_state(chip, i,
- PORT_CONTROL_STATE_DISABLED);
+ err = mv88e6xxx_port_set_state(chip, i, BR_STATE_DISABLED);
if (err)
return err;
}
@@ -2378,88 +1726,95 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
return mv88e6xxx_software_reset(chip);
}
-static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)
+static int mv88e6xxx_set_port_mode(struct mv88e6xxx_chip *chip, int port,
+ enum mv88e6xxx_frame_mode frame,
+ enum mv88e6xxx_egress_mode egress, u16 etype)
{
- u16 val;
int err;
- /* Clear Power Down bit */
- err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val);
+ if (!chip->info->ops->port_set_frame_mode)
+ return -EOPNOTSUPP;
+
+ err = mv88e6xxx_port_set_egress_mode(chip, port, egress);
if (err)
return err;
- if (val & BMCR_PDOWN) {
- val &= ~BMCR_PDOWN;
- err = mv88e6xxx_serdes_write(chip, MII_BMCR, val);
- }
+ err = chip->info->ops->port_set_frame_mode(chip, port, frame);
+ if (err)
+ return err;
- return err;
+ if (chip->info->ops->port_set_ether_type)
+ return chip->info->ops->port_set_ether_type(chip, port, etype);
+
+ return 0;
}
-static int mv88e6xxx_setup_port_dsa(struct mv88e6xxx_chip *chip, int port,
- int upstream_port)
+static int mv88e6xxx_set_port_mode_normal(struct mv88e6xxx_chip *chip, int port)
{
- int err;
+ return mv88e6xxx_set_port_mode(chip, port, MV88E6XXX_FRAME_MODE_NORMAL,
+ MV88E6XXX_EGRESS_MODE_UNMODIFIED,
+ MV88E6XXX_PORT_ETH_TYPE_DEFAULT);
+}
- err = chip->info->ops->port_set_frame_mode(
- chip, port, MV88E6XXX_FRAME_MODE_DSA);
- if (err)
- return err;
+static int mv88e6xxx_set_port_mode_dsa(struct mv88e6xxx_chip *chip, int port)
+{
+ return mv88e6xxx_set_port_mode(chip, port, MV88E6XXX_FRAME_MODE_DSA,
+ MV88E6XXX_EGRESS_MODE_UNMODIFIED,
+ MV88E6XXX_PORT_ETH_TYPE_DEFAULT);
+}
- return chip->info->ops->port_set_egress_unknowns(
- chip, port, port == upstream_port);
+static int mv88e6xxx_set_port_mode_edsa(struct mv88e6xxx_chip *chip, int port)
+{
+ return mv88e6xxx_set_port_mode(chip, port,
+ MV88E6XXX_FRAME_MODE_ETHERTYPE,
+ MV88E6XXX_EGRESS_MODE_ETHERTYPE,
+ ETH_P_EDSA);
}
-static int mv88e6xxx_setup_port_cpu(struct mv88e6xxx_chip *chip, int port)
+static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port)
{
- int err;
+ if (dsa_is_dsa_port(chip->ds, port))
+ return mv88e6xxx_set_port_mode_dsa(chip, port);
- switch (chip->info->tag_protocol) {
- case DSA_TAG_PROTO_EDSA:
- err = chip->info->ops->port_set_frame_mode(
- chip, port, MV88E6XXX_FRAME_MODE_ETHERTYPE);
- if (err)
- return err;
+ if (dsa_is_normal_port(chip->ds, port))
+ return mv88e6xxx_set_port_mode_normal(chip, port);
- err = mv88e6xxx_port_set_egress_mode(
- chip, port, PORT_CONTROL_EGRESS_ADD_TAG);
- if (err)
- return err;
+ /* Setup CPU port mode depending on its supported tag format */
+ if (chip->info->tag_protocol == DSA_TAG_PROTO_DSA)
+ return mv88e6xxx_set_port_mode_dsa(chip, port);
- if (chip->info->ops->port_set_ether_type)
- err = chip->info->ops->port_set_ether_type(
- chip, port, ETH_P_EDSA);
- break;
+ if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA)
+ return mv88e6xxx_set_port_mode_edsa(chip, port);
- case DSA_TAG_PROTO_DSA:
- err = chip->info->ops->port_set_frame_mode(
- chip, port, MV88E6XXX_FRAME_MODE_DSA);
- if (err)
- return err;
-
- err = mv88e6xxx_port_set_egress_mode(
- chip, port, PORT_CONTROL_EGRESS_UNMODIFIED);
- break;
- default:
- err = -EINVAL;
- }
+ return -EINVAL;
+}
- if (err)
- return err;
+static int mv88e6xxx_setup_message_port(struct mv88e6xxx_chip *chip, int port)
+{
+ bool message = dsa_is_dsa_port(chip->ds, port);
- return chip->info->ops->port_set_egress_unknowns(chip, port, true);
+ return mv88e6xxx_port_set_message_port(chip, port, message);
}
-static int mv88e6xxx_setup_port_normal(struct mv88e6xxx_chip *chip, int port)
+static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port)
{
- int err;
+ bool flood = port == dsa_upstream_port(chip->ds);
- err = chip->info->ops->port_set_frame_mode(
- chip, port, MV88E6XXX_FRAME_MODE_NORMAL);
- if (err)
- return err;
+ /* Upstream ports flood frames with unknown unicast or multicast DA */
+ if (chip->info->ops->port_set_egress_floods)
+ return chip->info->ops->port_set_egress_floods(chip, port,
+ flood, flood);
- return chip->info->ops->port_set_egress_unknowns(chip, port, false);
+ return 0;
+}
+
+static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
+ bool on)
+{
+ if (chip->info->ops->serdes_power)
+ return chip->info->ops->serdes_power(chip, port, on);
+
+ return 0;
}
static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
@@ -2497,39 +1852,29 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
* If this is the upstream port for this switch, enable
* forwarding of unknown unicasts and multicasts.
*/
- reg = PORT_CONTROL_IGMP_MLD_SNOOP |
- PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
- PORT_CONTROL_STATE_FORWARDING;
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
+ reg = MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP |
+ MV88E6185_PORT_CTL0_USE_TAG | MV88E6185_PORT_CTL0_USE_IP |
+ MV88E6XXX_PORT_CTL0_STATE_FORWARDING;
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
if (err)
return err;
- if (dsa_is_cpu_port(ds, port)) {
- err = mv88e6xxx_setup_port_cpu(chip, port);
- } else if (dsa_is_dsa_port(ds, port)) {
- err = mv88e6xxx_setup_port_dsa(chip, port,
- dsa_upstream_port(ds));
- } else {
- err = mv88e6xxx_setup_port_normal(chip, port);
- }
+ err = mv88e6xxx_setup_port_mode(chip, port);
if (err)
return err;
- /* If this port is connected to a SerDes, make sure the SerDes is not
- * powered down.
+ err = mv88e6xxx_setup_egress_floods(chip, port);
+ if (err)
+ return err;
+
+ /* Enable the SERDES interface for DSA and CPU ports. Normal
+ * ports SERDES are enabled when the port is enabled, thus
+ * saving a bit of power.
*/
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) {
- err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
+ if ((dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) {
+ err = mv88e6xxx_serdes_power(chip, port, true);
if (err)
return err;
- reg &= PORT_STATUS_CMODE_MASK;
- if ((reg == PORT_STATUS_CMODE_100BASE_X) ||
- (reg == PORT_STATUS_CMODE_1000BASE_X) ||
- (reg == PORT_STATUS_CMODE_SGMII)) {
- err = mv88e6xxx_serdes_power_on(chip);
- if (err < 0)
- return err;
- }
}
/* Port Control 2: don't force a good FCS, set the maximum frame size to
@@ -2538,33 +1883,25 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
* received packets as usual, disable ARP mirroring and don't send a
* copy of all transmitted/received frames on this port to the CPU.
*/
- reg = 0;
- if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
- mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
- mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
- mv88e6xxx_6185_family(chip))
- reg = PORT_CONTROL_2_MAP_DA;
-
- if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
- /* Set the upstream port this port should use */
- reg |= dsa_upstream_port(ds);
- /* enable forwarding of unknown multicast addresses to
- * the upstream port
- */
- if (port == dsa_upstream_port(ds))
- reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
- }
-
- reg |= PORT_CONTROL_2_8021Q_DISABLED;
+ err = mv88e6xxx_port_set_map_da(chip, port);
+ if (err)
+ return err;
- if (reg) {
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
+ reg = 0;
+ if (chip->info->ops->port_set_upstream_port) {
+ err = chip->info->ops->port_set_upstream_port(
+ chip, port, dsa_upstream_port(ds));
if (err)
return err;
}
- if (chip->info->ops->port_jumbo_config) {
- err = chip->info->ops->port_jumbo_config(chip, port);
+ err = mv88e6xxx_port_set_8021q_mode(chip, port,
+ MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED);
+ if (err)
+ return err;
+
+ if (chip->info->ops->port_set_jumbo_size) {
+ err = chip->info->ops->port_set_jumbo_size(chip, port, 10240);
if (err)
return err;
}
@@ -2579,35 +1916,31 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
if (dsa_is_cpu_port(ds, port))
reg = 0;
- err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
+ reg);
if (err)
return err;
/* Egress rate control 2: disable egress rate control. */
- err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL2,
+ 0x0000);
if (err)
return err;
- if (chip->info->ops->port_pause_config) {
- err = chip->info->ops->port_pause_config(chip, port);
+ if (chip->info->ops->port_pause_limit) {
+ err = chip->info->ops->port_pause_limit(chip, port, 0, 0);
if (err)
return err;
}
- if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
- mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
- mv88e6xxx_6320_family(chip)) {
- /* Port ATU control: disable limiting the number of
- * address database entries that this port is allowed
- * to use.
- */
- err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL,
- 0x0000);
- /* Priority Override: disable DA, SA and VTU priority
- * override.
- */
- err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE,
- 0x0000);
+ if (chip->info->ops->port_disable_learn_limit) {
+ err = chip->info->ops->port_disable_learn_limit(chip, port);
+ if (err)
+ return err;
+ }
+
+ if (chip->info->ops->port_disable_pri_override) {
+ err = chip->info->ops->port_disable_pri_override(chip, port);
if (err)
return err;
}
@@ -2624,10 +1957,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
return err;
}
- /* Port Control 1: disable trunking, disable sending
- * learning messages to this port.
- */
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000);
+ err = mv88e6xxx_setup_message_port(chip, port);
if (err)
return err;
@@ -2639,60 +1969,38 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
if (err)
return err;
- err = _mv88e6xxx_port_based_vlan_map(chip, port);
+ err = mv88e6xxx_port_vlan_map(chip, port);
if (err)
return err;
/* Default VLAN ID and priority: don't set a default VLAN
* ID, and set the default packet priority to zero.
*/
- return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN, 0);
}
-static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
+static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phydev)
{
+ struct mv88e6xxx_chip *chip = ds->priv;
int err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
- if (err)
- return err;
-
- err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
- if (err)
- return err;
-
- err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
- if (err)
- return err;
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_serdes_power(chip, port, true);
+ mutex_unlock(&chip->reg_lock);
- return 0;
+ return err;
}
-static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
- unsigned int msecs)
+static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port,
+ struct phy_device *phydev)
{
- const unsigned int coeff = chip->info->age_time_coeff;
- const unsigned int min = 0x01 * coeff;
- const unsigned int max = 0xff * coeff;
- u8 age_time;
- u16 val;
- int err;
-
- if (msecs < min || msecs > max)
- return -ERANGE;
-
- /* Round to nearest multiple of coeff */
- age_time = (msecs + coeff / 2) / coeff;
-
- err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
- if (err)
- return err;
-
- /* AgeTime is 11:4 bits */
- val &= ~0xff0;
- val |= age_time << 4;
+ struct mv88e6xxx_chip *chip = ds->priv;
- return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
+ mutex_lock(&chip->reg_lock);
+ if (mv88e6xxx_serdes_power(chip, port, false))
+ dev_err(chip->dev, "failed to power off SERDES\n");
+ mutex_unlock(&chip->reg_lock);
}
static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
@@ -2702,7 +2010,7 @@ static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
int err;
mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
+ err = mv88e6xxx_g1_atu_set_age_time(chip, ageing_time);
mutex_unlock(&chip->reg_lock);
return err;
@@ -2714,83 +2022,53 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
u32 upstream_port = dsa_upstream_port(ds);
int err;
- /* Enable the PHY Polling Unit if present, don't discard any packets,
- * and mask all interrupt sources.
- */
- err = mv88e6xxx_ppu_enable(chip);
- if (err)
- return err;
-
- if (chip->info->ops->g1_set_cpu_port) {
- err = chip->info->ops->g1_set_cpu_port(chip, upstream_port);
+ if (chip->info->ops->set_cpu_port) {
+ err = chip->info->ops->set_cpu_port(chip, upstream_port);
if (err)
return err;
}
- if (chip->info->ops->g1_set_egress_port) {
- err = chip->info->ops->g1_set_egress_port(chip, upstream_port);
+ if (chip->info->ops->set_egress_port) {
+ err = chip->info->ops->set_egress_port(chip, upstream_port);
if (err)
return err;
}
/* Disable remote management, and set the switch's DSA device number. */
- err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2,
- GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL2,
+ MV88E6XXX_G1_CTL2_MULTIPLE_CASCADE |
(ds->index & 0x1f));
if (err)
return err;
- /* Clear all the VTU and STU entries */
- err = _mv88e6xxx_vtu_stu_flush(chip);
- if (err < 0)
- return err;
-
- /* Set the default address aging time to 5 minutes, and
- * enable address learn messages to be sent to all message
- * ports.
- */
- err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
- GLOBAL_ATU_CONTROL_LEARN2ALL);
- if (err)
- return err;
-
- err = mv88e6xxx_g1_set_age_time(chip, 300000);
- if (err)
- return err;
-
- /* Clear all ATU entries */
- err = _mv88e6xxx_atu_flush(chip, 0, true);
- if (err)
- return err;
-
/* Configure the IP ToS mapping registers. */
- err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_0, 0x0000);
if (err)
return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_1, 0x0000);
if (err)
return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_2, 0x5555);
if (err)
return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_3, 0x5555);
if (err)
return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_4, 0xaaaa);
if (err)
return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_5, 0xaaaa);
if (err)
return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_6, 0xffff);
if (err)
return err;
- err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_7, 0xffff);
if (err)
return err;
/* Configure the IEEE 802.1p priority mapping register. */
- err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, 0xfa41);
if (err)
return err;
@@ -2800,8 +2078,9 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
return err;
/* Clear the statistics counters for all ports */
- err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
- GLOBAL_STATS_OP_FLUSH_ALL);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP,
+ MV88E6XXX_G1_STATS_OP_BUSY |
+ MV88E6XXX_G1_STATS_OP_FLUSH_ALL);
if (err)
return err;
@@ -2820,7 +2099,7 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
int i;
chip->ds = ds;
- ds->slave_mii_bus = chip->mdio_bus;
+ ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip);
mutex_lock(&chip->reg_lock);
@@ -2843,6 +2122,26 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
goto unlock;
}
+ err = mv88e6xxx_irl_setup(chip);
+ if (err)
+ goto unlock;
+
+ err = mv88e6xxx_phy_setup(chip);
+ if (err)
+ goto unlock;
+
+ err = mv88e6xxx_vtu_setup(chip);
+ if (err)
+ goto unlock;
+
+ err = mv88e6xxx_pvt_setup(chip);
+ if (err)
+ goto unlock;
+
+ err = mv88e6xxx_atu_setup(chip);
+ if (err)
+ goto unlock;
+
/* Some generations have the configuration of sending reserved
* management frames to the CPU in global2, others in
* global1. Hence it does not fit the two setup functions
@@ -2877,50 +2176,64 @@ static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
{
- struct mv88e6xxx_chip *chip = bus->priv;
+ struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
+ struct mv88e6xxx_chip *chip = mdio_bus->chip;
u16 val;
int err;
- if (phy >= mv88e6xxx_num_ports(chip))
- return 0xffff;
+ if (!chip->info->ops->phy_read)
+ return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_phy_read(chip, phy, reg, &val);
+ err = chip->info->ops->phy_read(chip, bus, phy, reg, &val);
mutex_unlock(&chip->reg_lock);
+ if (reg == MII_PHYSID2) {
+ /* Some internal PHYS don't have a model number. Use
+ * the mv88e6390 family model number instead.
+ */
+ if (!(val & 0x3f0))
+ val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4;
+ }
+
return err ? err : val;
}
static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
{
- struct mv88e6xxx_chip *chip = bus->priv;
+ struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
+ struct mv88e6xxx_chip *chip = mdio_bus->chip;
int err;
- if (phy >= mv88e6xxx_num_ports(chip))
- return 0xffff;
+ if (!chip->info->ops->phy_write)
+ return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_phy_write(chip, phy, reg, val);
+ err = chip->info->ops->phy_write(chip, bus, phy, reg, val);
mutex_unlock(&chip->reg_lock);
return err;
}
static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
- struct device_node *np)
+ struct device_node *np,
+ bool external)
{
static int index;
+ struct mv88e6xxx_mdio_bus *mdio_bus;
struct mii_bus *bus;
int err;
- if (np)
- chip->mdio_np = of_get_child_by_name(np, "mdio");
-
- bus = devm_mdiobus_alloc(chip->dev);
+ bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus));
if (!bus)
return -ENOMEM;
- bus->priv = (void *)chip;
+ mdio_bus = bus->priv;
+ mdio_bus->bus = bus;
+ mdio_bus->chip = chip;
+ INIT_LIST_HEAD(&mdio_bus->list);
+ mdio_bus->external = external;
+
if (np) {
bus->name = np->full_name;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
@@ -2933,183 +2246,73 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
bus->write = mv88e6xxx_mdio_write;
bus->parent = chip->dev;
- if (chip->mdio_np)
- err = of_mdiobus_register(bus, chip->mdio_np);
+ if (np)
+ err = of_mdiobus_register(bus, np);
else
err = mdiobus_register(bus);
if (err) {
dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
- goto out;
+ return err;
}
- chip->mdio_bus = bus;
-
- return 0;
-
-out:
- if (chip->mdio_np)
- of_node_put(chip->mdio_np);
-
- return err;
-}
-
-static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
-
-{
- struct mii_bus *bus = chip->mdio_bus;
-
- mdiobus_unregister(bus);
-
- if (chip->mdio_np)
- of_node_put(chip->mdio_np);
-}
-
-#ifdef CONFIG_NET_DSA_HWMON
-
-static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
-{
- struct mv88e6xxx_chip *chip = ds->priv;
- u16 val;
- int ret;
-
- *temp = 0;
-
- mutex_lock(&chip->reg_lock);
-
- ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6);
- if (ret < 0)
- goto error;
-
- /* Enable temperature sensor */
- ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
- if (ret < 0)
- goto error;
-
- ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5));
- if (ret < 0)
- goto error;
-
- /* Wait for temperature to stabilize */
- usleep_range(10000, 12000);
-
- ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
- if (ret < 0)
- goto error;
-
- /* Disable temperature sensor */
- ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5));
- if (ret < 0)
- goto error;
-
- *temp = ((val & 0x1f) - 5) * 5;
-
-error:
- mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0);
- mutex_unlock(&chip->reg_lock);
- return ret;
-}
-
-static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
-{
- struct mv88e6xxx_chip *chip = ds->priv;
- int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
- u16 val;
- int ret;
-
- *temp = 0;
-
- mutex_lock(&chip->reg_lock);
- ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val);
- mutex_unlock(&chip->reg_lock);
- if (ret < 0)
- return ret;
- *temp = (val & 0xff) - 25;
+ if (external)
+ list_add_tail(&mdio_bus->list, &chip->mdios);
+ else
+ list_add(&mdio_bus->list, &chip->mdios);
return 0;
}
-static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
-{
- struct mv88e6xxx_chip *chip = ds->priv;
-
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
- return -EOPNOTSUPP;
-
- if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
- return mv88e63xx_get_temp(ds, temp);
-
- return mv88e61xx_get_temp(ds, temp);
-}
+static const struct of_device_id mv88e6xxx_mdio_external_match[] = {
+ { .compatible = "marvell,mv88e6xxx-mdio-external",
+ .data = (void *)true },
+ { },
+};
-static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
+static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip,
+ struct device_node *np)
{
- struct mv88e6xxx_chip *chip = ds->priv;
- int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
- u16 val;
- int ret;
-
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
- return -EOPNOTSUPP;
-
- *temp = 0;
+ const struct of_device_id *match;
+ struct device_node *child;
+ int err;
- mutex_lock(&chip->reg_lock);
- ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
- mutex_unlock(&chip->reg_lock);
- if (ret < 0)
- return ret;
+ /* Always register one mdio bus for the internal/default mdio
+ * bus. This maybe represented in the device tree, but is
+ * optional.
+ */
+ child = of_get_child_by_name(np, "mdio");
+ err = mv88e6xxx_mdio_register(chip, child, false);
+ if (err)
+ return err;
- *temp = (((val >> 8) & 0x1f) * 5) - 25;
+ /* Walk the device tree, and see if there are any other nodes
+ * which say they are compatible with the external mdio
+ * bus.
+ */
+ for_each_available_child_of_node(np, child) {
+ match = of_match_node(mv88e6xxx_mdio_external_match, child);
+ if (match) {
+ err = mv88e6xxx_mdio_register(chip, child, true);
+ if (err)
+ return err;
+ }
+ }
return 0;
}
-static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
-{
- struct mv88e6xxx_chip *chip = ds->priv;
- int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
- u16 val;
- int err;
-
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
- return -EOPNOTSUPP;
-
- mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
- if (err)
- goto unlock;
- temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
- err = mv88e6xxx_phy_page_write(chip, phy, 6, 26,
- (val & 0xe0ff) | (temp << 8));
-unlock:
- mutex_unlock(&chip->reg_lock);
-
- return err;
-}
+static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
-static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
{
- struct mv88e6xxx_chip *chip = ds->priv;
- int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
- u16 val;
- int ret;
-
- if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
- return -EOPNOTSUPP;
-
- *alarm = false;
-
- mutex_lock(&chip->reg_lock);
- ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
- mutex_unlock(&chip->reg_lock);
- if (ret < 0)
- return ret;
+ struct mv88e6xxx_mdio_bus *mdio_bus;
+ struct mii_bus *bus;
- *alarm = !!(val & 0x40);
+ list_for_each_entry(mdio_bus, &chip->mdios, list) {
+ bus = mdio_bus->bus;
- return 0;
+ mdiobus_unregister(bus);
+ }
}
-#endif /* CONFIG_NET_DSA_HWMON */
static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
{
@@ -3160,40 +2363,47 @@ static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
static const struct mv88e6xxx_ops mv88e6085_ops = {
/* MV88E6XXX_FAMILY_6097 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
- .phy_read = mv88e6xxx_phy_ppu_read,
- .phy_write = mv88e6xxx_phy_ppu_write,
+ .phy_read = mv88e6185_phy_ppu_read,
+ .phy_write = mv88e6185_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.ppu_enable = mv88e6185_g1_ppu_enable,
.ppu_disable = mv88e6185_g1_ppu_disable,
.reset = mv88e6185_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6095_ops = {
/* MV88E6XXX_FAMILY_6095 */
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
- .phy_read = mv88e6xxx_phy_ppu_read,
- .phy_write = mv88e6xxx_phy_ppu_write,
+ .phy_read = mv88e6185_phy_ppu_read,
+ .phy_write = mv88e6185_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
.port_set_frame_mode = mv88e6085_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
@@ -3202,10 +2412,13 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
.ppu_enable = mv88e6185_g1_ppu_enable,
.ppu_disable = mv88e6185_g1_ppu_disable,
.reset = mv88e6185_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6097_ops = {
/* MV88E6XXX_FAMILY_6097 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3214,113 +2427,175 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6123_ops = {
/* MV88E6XXX_FAMILY_6165 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
- .phy_read = mv88e6xxx_read,
- .phy_write = mv88e6xxx_write,
+ .phy_read = mv88e6xxx_g2_smi_phy_read,
+ .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
.port_set_frame_mode = mv88e6085_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns,
- .stats_snapshot = mv88e6xxx_g1_stats_snapshot,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6131_ops = {
/* MV88E6XXX_FAMILY_6185 */
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
- .phy_read = mv88e6xxx_phy_ppu_read,
- .phy_write = mv88e6xxx_phy_ppu_write,
+ .phy_read = mv88e6185_phy_ppu_read,
+ .phy_write = mv88e6185_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6185_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_upstream_port = mv88e6095_port_set_upstream_port,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
.stats_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.ppu_enable = mv88e6185_g1_ppu_enable,
.ppu_disable = mv88e6185_g1_ppu_disable,
.reset = mv88e6185_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+};
+
+static const struct mv88e6xxx_ops mv88e6141_ops = {
+ /* MV88E6XXX_FAMILY_6341 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
+ .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom8,
+ .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
+ .phy_read = mv88e6xxx_g2_smi_phy_read,
+ .phy_write = mv88e6xxx_g2_smi_phy_write,
+ .port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
+ .port_set_speed = mv88e6390_port_set_speed,
+ .port_tag_remap = mv88e6095_port_tag_remap,
+ .port_set_frame_mode = mv88e6351_port_set_frame_mode,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
+ .port_set_ether_type = mv88e6351_port_set_ether_type,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6320_stats_get_sset_count,
+ .stats_get_strings = mv88e6320_stats_get_strings,
+ .stats_get_stats = mv88e6390_stats_get_stats,
+ .set_cpu_port = mv88e6390_g1_set_cpu_port,
+ .set_egress_port = mv88e6390_g1_set_egress_port,
+ .watchdog_ops = &mv88e6390_watchdog_ops,
+ .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+ .reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6161_ops = {
/* MV88E6XXX_FAMILY_6165 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
- .phy_read = mv88e6xxx_read,
- .phy_write = mv88e6xxx_write,
+ .phy_read = mv88e6xxx_g2_smi_phy_read,
+ .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
- .stats_snapshot = mv88e6xxx_g1_stats_snapshot,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6165_ops = {
/* MV88E6XXX_FAMILY_6165 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
- .phy_read = mv88e6xxx_read,
- .phy_write = mv88e6xxx_write,
+ .phy_read = mv88e6165_phy_read,
+ .phy_write = mv88e6165_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6171_ops = {
/* MV88E6XXX_FAMILY_6351 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3330,23 +2605,29 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6172_ops = {
/* MV88E6XXX_FAMILY_6352 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
@@ -3358,23 +2639,30 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.port_set_speed = mv88e6352_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .serdes_power = mv88e6352_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6175_ops = {
/* MV88E6XXX_FAMILY_6351 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3384,23 +2672,29 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6176_ops = {
/* MV88E6XXX_FAMILY_6352 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
@@ -3412,46 +2706,59 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.port_set_speed = mv88e6352_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .serdes_power = mv88e6352_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6185_ops = {
/* MV88E6XXX_FAMILY_6185 */
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
- .phy_read = mv88e6xxx_phy_ppu_read,
- .phy_write = mv88e6xxx_phy_ppu_write,
+ .phy_read = mv88e6185_phy_ppu_read,
+ .phy_write = mv88e6185_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
.port_set_frame_mode = mv88e6085_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6185_port_set_egress_floods,
.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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.ppu_enable = mv88e6185_g1_ppu_enable,
.ppu_disable = mv88e6185_g1_ppu_disable,
.reset = mv88e6185_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6190_ops = {
/* MV88E6XXX_FAMILY_6390 */
+ .irl_init_all = mv88e6390_g2_irl_init_all,
+ .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom8,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3461,22 +2768,31 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
.port_set_speed = mv88e6390_port_set_speed,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_pause_config = mv88e6390_port_pause_config,
+ .port_pause_limit = mv88e6390_port_pause_limit,
+ .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 = mv88e6390_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,
- .g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6390_g1_set_egress_port,
+ .set_cpu_port = mv88e6390_g1_set_cpu_port,
+ .set_egress_port = mv88e6390_g1_set_egress_port,
+ .watchdog_ops = &mv88e6390_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6390_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
+ .serdes_power = mv88e6390_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6190x_ops = {
/* MV88E6XXX_FAMILY_6390 */
+ .irl_init_all = mv88e6390_g2_irl_init_all,
+ .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom8,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3486,22 +2802,31 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
.port_set_speed = mv88e6390x_port_set_speed,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_pause_config = mv88e6390_port_pause_config,
+ .port_pause_limit = mv88e6390_port_pause_limit,
+ .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 = mv88e6390_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,
- .g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6390_g1_set_egress_port,
+ .set_cpu_port = mv88e6390_g1_set_cpu_port,
+ .set_egress_port = mv88e6390_g1_set_egress_port,
+ .watchdog_ops = &mv88e6390_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6390_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
+ .serdes_power = mv88e6390_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6191_ops = {
/* MV88E6XXX_FAMILY_6390 */
+ .irl_init_all = mv88e6390_g2_irl_init_all,
+ .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom8,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3511,22 +2836,29 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
.port_set_speed = mv88e6390_port_set_speed,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_pause_config = mv88e6390_port_pause_config,
+ .port_pause_limit = mv88e6390_port_pause_limit,
+ .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 = mv88e6390_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,
- .g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6390_g1_set_egress_port,
+ .set_cpu_port = mv88e6390_g1_set_cpu_port,
+ .set_egress_port = mv88e6390_g1_set_egress_port,
+ .watchdog_ops = &mv88e6390_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6390_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
+ .serdes_power = mv88e6390_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6240_ops = {
/* MV88E6XXX_FAMILY_6352 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
@@ -3538,23 +2870,32 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.port_set_speed = mv88e6352_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .serdes_power = mv88e6352_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6290_ops = {
/* MV88E6XXX_FAMILY_6390 */
+ .irl_init_all = mv88e6390_g2_irl_init_all,
+ .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom8,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3564,22 +2905,30 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
.port_set_speed = mv88e6390_port_set_speed,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_pause_config = mv88e6390_port_pause_config,
+ .port_pause_limit = mv88e6390_port_pause_limit,
+ .port_set_cmode = mv88e6390x_port_set_cmode,
+ .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 = mv88e6390_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,
- .g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6390_g1_set_egress_port,
+ .set_cpu_port = mv88e6390_g1_set_cpu_port,
+ .set_egress_port = mv88e6390_g1_set_egress_port,
+ .watchdog_ops = &mv88e6390_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6390_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
+ .serdes_power = mv88e6390_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6320_ops = {
/* MV88E6XXX_FAMILY_6320 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
@@ -3590,23 +2939,28 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6320_stats_get_sset_count,
.stats_get_strings = mv88e6320_stats_get_strings,
.stats_get_stats = mv88e6320_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6321_ops = {
/* MV88E6XXX_FAMILY_6321 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
@@ -3617,22 +2971,61 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6320_stats_get_sset_count,
.stats_get_strings = mv88e6320_stats_get_strings,
.stats_get_stats = mv88e6320_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+};
+
+static const struct mv88e6xxx_ops mv88e6341_ops = {
+ /* MV88E6XXX_FAMILY_6341 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
+ .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom8,
+ .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
+ .phy_read = mv88e6xxx_g2_smi_phy_read,
+ .phy_write = mv88e6xxx_g2_smi_phy_write,
+ .port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
+ .port_set_speed = mv88e6390_port_set_speed,
+ .port_tag_remap = mv88e6095_port_tag_remap,
+ .port_set_frame_mode = mv88e6351_port_set_frame_mode,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
+ .port_set_ether_type = mv88e6351_port_set_ether_type,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6320_stats_get_sset_count,
+ .stats_get_strings = mv88e6320_stats_get_strings,
+ .stats_get_stats = mv88e6390_stats_get_stats,
+ .set_cpu_port = mv88e6390_g1_set_cpu_port,
+ .set_egress_port = mv88e6390_g1_set_egress_port,
+ .watchdog_ops = &mv88e6390_watchdog_ops,
+ .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+ .reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6350_ops = {
/* MV88E6XXX_FAMILY_6351 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3642,23 +3035,29 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6351_ops = {
/* MV88E6XXX_FAMILY_6351 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3668,23 +3067,29 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
.port_set_speed = mv88e6185_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
};
static const struct mv88e6xxx_ops mv88e6352_ops = {
/* MV88E6XXX_FAMILY_6352 */
+ .irl_init_all = mv88e6352_g2_irl_init_all,
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
@@ -3696,23 +3101,32 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.port_set_speed = mv88e6352_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6097_port_pause_config,
+ .port_pause_limit = mv88e6097_port_pause_limit,
+ .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_get_sset_count = mv88e6095_stats_get_sset_count,
.stats_get_strings = mv88e6095_stats_get_strings,
.stats_get_stats = mv88e6095_stats_get_stats,
- .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .set_egress_port = mv88e6095_g1_set_egress_port,
+ .watchdog_ops = &mv88e6097_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .serdes_power = mv88e6352_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6390_ops = {
/* MV88E6XXX_FAMILY_6390 */
+ .irl_init_all = mv88e6390_g2_irl_init_all,
+ .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom8,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3722,24 +3136,34 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
.port_set_speed = mv88e6390_port_set_speed,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6390_port_pause_config,
+ .port_pause_limit = mv88e6390_port_pause_limit,
+ .port_set_cmode = mv88e6390x_port_set_cmode,
+ .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 = mv88e6390_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,
- .g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6390_g1_set_egress_port,
+ .set_cpu_port = mv88e6390_g1_set_cpu_port,
+ .set_egress_port = mv88e6390_g1_set_egress_port,
+ .watchdog_ops = &mv88e6390_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6390_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
+ .serdes_power = mv88e6390_serdes_power,
};
static const struct mv88e6xxx_ops mv88e6390x_ops = {
/* MV88E6XXX_FAMILY_6390 */
+ .irl_init_all = mv88e6390_g2_irl_init_all,
+ .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom8,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
@@ -3749,417 +3173,485 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
.port_set_speed = mv88e6390x_port_set_speed,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_egress_floods = mv88e6352_port_set_egress_floods,
.port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
- .port_pause_config = mv88e6390_port_pause_config,
- .stats_snapshot = mv88e6390_g1_stats_snapshot,
- .stats_set_histogram = mv88e6390_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,
- .g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6390_g1_set_egress_port,
- .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
- .reset = mv88e6352_g1_reset,
-};
-
-static const struct mv88e6xxx_ops mv88e6391_ops = {
- /* MV88E6XXX_FAMILY_6390 */
- .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
- .phy_read = mv88e6xxx_g2_smi_phy_read,
- .phy_write = mv88e6xxx_g2_smi_phy_write,
- .port_set_link = mv88e6xxx_port_set_link,
- .port_set_duplex = mv88e6xxx_port_set_duplex,
- .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
- .port_set_speed = mv88e6390_port_set_speed,
- .port_tag_remap = mv88e6390_port_tag_remap,
- .port_set_frame_mode = mv88e6351_port_set_frame_mode,
- .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
- .port_set_ether_type = mv88e6351_port_set_ether_type,
- .port_pause_config = mv88e6390_port_pause_config,
+ .port_pause_limit = mv88e6390_port_pause_limit,
+ .port_set_cmode = mv88e6390x_port_set_cmode,
+ .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 = mv88e6390_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,
- .g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
- .g1_set_egress_port = mv88e6390_g1_set_egress_port,
+ .set_cpu_port = mv88e6390_g1_set_cpu_port,
+ .set_egress_port = mv88e6390_g1_set_egress_port,
+ .watchdog_ops = &mv88e6390_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
.reset = mv88e6352_g1_reset,
+ .vtu_getnext = mv88e6390_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
+ .serdes_power = mv88e6390_serdes_power,
};
-static int mv88e6xxx_verify_madatory_ops(struct mv88e6xxx_chip *chip,
- const struct mv88e6xxx_ops *ops)
-{
- if (!ops->port_set_frame_mode) {
- dev_err(chip->dev, "Missing port_set_frame_mode");
- return -EINVAL;
- }
-
- if (!ops->port_set_egress_unknowns) {
- dev_err(chip->dev, "Missing port_set_egress_mode");
- return -EINVAL;
- }
-
- return 0;
-}
-
static const struct mv88e6xxx_info mv88e6xxx_table[] = {
[MV88E6085] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085,
.family = MV88E6XXX_FAMILY_6097,
.name = "Marvell 88E6085",
.num_databases = 4096,
.num_ports = 10,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 8,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6097,
.ops = &mv88e6085_ops,
},
[MV88E6095] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6095,
.family = MV88E6XXX_FAMILY_6095,
.name = "Marvell 88E6095/88E6095F",
.num_databases = 256,
.num_ports = 11,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 8,
+ .atu_move_port_mask = 0xf,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6095,
.ops = &mv88e6095_ops,
},
[MV88E6097] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6097,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6097,
.family = MV88E6XXX_FAMILY_6097,
.name = "Marvell 88E6097/88E6097F",
.num_databases = 4096,
.num_ports = 11,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 8,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6097,
.ops = &mv88e6097_ops,
},
[MV88E6123] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6123,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6123",
.num_databases = 4096,
.num_ports = 3,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
- .tag_protocol = DSA_TAG_PROTO_DSA,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
+ .tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
.ops = &mv88e6123_ops,
},
[MV88E6131] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6131,
.family = MV88E6XXX_FAMILY_6185,
.name = "Marvell 88E6131",
.num_databases = 256,
.num_ports = 8,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
.ops = &mv88e6131_ops,
},
+ [MV88E6141] = {
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6141,
+ .family = MV88E6XXX_FAMILY_6341,
+ .name = "Marvell 88E6341",
+ .num_databases = 4096,
+ .num_ports = 6,
+ .max_vid = 4095,
+ .port_base_addr = 0x10,
+ .global1_addr = 0x1b,
+ .age_time_coeff = 3750,
+ .atu_move_port_mask = 0x1f,
+ .pvt = true,
+ .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .flags = MV88E6XXX_FLAGS_FAMILY_6341,
+ .ops = &mv88e6141_ops,
+ },
+
[MV88E6161] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6161,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6161",
.num_databases = 4096,
.num_ports = 6,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
- .tag_protocol = DSA_TAG_PROTO_DSA,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
+ .tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
.ops = &mv88e6161_ops,
},
[MV88E6165] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6165,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6165",
.num_databases = 4096,
.num_ports = 6,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
.ops = &mv88e6165_ops,
},
[MV88E6171] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6171,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6171",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
.ops = &mv88e6171_ops,
},
[MV88E6172] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6172,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6172",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
.ops = &mv88e6172_ops,
},
[MV88E6175] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6175,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6175",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
.ops = &mv88e6175_ops,
},
[MV88E6176] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6176,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6176",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
.ops = &mv88e6176_ops,
},
[MV88E6185] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6185,
.family = MV88E6XXX_FAMILY_6185,
.name = "Marvell 88E6185",
.num_databases = 256,
.num_ports = 10,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 8,
+ .atu_move_port_mask = 0xf,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
.ops = &mv88e6185_ops,
},
[MV88E6190] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6190,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6190,
.family = MV88E6XXX_FAMILY_6390,
.name = "Marvell 88E6190",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
.tag_protocol = DSA_TAG_PROTO_DSA,
- .age_time_coeff = 15000,
+ .age_time_coeff = 3750,
.g1_irqs = 9,
+ .pvt = true,
+ .atu_move_port_mask = 0x1f,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6190_ops,
},
[MV88E6190X] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6190X,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6190X,
.family = MV88E6XXX_FAMILY_6390,
.name = "Marvell 88E6190X",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
- .age_time_coeff = 15000,
+ .age_time_coeff = 3750,
.g1_irqs = 9,
+ .atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6190x_ops,
},
[MV88E6191] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6191,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6191,
.family = MV88E6XXX_FAMILY_6390,
.name = "Marvell 88E6191",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
- .age_time_coeff = 15000,
+ .age_time_coeff = 3750,
.g1_irqs = 9,
+ .atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
- .ops = &mv88e6391_ops,
+ .ops = &mv88e6191_ops,
},
[MV88E6240] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6240,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6240",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
.ops = &mv88e6240_ops,
},
[MV88E6290] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6290,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6290,
.family = MV88E6XXX_FAMILY_6390,
.name = "Marvell 88E6290",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
- .age_time_coeff = 15000,
+ .age_time_coeff = 3750,
.g1_irqs = 9,
+ .atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6290_ops,
},
[MV88E6320] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6320,
.family = MV88E6XXX_FAMILY_6320,
.name = "Marvell 88E6320",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 8,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
.ops = &mv88e6320_ops,
},
[MV88E6321] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6321,
.family = MV88E6XXX_FAMILY_6320,
.name = "Marvell 88E6321",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 8,
+ .atu_move_port_mask = 0xf,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
.ops = &mv88e6321_ops,
},
+ [MV88E6341] = {
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6341,
+ .family = MV88E6XXX_FAMILY_6341,
+ .name = "Marvell 88E6341",
+ .num_databases = 4096,
+ .num_ports = 6,
+ .max_vid = 4095,
+ .port_base_addr = 0x10,
+ .global1_addr = 0x1b,
+ .age_time_coeff = 3750,
+ .atu_move_port_mask = 0x1f,
+ .pvt = true,
+ .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .flags = MV88E6XXX_FLAGS_FAMILY_6341,
+ .ops = &mv88e6341_ops,
+ },
+
[MV88E6350] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6350,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6350",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
.ops = &mv88e6350_ops,
},
[MV88E6351] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6351,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6351",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
.ops = &mv88e6351_ops,
},
[MV88E6352] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6352,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6352",
.num_databases = 4096,
.num_ports = 7,
+ .max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.age_time_coeff = 15000,
.g1_irqs = 9,
+ .atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
.ops = &mv88e6352_ops,
},
[MV88E6390] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6390,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390,
.family = MV88E6XXX_FAMILY_6390,
.name = "Marvell 88E6390",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
- .age_time_coeff = 15000,
+ .age_time_coeff = 3750,
.g1_irqs = 9,
+ .atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6390_ops,
},
[MV88E6390X] = {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6390X,
+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390X,
.family = MV88E6XXX_FAMILY_6390,
.name = "Marvell 88E6390X",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
- .age_time_coeff = 15000,
+ .age_time_coeff = 3750,
.g1_irqs = 9,
+ .atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6390x_ops,
@@ -4185,13 +3677,13 @@ static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
int err;
mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
+ err = mv88e6xxx_port_read(chip, 0, MV88E6XXX_PORT_SWITCH_ID, &id);
mutex_unlock(&chip->reg_lock);
if (err)
return err;
- prod_num = (id & 0xfff0) >> 4;
- rev = id & 0x000f;
+ prod_num = id & MV88E6XXX_PORT_SWITCH_ID_PROD_MASK;
+ rev = id & MV88E6XXX_PORT_SWITCH_ID_REV_MASK;
info = mv88e6xxx_lookup_info(prod_num);
if (!info)
@@ -4221,29 +3713,14 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
chip->dev = dev;
mutex_init(&chip->reg_lock);
+ INIT_LIST_HEAD(&chip->mdios);
return chip;
}
-static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
-{
- if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
- mv88e6xxx_ppu_state_init(chip);
-}
-
-static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
-{
- if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
- mv88e6xxx_ppu_state_destroy(chip);
-}
-
static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
struct mii_bus *bus, int sw_addr)
{
- /* ADDR[0] pin is unavailable externally and considered zero */
- if (sw_addr & 0x1)
- return -EINVAL;
-
if (sw_addr == 0)
chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
@@ -4299,7 +3776,7 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
mv88e6xxx_phy_init(chip);
- err = mv88e6xxx_mdio_register(chip, NULL);
+ err = mv88e6xxx_mdios_register(chip, NULL);
if (err)
goto free;
@@ -4331,8 +3808,9 @@ static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
mutex_lock(&chip->reg_lock);
if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
- GLOBAL_ATU_DATA_STATE_MC_STATIC))
- netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n");
+ MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC))
+ dev_err(ds->dev, "p%d: failed to load multicast MAC address\n",
+ port);
mutex_unlock(&chip->reg_lock);
}
@@ -4344,7 +3822,7 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
- GLOBAL_ATU_DATA_STATE_UNUSED);
+ MV88E6XXX_G1_ATU_DATA_STATE_UNUSED);
mutex_unlock(&chip->reg_lock);
return err;
@@ -4352,7 +3830,7 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_mdb *mdb,
- int (*cb)(struct switchdev_obj *obj))
+ switchdev_obj_dump_cb_t *cb)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
@@ -4364,7 +3842,7 @@ static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
return err;
}
-static struct dsa_switch_ops mv88e6xxx_switch_ops = {
+static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.probe = mv88e6xxx_drv_probe,
.get_tag_protocol = mv88e6xxx_get_tag_protocol,
.setup = mv88e6xxx_setup,
@@ -4373,14 +3851,10 @@ static struct dsa_switch_ops mv88e6xxx_switch_ops = {
.get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count,
+ .port_enable = mv88e6xxx_port_enable,
+ .port_disable = mv88e6xxx_port_disable,
.set_eee = mv88e6xxx_set_eee,
.get_eee = mv88e6xxx_get_eee,
-#ifdef CONFIG_NET_DSA_HWMON
- .get_temp = mv88e6xxx_get_temp,
- .get_temp_limit = mv88e6xxx_get_temp_limit,
- .set_temp_limit = mv88e6xxx_set_temp_limit,
- .get_temp_alarm = mv88e6xxx_get_temp_alarm,
-#endif
.get_eeprom_len = mv88e6xxx_get_eeprom_len,
.get_eeprom = mv88e6xxx_get_eeprom,
.set_eeprom = mv88e6xxx_set_eeprom,
@@ -4404,25 +3878,31 @@ static struct dsa_switch_ops mv88e6xxx_switch_ops = {
.port_mdb_add = mv88e6xxx_port_mdb_add,
.port_mdb_del = mv88e6xxx_port_mdb_del,
.port_mdb_dump = mv88e6xxx_port_mdb_dump,
+ .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join,
+ .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave,
+};
+
+static struct dsa_switch_driver mv88e6xxx_switch_drv = {
+ .ops = &mv88e6xxx_switch_ops,
};
-static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
- struct device_node *np)
+static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
{
struct device *dev = chip->dev;
struct dsa_switch *ds;
- ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
+ ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip));
if (!ds)
return -ENOMEM;
- ds->dev = dev;
ds->priv = chip;
ds->ops = &mv88e6xxx_switch_ops;
+ ds->ageing_time_min = chip->info->age_time_coeff;
+ ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
dev_set_drvdata(dev, ds);
- return dsa_register_switch(ds, np);
+ return dsa_register_switch(ds);
}
static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
@@ -4449,10 +3929,6 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
chip->info = compat_info;
- err = mv88e6xxx_verify_madatory_ops(chip, chip->info->ops);
- if (err)
- return err;
-
err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
if (err)
return err;
@@ -4502,18 +3978,18 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
}
}
- err = mv88e6xxx_mdio_register(chip, np);
+ err = mv88e6xxx_mdios_register(chip, np);
if (err)
goto out_g2_irq;
- err = mv88e6xxx_register_switch(chip, np);
+ err = mv88e6xxx_register_switch(chip);
if (err)
goto out_mdio;
return 0;
out_mdio:
- mv88e6xxx_mdio_unregister(chip);
+ mv88e6xxx_mdios_unregister(chip);
out_g2_irq:
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0)
mv88e6xxx_g2_irq_free(chip);
@@ -4534,7 +4010,7 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
mv88e6xxx_phy_destroy(chip);
mv88e6xxx_unregister_switch(chip);
- mv88e6xxx_mdio_unregister(chip);
+ mv88e6xxx_mdios_unregister(chip);
if (chip->irq > 0) {
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
@@ -4568,7 +4044,7 @@ static struct mdio_driver mv88e6xxx_driver = {
static int __init mv88e6xxx_init(void)
{
- register_switch_driver(&mv88e6xxx_switch_ops);
+ register_switch_driver(&mv88e6xxx_switch_drv);
return mdio_driver_register(&mv88e6xxx_driver);
}
module_init(mv88e6xxx_init);
@@ -4576,7 +4052,7 @@ module_init(mv88e6xxx_init);
static void __exit mv88e6xxx_cleanup(void)
{
mdio_driver_unregister(&mv88e6xxx_driver);
- unregister_switch_driver(&mv88e6xxx_switch_ops);
+ unregister_switch_driver(&mv88e6xxx_switch_drv);
}
module_exit(mv88e6xxx_cleanup);
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
new file mode 100644
index 000000000000..086444016352
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -0,0 +1,518 @@
+/*
+ * Marvell 88E6xxx Ethernet switch single-chip definition
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _MV88E6XXX_CHIP_H
+#define _MV88E6XXX_CHIP_H
+
+#include <linux/if_vlan.h>
+#include <linux/irq.h>
+#include <linux/gpio/consumer.h>
+#include <linux/phy.h>
+#include <net/dsa.h>
+
+#ifndef UINT64_MAX
+#define UINT64_MAX (u64)(~((u64)0))
+#endif
+
+#define SMI_CMD 0x00
+#define SMI_CMD_BUSY BIT(15)
+#define SMI_CMD_CLAUSE_22 BIT(12)
+#define SMI_CMD_OP_22_WRITE ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
+#define SMI_CMD_OP_22_READ ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
+#define SMI_CMD_OP_45_WRITE_ADDR ((0 << 10) | SMI_CMD_BUSY)
+#define SMI_CMD_OP_45_WRITE_DATA ((1 << 10) | SMI_CMD_BUSY)
+#define SMI_CMD_OP_45_READ_DATA ((2 << 10) | SMI_CMD_BUSY)
+#define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY)
+#define SMI_DATA 0x01
+
+#define MV88E6XXX_N_FID 4096
+
+/* PVT limits for 4-bit port and 5-bit switch */
+#define MV88E6XXX_MAX_PVT_SWITCHES 32
+#define MV88E6XXX_MAX_PVT_PORTS 16
+
+enum mv88e6xxx_egress_mode {
+ MV88E6XXX_EGRESS_MODE_UNMODIFIED,
+ MV88E6XXX_EGRESS_MODE_UNTAGGED,
+ MV88E6XXX_EGRESS_MODE_TAGGED,
+ MV88E6XXX_EGRESS_MODE_ETHERTYPE,
+};
+
+enum mv88e6xxx_frame_mode {
+ MV88E6XXX_FRAME_MODE_NORMAL,
+ MV88E6XXX_FRAME_MODE_DSA,
+ MV88E6XXX_FRAME_MODE_PROVIDER,
+ MV88E6XXX_FRAME_MODE_ETHERTYPE,
+};
+
+/* List of supported models */
+enum mv88e6xxx_model {
+ MV88E6085,
+ MV88E6095,
+ MV88E6097,
+ MV88E6123,
+ MV88E6131,
+ MV88E6141,
+ MV88E6161,
+ MV88E6165,
+ MV88E6171,
+ MV88E6172,
+ MV88E6175,
+ MV88E6176,
+ MV88E6185,
+ MV88E6190,
+ MV88E6190X,
+ MV88E6191,
+ MV88E6240,
+ MV88E6290,
+ MV88E6320,
+ MV88E6321,
+ MV88E6341,
+ MV88E6350,
+ MV88E6351,
+ MV88E6352,
+ MV88E6390,
+ MV88E6390X,
+};
+
+enum mv88e6xxx_family {
+ MV88E6XXX_FAMILY_NONE,
+ MV88E6XXX_FAMILY_6065, /* 6031 6035 6061 6065 */
+ MV88E6XXX_FAMILY_6095, /* 6092 6095 */
+ MV88E6XXX_FAMILY_6097, /* 6046 6085 6096 6097 */
+ MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */
+ MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */
+ MV88E6XXX_FAMILY_6320, /* 6320 6321 */
+ MV88E6XXX_FAMILY_6341, /* 6141 6341 */
+ MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */
+ MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */
+ MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */
+};
+
+enum mv88e6xxx_cap {
+ /* Energy Efficient Ethernet.
+ */
+ MV88E6XXX_CAP_EEE,
+
+ /* Multi-chip Addressing Mode.
+ * Some chips respond to only 2 registers of its own SMI device address
+ * when it is non-zero, and use indirect access to internal registers.
+ */
+ MV88E6XXX_CAP_SMI_CMD, /* (0x00) SMI Command */
+ MV88E6XXX_CAP_SMI_DATA, /* (0x01) SMI Data */
+
+ /* Switch Global (1) Registers.
+ */
+ MV88E6XXX_CAP_G1_ATU_FID, /* (0x01) ATU FID Register */
+ MV88E6XXX_CAP_G1_VTU_FID, /* (0x02) VTU FID Register */
+
+ /* Switch Global 2 Registers.
+ * The device contains a second set of global 16-bit registers.
+ */
+ MV88E6XXX_CAP_GLOBAL2,
+ MV88E6XXX_CAP_G2_INT, /* (0x00) Interrupt Status */
+ MV88E6XXX_CAP_G2_MGMT_EN_2X, /* (0x02) MGMT Enable Register 2x */
+ MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */
+ MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */
+
+ /* Per VLAN Spanning Tree Unit (STU).
+ * The Port State database, if present, is accessed through VTU
+ * operations and dedicated SID registers. See MV88E6352_G1_VTU_SID.
+ */
+ MV88E6XXX_CAP_STU,
+
+ /* VLAN Table Unit.
+ * The VTU is used to program 802.1Q VLANs. See MV88E6XXX_G1_VTU_OP.
+ */
+ MV88E6XXX_CAP_VTU,
+};
+
+/* Bitmask of capabilities */
+#define MV88E6XXX_FLAG_EEE BIT_ULL(MV88E6XXX_CAP_EEE)
+
+#define MV88E6XXX_FLAG_SMI_CMD BIT_ULL(MV88E6XXX_CAP_SMI_CMD)
+#define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA)
+
+#define MV88E6XXX_FLAG_G1_VTU_FID BIT_ULL(MV88E6XXX_CAP_G1_VTU_FID)
+
+#define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2)
+#define MV88E6XXX_FLAG_G2_INT BIT_ULL(MV88E6XXX_CAP_G2_INT)
+#define MV88E6XXX_FLAG_G2_MGMT_EN_2X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_2X)
+#define MV88E6XXX_FLAG_G2_MGMT_EN_0X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_0X)
+#define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT)
+
+/* Multi-chip Addressing Mode */
+#define MV88E6XXX_FLAGS_MULTI_CHIP \
+ (MV88E6XXX_FLAG_SMI_CMD | \
+ MV88E6XXX_FLAG_SMI_DATA)
+
+#define MV88E6XXX_FLAGS_FAMILY_6095 \
+ (MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+#define MV88E6XXX_FLAGS_FAMILY_6097 \
+ (MV88E6XXX_FLAG_G1_VTU_FID | \
+ MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_INT | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
+ MV88E6XXX_FLAG_G2_POT | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+#define MV88E6XXX_FLAGS_FAMILY_6165 \
+ (MV88E6XXX_FLAG_G1_VTU_FID | \
+ MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_INT | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
+ MV88E6XXX_FLAG_G2_POT | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+#define MV88E6XXX_FLAGS_FAMILY_6185 \
+ (MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_INT | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+#define MV88E6XXX_FLAGS_FAMILY_6320 \
+ (MV88E6XXX_FLAG_EEE | \
+ MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
+ MV88E6XXX_FLAG_G2_POT | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+#define MV88E6XXX_FLAGS_FAMILY_6341 \
+ (MV88E6XXX_FLAG_EEE | \
+ MV88E6XXX_FLAG_G1_VTU_FID | \
+ MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_INT | \
+ MV88E6XXX_FLAG_G2_POT | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+#define MV88E6XXX_FLAGS_FAMILY_6351 \
+ (MV88E6XXX_FLAG_G1_VTU_FID | \
+ MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_INT | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
+ MV88E6XXX_FLAG_G2_POT | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+#define MV88E6XXX_FLAGS_FAMILY_6352 \
+ (MV88E6XXX_FLAG_EEE | \
+ MV88E6XXX_FLAG_G1_VTU_FID | \
+ MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_INT | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
+ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
+ MV88E6XXX_FLAG_G2_POT | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+#define MV88E6XXX_FLAGS_FAMILY_6390 \
+ (MV88E6XXX_FLAG_EEE | \
+ MV88E6XXX_FLAG_GLOBAL2 | \
+ MV88E6XXX_FLAG_G2_INT | \
+ MV88E6XXX_FLAGS_MULTI_CHIP)
+
+struct mv88e6xxx_ops;
+
+struct mv88e6xxx_info {
+ enum mv88e6xxx_family family;
+ u16 prod_num;
+ const char *name;
+ unsigned int num_databases;
+ unsigned int num_ports;
+ unsigned int max_vid;
+ unsigned int port_base_addr;
+ unsigned int global1_addr;
+ unsigned int age_time_coeff;
+ unsigned int g1_irqs;
+ bool pvt;
+ enum dsa_tag_protocol tag_protocol;
+ unsigned long long flags;
+
+ /* Mask for FromPort and ToPort value of PortVec used in ATU Move
+ * operation. 0 means that the ATU Move operation is not supported.
+ */
+ u8 atu_move_port_mask;
+ const struct mv88e6xxx_ops *ops;
+};
+
+struct mv88e6xxx_atu_entry {
+ u8 state;
+ bool trunk;
+ u16 portvec;
+ u8 mac[ETH_ALEN];
+};
+
+struct mv88e6xxx_vtu_entry {
+ u16 vid;
+ u16 fid;
+ u8 sid;
+ bool valid;
+ u8 member[DSA_MAX_PORTS];
+ u8 state[DSA_MAX_PORTS];
+};
+
+struct mv88e6xxx_bus_ops;
+struct mv88e6xxx_irq_ops;
+
+struct mv88e6xxx_irq {
+ u16 masked;
+ struct irq_chip chip;
+ struct irq_domain *domain;
+ unsigned int nirqs;
+};
+
+struct mv88e6xxx_chip {
+ const struct mv88e6xxx_info *info;
+
+ /* The dsa_switch this private structure is related to */
+ struct dsa_switch *ds;
+
+ /* The device this structure is associated to */
+ struct device *dev;
+
+ /* This mutex protects the access to the switch registers */
+ struct mutex reg_lock;
+
+ /* The MII bus and the address on the bus that is used to
+ * communication with the switch
+ */
+ const struct mv88e6xxx_bus_ops *smi_ops;
+ struct mii_bus *bus;
+ int sw_addr;
+
+ /* Handles automatic disabling and re-enabling of the PHY
+ * polling unit.
+ */
+ const struct mv88e6xxx_bus_ops *phy_ops;
+ struct mutex ppu_mutex;
+ int ppu_disabled;
+ struct work_struct ppu_work;
+ struct timer_list ppu_timer;
+
+ /* This mutex serialises access to the statistics unit.
+ * Hold this mutex over snapshot + dump sequences.
+ */
+ struct mutex stats_mutex;
+
+ /* A switch may have a GPIO line tied to its reset pin. Parse
+ * this from the device tree, and use it before performing
+ * switch soft reset.
+ */
+ struct gpio_desc *reset;
+
+ /* set to size of eeprom if supported by the switch */
+ int eeprom_len;
+
+ /* List of mdio busses */
+ struct list_head mdios;
+
+ /* There can be two interrupt controllers, which are chained
+ * off a GPIO as interrupt source
+ */
+ struct mv88e6xxx_irq g1_irq;
+ struct mv88e6xxx_irq g2_irq;
+ int irq;
+ int device_irq;
+ int watchdog_irq;
+};
+
+struct mv88e6xxx_bus_ops {
+ int (*read)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val);
+ int (*write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val);
+};
+
+struct mv88e6xxx_mdio_bus {
+ struct mii_bus *bus;
+ struct mv88e6xxx_chip *chip;
+ struct list_head list;
+ bool external;
+};
+
+struct mv88e6xxx_ops {
+ /* Ingress Rate Limit unit (IRL) operations */
+ int (*irl_init_all)(struct mv88e6xxx_chip *chip, int port);
+
+ int (*get_eeprom)(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom, u8 *data);
+ int (*set_eeprom)(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom, u8 *data);
+
+ int (*set_switch_mac)(struct mv88e6xxx_chip *chip, u8 *addr);
+
+ int (*phy_read)(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus,
+ int addr, int reg, u16 *val);
+ int (*phy_write)(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus,
+ int addr, int reg, u16 val);
+
+ /* PHY Polling Unit (PPU) operations */
+ int (*ppu_enable)(struct mv88e6xxx_chip *chip);
+ int (*ppu_disable)(struct mv88e6xxx_chip *chip);
+
+ /* Switch Software Reset */
+ int (*reset)(struct mv88e6xxx_chip *chip);
+
+ /* RGMII Receive/Transmit Timing Control
+ * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise.
+ */
+ int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode);
+
+#define LINK_FORCED_DOWN 0
+#define LINK_FORCED_UP 1
+#define LINK_UNFORCED -2
+
+ /* Port's MAC link state
+ * Use LINK_FORCED_UP or LINK_FORCED_DOWN to force link up or down,
+ * or LINK_UNFORCED for normal link detection.
+ */
+ int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
+
+#define DUPLEX_UNFORCED -2
+
+ /* Port's MAC duplex mode
+ *
+ * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex,
+ * or DUPLEX_UNFORCED for normal duplex detection.
+ */
+ int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);
+
+#define SPEED_MAX INT_MAX
+#define SPEED_UNFORCED -2
+
+ /* Port's MAC speed (in Mbps)
+ *
+ * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid.
+ * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value.
+ */
+ int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed);
+
+ int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port);
+
+ int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port,
+ enum mv88e6xxx_frame_mode mode);
+ int (*port_set_egress_floods)(struct mv88e6xxx_chip *chip, int port,
+ bool unicast, bool multicast);
+ int (*port_set_ether_type)(struct mv88e6xxx_chip *chip, int port,
+ u16 etype);
+ int (*port_set_jumbo_size)(struct mv88e6xxx_chip *chip, int port,
+ size_t size);
+
+ int (*port_egress_rate_limiting)(struct mv88e6xxx_chip *chip, int port);
+ int (*port_pause_limit)(struct mv88e6xxx_chip *chip, int port, u8 in,
+ u8 out);
+ int (*port_disable_learn_limit)(struct mv88e6xxx_chip *chip, int port);
+ int (*port_disable_pri_override)(struct mv88e6xxx_chip *chip, int port);
+
+ /* CMODE control what PHY mode the MAC will use, eg. SGMII, RGMII, etc.
+ * Some chips allow this to be configured on specific ports.
+ */
+ int (*port_set_cmode)(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode);
+
+ /* Some devices have a per port register indicating what is
+ * the upstream port this port should forward to.
+ */
+ int (*port_set_upstream_port)(struct mv88e6xxx_chip *chip, int port,
+ int upstream_port);
+
+ /* Snapshot the statistics for a port. The statistics can then
+ * be read back a leisure but still with a consistent view.
+ */
+ int (*stats_snapshot)(struct mv88e6xxx_chip *chip, int port);
+
+ /* Set the histogram mode for statistics, when the control registers
+ * are separated out of the STATS_OP register.
+ */
+ int (*stats_set_histogram)(struct mv88e6xxx_chip *chip);
+
+ /* Return the number of strings describing statistics */
+ int (*stats_get_sset_count)(struct mv88e6xxx_chip *chip);
+ void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data);
+ void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data);
+ int (*set_cpu_port)(struct mv88e6xxx_chip *chip, int port);
+ int (*set_egress_port)(struct mv88e6xxx_chip *chip, int port);
+ const struct mv88e6xxx_irq_ops *watchdog_ops;
+
+ /* Can be either in g1 or g2, so don't use a prefix */
+ int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip);
+
+ /* Power on/off a SERDES interface */
+ int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on);
+
+ /* VLAN Translation Unit operations */
+ int (*vtu_getnext)(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry);
+ int (*vtu_loadpurge)(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry);
+};
+
+struct mv88e6xxx_irq_ops {
+ /* Action to be performed when the interrupt happens */
+ int (*irq_action)(struct mv88e6xxx_chip *chip, int irq);
+ /* Setup the hardware to generate the interrupt */
+ int (*irq_setup)(struct mv88e6xxx_chip *chip);
+ /* Reset the hardware to stop generating the interrupt */
+ void (*irq_free)(struct mv88e6xxx_chip *chip);
+};
+
+#define STATS_TYPE_PORT BIT(0)
+#define STATS_TYPE_BANK0 BIT(1)
+#define STATS_TYPE_BANK1 BIT(2)
+
+struct mv88e6xxx_hw_stat {
+ char string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int reg;
+ int type;
+};
+
+static inline bool mv88e6xxx_has(struct mv88e6xxx_chip *chip,
+ unsigned long flags)
+{
+ return (chip->info->flags & flags) == flags;
+}
+
+static inline bool mv88e6xxx_has_pvt(struct mv88e6xxx_chip *chip)
+{
+ return chip->info->pvt;
+}
+
+static inline unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip)
+{
+ return chip->info->num_databases;
+}
+
+static inline unsigned int mv88e6xxx_num_ports(struct mv88e6xxx_chip *chip)
+{
+ return chip->info->num_ports;
+}
+
+static inline u16 mv88e6xxx_port_mask(struct mv88e6xxx_chip *chip)
+{
+ return GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
+}
+
+int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val);
+int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val);
+int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
+ u16 update);
+int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask);
+struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip);
+
+#endif /* _MV88E6XXX_CHIP_H */
diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c
index 75af86a7fad8..d76d7c7ea819 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.c
+++ b/drivers/net/dsa/mv88e6xxx/global1.c
@@ -3,7 +3,8 @@
*
* Copyright (c) 2008 Marvell Semiconductor
*
- * Copyright (c) 2016 Vivien Didelot <[email protected]>
+ * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
+ * Vivien Didelot <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -11,7 +12,9 @@
* (at your option) any later version.
*/
-#include "mv88e6xxx.h"
+#include <linux/bitfield.h>
+
+#include "chip.h"
#include "global1.h"
int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
@@ -41,13 +44,13 @@ static int mv88e6185_g1_wait_ppu_disabled(struct mv88e6xxx_chip *chip)
int i, err;
for (i = 0; i < 16; i++) {
- err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &state);
if (err)
return err;
/* Check the value of the PPUState bits 15:14 */
- state &= GLOBAL_STATUS_PPU_STATE_MASK;
- if (state != GLOBAL_STATUS_PPU_STATE_POLLING)
+ state &= MV88E6185_G1_STS_PPU_STATE_MASK;
+ if (state != MV88E6185_G1_STS_PPU_STATE_POLLING)
return 0;
usleep_range(1000, 2000);
@@ -62,13 +65,13 @@ static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
int i, err;
for (i = 0; i < 16; ++i) {
- err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &state);
if (err)
return err;
/* Check the value of the PPUState bits 15:14 */
- state &= GLOBAL_STATUS_PPU_STATE_MASK;
- if (state == GLOBAL_STATUS_PPU_STATE_POLLING)
+ state &= MV88E6185_G1_STS_PPU_STATE_MASK;
+ if (state == MV88E6185_G1_STS_PPU_STATE_POLLING)
return 0;
usleep_range(1000, 2000);
@@ -83,12 +86,12 @@ static int mv88e6352_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
int i, err;
for (i = 0; i < 16; ++i) {
- err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &state);
if (err)
return err;
/* Check the value of the PPUState (or InitState) bit 15 */
- if (state & GLOBAL_STATUS_PPU_STATE)
+ if (state & MV88E6352_G1_STS_PPU_STATE)
return 0;
usleep_range(1000, 2000);
@@ -108,11 +111,11 @@ static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip)
* have finished their initialization and are ready to accept frames.
*/
while (time_before(jiffies, timeout)) {
- err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &val);
if (err)
return err;
- if (val & GLOBAL_STATUS_INIT_READY)
+ if (val & MV88E6XXX_G1_STS_INIT_READY)
break;
usleep_range(1000, 2000);
@@ -124,6 +127,33 @@ static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip)
return 0;
}
+/* Offset 0x01: Switch MAC Address Register Bytes 0 & 1
+ * Offset 0x02: Switch MAC Address Register Bytes 2 & 3
+ * Offset 0x03: Switch MAC Address Register Bytes 4 & 5
+ */
+int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
+{
+ u16 reg;
+ int err;
+
+ reg = (addr[0] << 8) | addr[1];
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_MAC_01, reg);
+ if (err)
+ return err;
+
+ reg = (addr[2] << 8) | addr[3];
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_MAC_23, reg);
+ if (err)
+ return err;
+
+ reg = (addr[4] << 8) | addr[5];
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_MAC_45, reg);
+ if (err)
+ return err;
+
+ return 0;
+}
+
/* Offset 0x04: Switch Global Control Register */
int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip)
@@ -134,14 +164,14 @@ int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip)
/* Set the SWReset bit 15 along with the PPUEn bit 14, to also restart
* the PPU, including re-doing PHY detection and initialization
*/
- err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &val);
if (err)
return err;
- val |= GLOBAL_CONTROL_SW_RESET;
- val |= GLOBAL_CONTROL_PPU_ENABLE;
+ val |= MV88E6XXX_G1_CTL1_SW_RESET;
+ val |= MV88E6XXX_G1_CTL1_PPU_ENABLE;
- err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, val);
if (err)
return err;
@@ -158,13 +188,13 @@ int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip)
int err;
/* Set the SWReset bit 15 */
- err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &val);
if (err)
return err;
- val |= GLOBAL_CONTROL_SW_RESET;
+ val |= MV88E6XXX_G1_CTL1_SW_RESET;
- err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, val);
if (err)
return err;
@@ -180,13 +210,13 @@ int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip)
u16 val;
int err;
- err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &val);
if (err)
return err;
- val |= GLOBAL_CONTROL_PPU_ENABLE;
+ val |= MV88E6XXX_G1_CTL1_PPU_ENABLE;
- err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, val);
if (err)
return err;
@@ -198,13 +228,13 @@ int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip)
u16 val;
int err;
- err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &val);
if (err)
return err;
- val &= ~GLOBAL_CONTROL_PPU_ENABLE;
+ val &= ~MV88E6XXX_G1_CTL1_PPU_ENABLE;
- err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, val);
if (err)
return err;
@@ -219,17 +249,17 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
u16 reg;
int err;
- err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, &reg);
+ err = mv88e6xxx_g1_read(chip, MV88E6185_G1_MONITOR_CTL, &reg);
if (err)
return err;
- reg &= ~(GLOBAL_MONITOR_CONTROL_INGRESS_MASK |
- GLOBAL_MONITOR_CONTROL_EGRESS_MASK);
+ reg &= ~(MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK |
+ MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK);
- reg |= port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
- port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT;
+ reg |= port << __bf_shf(MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK) |
+ port << __bf_shf(MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK);
- return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
+ return mv88e6xxx_g1_write(chip, MV88E6185_G1_MONITOR_CTL, reg);
}
/* Older generations also call this the ARP destination. It has been
@@ -241,14 +271,14 @@ int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
u16 reg;
int err;
- err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, &reg);
+ err = mv88e6xxx_g1_read(chip, MV88E6185_G1_MONITOR_CTL, &reg);
if (err)
return err;
- reg &= ~GLOBAL_MONITOR_CONTROL_ARP_MASK;
- reg |= port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
+ reg &= ~MV88E6185_G1_MONITOR_CTL_ARP_DEST_MASK;
+ reg |= port << __bf_shf(MV88E6185_G1_MONITOR_CTL_ARP_DEST_MASK);
- return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
+ return mv88e6xxx_g1_write(chip, MV88E6185_G1_MONITOR_CTL, reg);
}
static int mv88e6390_g1_monitor_write(struct mv88e6xxx_chip *chip,
@@ -256,55 +286,66 @@ static int mv88e6390_g1_monitor_write(struct mv88e6xxx_chip *chip,
{
u16 reg;
- reg = GLOBAL_MONITOR_CONTROL_UPDATE | pointer | data;
+ reg = MV88E6390_G1_MONITOR_MGMT_CTL_UPDATE | pointer | data;
- return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
+ return mv88e6xxx_g1_write(chip, MV88E6390_G1_MONITOR_MGMT_CTL, reg);
}
int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
{
+ u16 ptr;
int err;
- err = mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_INGRESS,
- port);
+ ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST;
+ err = mv88e6390_g1_monitor_write(chip, ptr, port);
+ if (err)
+ return err;
+
+ ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST;
+ err = mv88e6390_g1_monitor_write(chip, ptr, port);
if (err)
return err;
- return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_EGRESS,
- port);
+ return 0;
}
int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
{
- return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_CPU_DEST,
- port);
+ u16 ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST;
+
+ return mv88e6390_g1_monitor_write(chip, ptr, port);
}
int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
{
+ u16 ptr;
int err;
/* 01:c2:80:00:00:00:00-01:c2:80:00:00:00:07 are Management */
- err = mv88e6390_g1_monitor_write(
- chip, GLOBAL_MONITOR_CONTROL_0180C280000000XLO, 0xff);
+ ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XLO;
+ err = mv88e6390_g1_monitor_write(chip, ptr, 0xff);
if (err)
return err;
/* 01:c2:80:00:00:00:08-01:c2:80:00:00:00:0f are Management */
- err = mv88e6390_g1_monitor_write(
- chip, GLOBAL_MONITOR_CONTROL_0180C280000000XHI, 0xff);
+ ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XHI;
+ err = mv88e6390_g1_monitor_write(chip, ptr, 0xff);
if (err)
return err;
/* 01:c2:80:00:00:00:20-01:c2:80:00:00:00:27 are Management */
- err = mv88e6390_g1_monitor_write(
- chip, GLOBAL_MONITOR_CONTROL_0180C280000002XLO, 0xff);
+ ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XLO;
+ err = mv88e6390_g1_monitor_write(chip, ptr, 0xff);
if (err)
return err;
/* 01:c2:80:00:00:00:28-01:c2:80:00:00:00:2f are Management */
- return mv88e6390_g1_monitor_write(
- chip, GLOBAL_MONITOR_CONTROL_0180C280000002XHI, 0xff);
+ ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XHI;
+ err = mv88e6390_g1_monitor_write(chip, ptr, 0xff);
+ if (err)
+ return err;
+
+ return 0;
}
/* Offset 0x1c: Global Control 2 */
@@ -314,13 +355,13 @@ int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip)
u16 val;
int err;
- err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL_2, &val);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL2, &val);
if (err)
return err;
- val |= GLOBAL_CONTROL_2_HIST_RX_TX;
+ val |= MV88E6XXX_G1_CTL2_HIST_RX_TX;
- err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, val);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL2, val);
return err;
}
@@ -329,7 +370,8 @@ int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip)
int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip)
{
- return mv88e6xxx_g1_wait(chip, GLOBAL_STATS_OP, GLOBAL_STATS_OP_BUSY);
+ return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_STATS_OP,
+ MV88E6XXX_G1_STATS_OP_BUSY);
}
int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
@@ -337,9 +379,10 @@ int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
int err;
/* Snapshot the hardware statistics counters for this port. */
- err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
- GLOBAL_STATS_OP_CAPTURE_PORT |
- GLOBAL_STATS_OP_HIST_RX_TX | port);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP,
+ MV88E6XXX_G1_STATS_OP_BUSY |
+ MV88E6XXX_G1_STATS_OP_CAPTURE_PORT |
+ MV88E6XXX_G1_STATS_OP_HIST_RX_TX | port);
if (err)
return err;
@@ -361,8 +404,9 @@ int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
port = (port + 1) << 5;
/* Snapshot the hardware statistics counters for this port. */
- err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
- GLOBAL_STATS_OP_CAPTURE_PORT | port);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP,
+ MV88E6XXX_G1_STATS_OP_BUSY |
+ MV88E6XXX_G1_STATS_OP_CAPTURE_PORT | port);
if (err)
return err;
@@ -378,8 +422,9 @@ void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val)
*val = 0;
- err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
- GLOBAL_STATS_OP_READ_CAPTURED | stat);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP,
+ MV88E6XXX_G1_STATS_OP_BUSY |
+ MV88E6XXX_G1_STATS_OP_READ_CAPTURED | stat);
if (err)
return;
@@ -387,13 +432,13 @@ void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val)
if (err)
return;
- err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, &reg);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STATS_COUNTER_32, &reg);
if (err)
return;
value = reg << 16;
- err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, &reg);
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STATS_COUNTER_01, &reg);
if (err)
return;
diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
index 1aec7382c02d..950b914f9251 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.h
+++ b/drivers/net/dsa/mv88e6xxx/global1.h
@@ -3,7 +3,8 @@
*
* Copyright (c) 2008 Marvell Semiconductor
*
- * Copyright (c) 2016 Vivien Didelot <[email protected]>
+ * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
+ * Vivien Didelot <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,12 +15,216 @@
#ifndef _MV88E6XXX_GLOBAL1_H
#define _MV88E6XXX_GLOBAL1_H
-#include "mv88e6xxx.h"
+#include "chip.h"
+
+/* Offset 0x00: Switch Global Status Register */
+#define MV88E6XXX_G1_STS 0x00
+#define MV88E6352_G1_STS_PPU_STATE 0x8000
+#define MV88E6185_G1_STS_PPU_STATE_MASK 0xc000
+#define MV88E6185_G1_STS_PPU_STATE_DISABLED_RST 0x0000
+#define MV88E6185_G1_STS_PPU_STATE_INITIALIZING 0x4000
+#define MV88E6185_G1_STS_PPU_STATE_DISABLED 0x8000
+#define MV88E6185_G1_STS_PPU_STATE_POLLING 0xc000
+#define MV88E6XXX_G1_STS_INIT_READY 0x0800
+#define MV88E6XXX_G1_STS_IRQ_AVB 8
+#define MV88E6XXX_G1_STS_IRQ_DEVICE 7
+#define MV88E6XXX_G1_STS_IRQ_STATS 6
+#define MV88E6XXX_G1_STS_IRQ_VTU_PROBLEM 5
+#define MV88E6XXX_G1_STS_IRQ_VTU_DONE 4
+#define MV88E6XXX_G1_STS_IRQ_ATU_PROBLEM 3
+#define MV88E6XXX_G1_STS_IRQ_ATU_DONE 2
+#define MV88E6XXX_G1_STS_IRQ_TCAM_DONE 1
+#define MV88E6XXX_G1_STS_IRQ_EEPROM_DONE 0
+
+/* Offset 0x01: Switch MAC Address Register Bytes 0 & 1
+ * Offset 0x02: Switch MAC Address Register Bytes 2 & 3
+ * Offset 0x03: Switch MAC Address Register Bytes 4 & 5
+ */
+#define MV88E6XXX_G1_MAC_01 0x01
+#define MV88E6XXX_G1_MAC_23 0x02
+#define MV88E6XXX_G1_MAC_45 0x03
+
+/* Offset 0x01: ATU FID Register */
+#define MV88E6352_G1_ATU_FID 0x01
+
+/* Offset 0x02: VTU FID Register */
+#define MV88E6352_G1_VTU_FID 0x02
+#define MV88E6352_G1_VTU_FID_MASK 0x0fff
+
+/* Offset 0x03: VTU SID Register */
+#define MV88E6352_G1_VTU_SID 0x03
+#define MV88E6352_G1_VTU_SID_MASK 0x3f
+
+/* Offset 0x04: Switch Global Control Register */
+#define MV88E6XXX_G1_CTL1 0x04
+#define MV88E6XXX_G1_CTL1_SW_RESET 0x8000
+#define MV88E6XXX_G1_CTL1_PPU_ENABLE 0x4000
+#define MV88E6352_G1_CTL1_DISCARD_EXCESS 0x2000
+#define MV88E6185_G1_CTL1_SCHED_PRIO 0x0800
+#define MV88E6185_G1_CTL1_MAX_FRAME_1632 0x0400
+#define MV88E6185_G1_CTL1_RELOAD_EEPROM 0x0200
+#define MV88E6XXX_G1_CTL1_DEVICE_EN 0x0080
+#define MV88E6XXX_G1_CTL1_STATS_DONE_EN 0x0040
+#define MV88E6XXX_G1_CTL1_VTU_PROBLEM_EN 0x0020
+#define MV88E6XXX_G1_CTL1_VTU_DONE_EN 0x0010
+#define MV88E6XXX_G1_CTL1_ATU_PROBLEM_EN 0x0008
+#define MV88E6XXX_G1_CTL1_ATU_DONE_EN 0x0004
+#define MV88E6XXX_G1_CTL1_TCAM_EN 0x0002
+#define MV88E6XXX_G1_CTL1_EEPROM_DONE_EN 0x0001
+
+/* Offset 0x05: VTU Operation Register */
+#define MV88E6XXX_G1_VTU_OP 0x05
+#define MV88E6XXX_G1_VTU_OP_BUSY 0x8000
+#define MV88E6XXX_G1_VTU_OP_MASK 0x7000
+#define MV88E6XXX_G1_VTU_OP_FLUSH_ALL 0x1000
+#define MV88E6XXX_G1_VTU_OP_NOOP 0x2000
+#define MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE 0x3000
+#define MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT 0x4000
+#define MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE 0x5000
+#define MV88E6XXX_G1_VTU_OP_STU_GET_NEXT 0x6000
+
+/* Offset 0x06: VTU VID Register */
+#define MV88E6XXX_G1_VTU_VID 0x06
+#define MV88E6XXX_G1_VTU_VID_MASK 0x0fff
+#define MV88E6390_G1_VTU_VID_PAGE 0x2000
+#define MV88E6XXX_G1_VTU_VID_VALID 0x1000
+
+/* Offset 0x07: VTU/STU Data Register 1
+ * Offset 0x08: VTU/STU Data Register 2
+ * Offset 0x09: VTU/STU Data Register 3
+ */
+#define MV88E6XXX_G1_VTU_DATA1 0x07
+#define MV88E6XXX_G1_VTU_DATA2 0x08
+#define MV88E6XXX_G1_VTU_DATA3 0x09
+#define MV88E6XXX_G1_VTU_STU_DATA_MASK 0x0003
+#define MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED 0x0000
+#define MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED 0x0001
+#define MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED 0x0002
+#define MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER 0x0003
+#define MV88E6XXX_G1_STU_DATA_PORT_STATE_DISABLED 0x0000
+#define MV88E6XXX_G1_STU_DATA_PORT_STATE_BLOCKING 0x0001
+#define MV88E6XXX_G1_STU_DATA_PORT_STATE_LEARNING 0x0002
+#define MV88E6XXX_G1_STU_DATA_PORT_STATE_FORWARDING 0x0003
+
+/* Offset 0x0A: ATU Control Register */
+#define MV88E6XXX_G1_ATU_CTL 0x0a
+#define MV88E6XXX_G1_ATU_CTL_LEARN2ALL 0x0008
+
+/* Offset 0x0B: ATU Operation Register */
+#define MV88E6XXX_G1_ATU_OP 0x0b
+#define MV88E6XXX_G1_ATU_OP_BUSY 0x8000
+#define MV88E6XXX_G1_ATU_OP_MASK 0x7000
+#define MV88E6XXX_G1_ATU_OP_NOOP 0x0000
+#define MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL 0x1000
+#define MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC 0x2000
+#define MV88E6XXX_G1_ATU_OP_LOAD_DB 0x3000
+#define MV88E6XXX_G1_ATU_OP_GET_NEXT_DB 0x4000
+#define MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL_DB 0x5000
+#define MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB 0x6000
+#define MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION 0x7000
+
+/* Offset 0x0C: ATU Data Register */
+#define MV88E6XXX_G1_ATU_DATA 0x0c
+#define MV88E6XXX_G1_ATU_DATA_TRUNK 0x8000
+#define MV88E6XXX_G1_ATU_DATA_TRUNK_ID_MASK 0x00f0
+#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_MASK 0x3ff0
+#define MV88E6XXX_G1_ATU_DATA_STATE_MASK 0x000f
+#define MV88E6XXX_G1_ATU_DATA_STATE_UNUSED 0x0000
+#define MV88E6XXX_G1_ATU_DATA_STATE_UC_MGMT 0x000d
+#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC 0x000e
+#define MV88E6XXX_G1_ATU_DATA_STATE_UC_PRIO_OVER 0x000f
+#define MV88E6XXX_G1_ATU_DATA_STATE_MC_NONE_RATE 0x0005
+#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC 0x0007
+#define MV88E6XXX_G1_ATU_DATA_STATE_MC_MGMT 0x000e
+#define MV88E6XXX_G1_ATU_DATA_STATE_MC_PRIO_OVER 0x000f
+
+/* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
+ * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
+ * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5
+ */
+#define MV88E6XXX_G1_ATU_MAC01 0x0d
+#define MV88E6XXX_G1_ATU_MAC23 0x0e
+#define MV88E6XXX_G1_ATU_MAC45 0x0f
+
+/* Offset 0x10: IP-PRI Mapping Register 0
+ * Offset 0x11: IP-PRI Mapping Register 1
+ * Offset 0x12: IP-PRI Mapping Register 2
+ * Offset 0x13: IP-PRI Mapping Register 3
+ * Offset 0x14: IP-PRI Mapping Register 4
+ * Offset 0x15: IP-PRI Mapping Register 5
+ * Offset 0x16: IP-PRI Mapping Register 6
+ * Offset 0x17: IP-PRI Mapping Register 7
+ */
+#define MV88E6XXX_G1_IP_PRI_0 0x10
+#define MV88E6XXX_G1_IP_PRI_1 0x11
+#define MV88E6XXX_G1_IP_PRI_2 0x12
+#define MV88E6XXX_G1_IP_PRI_3 0x13
+#define MV88E6XXX_G1_IP_PRI_4 0x14
+#define MV88E6XXX_G1_IP_PRI_5 0x15
+#define MV88E6XXX_G1_IP_PRI_6 0x16
+#define MV88E6XXX_G1_IP_PRI_7 0x17
+
+/* Offset 0x18: IEEE-PRI Register */
+#define MV88E6XXX_G1_IEEE_PRI 0x18
+
+/* Offset 0x19: Core Tag Type */
+#define MV88E6185_G1_CORE_TAG_TYPE 0x19
+
+/* Offset 0x1A: Monitor Control */
+#define MV88E6185_G1_MONITOR_CTL 0x1a
+#define MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK 0xf000
+#define MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK 0x0f00
+#define MV88E6185_G1_MONITOR_CTL_ARP_DEST_MASK 0x00f0
+#define MV88E6352_G1_MONITOR_CTL_CPU_DEST_MASK 0x00f0
+#define MV88E6352_G1_MONITOR_CTL_MIRROR_DEST_MASK 0x000f
+
+/* Offset 0x1A: Monitor & MGMT Control Register */
+#define MV88E6390_G1_MONITOR_MGMT_CTL 0x1a
+#define MV88E6390_G1_MONITOR_MGMT_CTL_UPDATE 0x8000
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_MASK 0x3f00
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XLO 0x0000
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XHI 0x0100
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XLO 0x0200
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XHI 0x0300
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST 0x2000
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST 0x2100
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST 0x3000
+#define MV88E6390_G1_MONITOR_MGMT_CTL_DATA_MASK 0x00ff
+
+/* Offset 0x1C: Global Control 2 */
+#define MV88E6XXX_G1_CTL2 0x1c
+#define MV88E6XXX_G1_CTL2_NO_CASCADE 0xe000
+#define MV88E6XXX_G1_CTL2_MULTIPLE_CASCADE 0xf000
+#define MV88E6XXX_G1_CTL2_HIST_RX 0x0040
+#define MV88E6XXX_G1_CTL2_HIST_TX 0x0080
+#define MV88E6XXX_G1_CTL2_HIST_RX_TX 0x00c0
+
+/* Offset 0x1D: Stats Operation Register */
+#define MV88E6XXX_G1_STATS_OP 0x1d
+#define MV88E6XXX_G1_STATS_OP_BUSY 0x8000
+#define MV88E6XXX_G1_STATS_OP_NOP 0x0000
+#define MV88E6XXX_G1_STATS_OP_FLUSH_ALL 0x1000
+#define MV88E6XXX_G1_STATS_OP_FLUSH_PORT 0x2000
+#define MV88E6XXX_G1_STATS_OP_READ_CAPTURED 0x4000
+#define MV88E6XXX_G1_STATS_OP_CAPTURE_PORT 0x5000
+#define MV88E6XXX_G1_STATS_OP_HIST_RX 0x0400
+#define MV88E6XXX_G1_STATS_OP_HIST_TX 0x0800
+#define MV88E6XXX_G1_STATS_OP_HIST_RX_TX 0x0c00
+#define MV88E6XXX_G1_STATS_OP_BANK_1_BIT_9 0x0200
+#define MV88E6XXX_G1_STATS_OP_BANK_1_BIT_10 0x0400
+
+/* Offset 0x1E: Stats Counter Register Bytes 3 & 2
+ * Offset 0x1F: Stats Counter Register Bytes 1 & 0
+ */
+#define MV88E6XXX_G1_STATS_COUNTER_32 0x1e
+#define MV88E6XXX_G1_STATS_COUNTER_01 0x1f
int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val);
int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val);
int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask);
+int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr);
+
int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip);
int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip);
@@ -38,4 +243,29 @@ int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
+int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all);
+int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip,
+ unsigned int msecs);
+int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
+ struct mv88e6xxx_atu_entry *entry);
+int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
+ struct mv88e6xxx_atu_entry *entry);
+int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all);
+int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,
+ bool all);
+
+int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry);
+int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry);
+int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry);
+int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry);
+int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry);
+int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry);
+int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip);
+
#endif /* _MV88E6XXX_GLOBAL1_H */
diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c
new file mode 100644
index 000000000000..efeef4b01442
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c
@@ -0,0 +1,309 @@
+/*
+ * Marvell 88E6xxx Address Translation Unit (ATU) support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ * Copyright (c) 2017 Savoir-faire Linux, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "chip.h"
+#include "global1.h"
+
+/* Offset 0x01: ATU FID Register */
+
+static int mv88e6xxx_g1_atu_fid_write(struct mv88e6xxx_chip *chip, u16 fid)
+{
+ return mv88e6xxx_g1_write(chip, MV88E6352_G1_ATU_FID, fid & 0xfff);
+}
+
+/* Offset 0x0A: ATU Control Register */
+
+int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val);
+ if (err)
+ return err;
+
+ if (learn2all)
+ val |= MV88E6XXX_G1_ATU_CTL_LEARN2ALL;
+ else
+ val &= ~MV88E6XXX_G1_ATU_CTL_LEARN2ALL;
+
+ return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, val);
+}
+
+int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip,
+ unsigned int msecs)
+{
+ const unsigned int coeff = chip->info->age_time_coeff;
+ const unsigned int min = 0x01 * coeff;
+ const unsigned int max = 0xff * coeff;
+ u8 age_time;
+ u16 val;
+ int err;
+
+ if (msecs < min || msecs > max)
+ return -ERANGE;
+
+ /* Round to nearest multiple of coeff */
+ age_time = (msecs + coeff / 2) / coeff;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val);
+ if (err)
+ return err;
+
+ /* AgeTime is 11:4 bits */
+ val &= ~0xff0;
+ val |= age_time << 4;
+
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, val);
+ if (err)
+ return err;
+
+ dev_dbg(chip->dev, "AgeTime set to 0x%02x (%d ms)\n", age_time,
+ age_time * coeff);
+
+ return 0;
+}
+
+/* Offset 0x0B: ATU Operation Register */
+
+static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip)
+{
+ return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_ATU_OP,
+ MV88E6XXX_G1_ATU_OP_BUSY);
+}
+
+static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
+{
+ u16 val;
+ int err;
+
+ /* FID bits are dispatched all around gradually as more are supported */
+ if (mv88e6xxx_num_databases(chip) > 256) {
+ err = mv88e6xxx_g1_atu_fid_write(chip, fid);
+ if (err)
+ return err;
+ } else {
+ if (mv88e6xxx_num_databases(chip) > 16) {
+ /* ATU DBNum[7:4] are located in ATU Control 15:12 */
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL,
+ &val);
+ if (err)
+ return err;
+
+ val = (val & 0x0fff) | ((fid << 8) & 0xf000);
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL,
+ val);
+ if (err)
+ return err;
+ }
+
+ /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
+ op |= fid & 0xf;
+ }
+
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP,
+ MV88E6XXX_G1_ATU_OP_BUSY | op);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g1_atu_op_wait(chip);
+}
+
+/* Offset 0x0C: ATU Data Register */
+
+static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_atu_entry *entry)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, &val);
+ if (err)
+ return err;
+
+ entry->state = val & 0xf;
+ if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
+ entry->trunk = !!(val & MV88E6XXX_G1_ATU_DATA_TRUNK);
+ entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip);
+ }
+
+ return 0;
+}
+
+static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_atu_entry *entry)
+{
+ u16 data = entry->state & 0xf;
+
+ if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
+ if (entry->trunk)
+ data |= MV88E6XXX_G1_ATU_DATA_TRUNK;
+
+ data |= (entry->portvec & mv88e6xxx_port_mask(chip)) << 4;
+ }
+
+ return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_DATA, data);
+}
+
+/* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
+ * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
+ * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5
+ */
+
+static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_atu_entry *entry)
+{
+ u16 val;
+ int i, err;
+
+ for (i = 0; i < 3; i++) {
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01 + i, &val);
+ if (err)
+ return err;
+
+ entry->mac[i * 2] = val >> 8;
+ entry->mac[i * 2 + 1] = val & 0xff;
+ }
+
+ return 0;
+}
+
+static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_atu_entry *entry)
+{
+ u16 val;
+ int i, err;
+
+ for (i = 0; i < 3; i++) {
+ val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1];
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_MAC01 + i, val);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Address Translation Unit operations */
+
+int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
+ struct mv88e6xxx_atu_entry *entry)
+{
+ int err;
+
+ err = mv88e6xxx_g1_atu_op_wait(chip);
+ if (err)
+ return err;
+
+ /* Write the MAC address to iterate from only once */
+ if (entry->state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
+ err = mv88e6xxx_g1_atu_mac_write(chip, entry);
+ if (err)
+ return err;
+ }
+
+ err = mv88e6xxx_g1_atu_op(chip, fid, MV88E6XXX_G1_ATU_OP_GET_NEXT_DB);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_atu_data_read(chip, entry);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g1_atu_mac_read(chip, entry);
+}
+
+int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
+ struct mv88e6xxx_atu_entry *entry)
+{
+ int err;
+
+ err = mv88e6xxx_g1_atu_op_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_atu_mac_write(chip, entry);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_atu_data_write(chip, entry);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g1_atu_op(chip, fid, MV88E6XXX_G1_ATU_OP_LOAD_DB);
+}
+
+static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip *chip, u16 fid,
+ struct mv88e6xxx_atu_entry *entry,
+ bool all)
+{
+ u16 op;
+ int err;
+
+ err = mv88e6xxx_g1_atu_op_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_atu_data_write(chip, entry);
+ if (err)
+ return err;
+
+ /* Flush/Move all or non-static entries from all or a given database */
+ if (all && fid)
+ op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL_DB;
+ else if (fid)
+ op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
+ else if (all)
+ op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL;
+ else
+ op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC;
+
+ return mv88e6xxx_g1_atu_op(chip, fid, op);
+}
+
+int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all)
+{
+ struct mv88e6xxx_atu_entry entry = {
+ .state = 0, /* Null EntryState means Flush */
+ };
+
+ return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
+}
+
+static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
+ int from_port, int to_port, bool all)
+{
+ struct mv88e6xxx_atu_entry entry = { 0 };
+ unsigned long mask;
+ int shift;
+
+ if (!chip->info->atu_move_port_mask)
+ return -EOPNOTSUPP;
+
+ mask = chip->info->atu_move_port_mask;
+ shift = bitmap_weight(&mask, 16);
+
+ entry.state = 0xf, /* Full EntryState means Move */
+ entry.portvec = from_port & mask;
+ entry.portvec |= (to_port & mask) << shift;
+
+ return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
+}
+
+int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,
+ bool all)
+{
+ int from_port = port;
+ int to_port = chip->info->atu_move_port_mask;
+
+ return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all);
+}
diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c
new file mode 100644
index 000000000000..8c8a0ec3d6e9
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c
@@ -0,0 +1,515 @@
+/*
+ * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ * Copyright (c) 2015 CMC Electronics, Inc.
+ * Copyright (c) 2017 Savoir-faire Linux, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "chip.h"
+#include "global1.h"
+
+/* Offset 0x02: VTU FID Register */
+
+static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID, &val);
+ if (err)
+ return err;
+
+ entry->fid = val & MV88E6352_G1_VTU_FID_MASK;
+
+ return 0;
+}
+
+static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK;
+
+ return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val);
+}
+
+/* Offset 0x03: VTU SID Register */
+
+static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID, &val);
+ if (err)
+ return err;
+
+ entry->sid = val & MV88E6352_G1_VTU_SID_MASK;
+
+ return 0;
+}
+
+static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 val = entry->sid & MV88E6352_G1_VTU_SID_MASK;
+
+ return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_SID, val);
+}
+
+/* Offset 0x05: VTU Operation Register */
+
+static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)
+{
+ return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_VTU_OP,
+ MV88E6XXX_G1_VTU_OP_BUSY);
+}
+
+static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)
+{
+ int err;
+
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_OP,
+ MV88E6XXX_G1_VTU_OP_BUSY | op);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g1_vtu_op_wait(chip);
+}
+
+/* Offset 0x06: VTU VID Register */
+
+static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID, &val);
+ if (err)
+ return err;
+
+ entry->vid = val & 0xfff;
+
+ if (val & MV88E6390_G1_VTU_VID_PAGE)
+ entry->vid |= 0x1000;
+
+ entry->valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID);
+
+ return 0;
+}
+
+static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 val = entry->vid & 0xfff;
+
+ if (entry->vid & 0x1000)
+ val |= MV88E6390_G1_VTU_VID_PAGE;
+
+ if (entry->valid)
+ val |= MV88E6XXX_G1_VTU_VID_VALID;
+
+ return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_VID, val);
+}
+
+/* Offset 0x07: VTU/STU Data Register 1
+ * Offset 0x08: VTU/STU Data Register 2
+ * Offset 0x09: VTU/STU Data Register 3
+ */
+
+static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 regs[3];
+ int i;
+
+ /* Read all 3 VTU/STU Data registers */
+ for (i = 0; i < 3; ++i) {
+ u16 *reg = &regs[i];
+ int err;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
+ if (err)
+ return err;
+ }
+
+ /* Extract MemberTag and PortState data */
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
+ unsigned int member_offset = (i % 4) * 4;
+ unsigned int state_offset = member_offset + 2;
+
+ entry->member[i] = (regs[i / 4] >> member_offset) & 0x3;
+ entry->state[i] = (regs[i / 4] >> state_offset) & 0x3;
+ }
+
+ return 0;
+}
+
+static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 regs[3] = { 0 };
+ int i;
+
+ /* Insert MemberTag and PortState data */
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
+ unsigned int member_offset = (i % 4) * 4;
+ unsigned int state_offset = member_offset + 2;
+
+ regs[i / 4] |= (entry->member[i] & 0x3) << member_offset;
+ regs[i / 4] |= (entry->state[i] & 0x3) << state_offset;
+ }
+
+ /* Write all 3 VTU/STU Data registers */
+ for (i = 0; i < 3; ++i) {
+ u16 reg = regs[i];
+ int err;
+
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data)
+{
+ u16 regs[2];
+ int i;
+
+ /* Read the 2 VTU/STU Data registers */
+ for (i = 0; i < 2; ++i) {
+ u16 *reg = &regs[i];
+ int err;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
+ if (err)
+ return err;
+ }
+
+ /* Extract data */
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
+ unsigned int offset = (i % 8) * 2;
+
+ data[i] = (regs[i / 8] >> offset) & 0x3;
+ }
+
+ return 0;
+}
+
+static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data)
+{
+ u16 regs[2] = { 0 };
+ int i;
+
+ /* Insert data */
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
+ unsigned int offset = (i % 8) * 2;
+
+ regs[i / 8] |= (data[i] & 0x3) << offset;
+ }
+
+ /* Write the 2 VTU/STU Data registers */
+ for (i = 0; i < 2; ++i) {
+ u16 reg = regs[i];
+ int err;
+
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/* VLAN Translation Unit Operations */
+
+static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ int err;
+
+ err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_sid_read(chip, entry);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g1_vtu_vid_read(chip, entry);
+}
+
+static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *vtu)
+{
+ struct mv88e6xxx_vtu_entry stu;
+ int err;
+
+ err = mv88e6xxx_g1_vtu_sid_read(chip, vtu);
+ if (err)
+ return err;
+
+ stu.sid = vtu->sid - 1;
+
+ err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu);
+ if (err)
+ return err;
+
+ if (stu.sid != vtu->sid || !stu.valid)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ int err;
+
+ err = mv88e6xxx_g1_vtu_op_wait(chip);
+ if (err)
+ return err;
+
+ /* To get the next higher active VID, the VTU GetNext operation can be
+ * started again without setting the VID registers since it already
+ * contains the last VID.
+ *
+ * To save a few hardware accesses and abstract this to the caller,
+ * write the VID only once, when the entry is given as invalid.
+ */
+ if (!entry->valid) {
+ err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
+ if (err)
+ return err;
+ }
+
+ err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g1_vtu_vid_read(chip, entry);
+}
+
+int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_g1_vtu_getnext(chip, entry);
+ if (err)
+ return err;
+
+ if (entry->valid) {
+ err = mv88e6185_g1_vtu_data_read(chip, entry);
+ if (err)
+ return err;
+
+ /* VTU DBNum[3:0] are located in VTU Operation 3:0
+ * VTU DBNum[7:4] are located in VTU Operation 11:8
+ */
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);
+ if (err)
+ return err;
+
+ entry->fid = val & 0x000f;
+ entry->fid |= (val & 0x0f00) >> 4;
+ }
+
+ return 0;
+}
+
+int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ int err;
+
+ /* Fetch VLAN MemberTag data from the VTU */
+ err = mv88e6xxx_g1_vtu_getnext(chip, entry);
+ if (err)
+ return err;
+
+ if (entry->valid) {
+ /* Fetch (and mask) VLAN PortState data from the STU */
+ err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
+ if (err)
+ return err;
+
+ err = mv88e6185_g1_vtu_data_read(chip, entry);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ int err;
+
+ /* Fetch VLAN MemberTag data from the VTU */
+ err = mv88e6xxx_g1_vtu_getnext(chip, entry);
+ if (err)
+ return err;
+
+ if (entry->valid) {
+ err = mv88e6390_g1_vtu_data_read(chip, entry->member);
+ if (err)
+ return err;
+
+ /* Fetch VLAN PortState data from the STU */
+ err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
+ if (err)
+ return err;
+
+ err = mv88e6390_g1_vtu_data_read(chip, entry->state);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE;
+ int err;
+
+ err = mv88e6xxx_g1_vtu_op_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
+ if (err)
+ return err;
+
+ if (entry->valid) {
+ err = mv88e6185_g1_vtu_data_write(chip, entry);
+ if (err)
+ return err;
+
+ /* VTU DBNum[3:0] are located in VTU Operation 3:0
+ * VTU DBNum[7:4] are located in VTU Operation 11:8
+ */
+ op |= entry->fid & 0x000f;
+ op |= (entry->fid & 0x00f0) << 8;
+ }
+
+ return mv88e6xxx_g1_vtu_op(chip, op);
+}
+
+int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ int err;
+
+ err = mv88e6xxx_g1_vtu_op_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
+ if (err)
+ return err;
+
+ if (entry->valid) {
+ /* Write MemberTag and PortState data */
+ err = mv88e6185_g1_vtu_data_write(chip, entry);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
+ if (err)
+ return err;
+
+ /* Load STU entry */
+ err = mv88e6xxx_g1_vtu_op(chip,
+ MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
+ if (err)
+ return err;
+ }
+
+ /* Load/Purge VTU entry */
+ return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);
+}
+
+int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_vtu_entry *entry)
+{
+ int err;
+
+ err = mv88e6xxx_g1_vtu_op_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
+ if (err)
+ return err;
+
+ if (entry->valid) {
+ /* Write PortState data */
+ err = mv88e6390_g1_vtu_data_write(chip, entry->state);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
+ if (err)
+ return err;
+
+ /* Load STU entry */
+ err = mv88e6xxx_g1_vtu_op(chip,
+ MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);
+ if (err)
+ return err;
+
+ /* Write MemberTag data */
+ err = mv88e6390_g1_vtu_data_write(chip, entry->member);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
+ if (err)
+ return err;
+ }
+
+ /* Load/Purge VTU entry */
+ return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);
+}
+
+int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)
+{
+ int err;
+
+ err = mv88e6xxx_g1_vtu_op_wait(chip);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL);
+}
diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c
index 3e77071949ab..158d0f499874 100644
--- a/drivers/net/dsa/mv88e6xxx/global2.c
+++ b/drivers/net/dsa/mv88e6xxx/global2.c
@@ -1,10 +1,10 @@
/*
- * Marvell 88E6xxx Switch Global 2 Registers support (device address
- * 0x1C)
+ * Marvell 88E6xxx Switch Global 2 Registers support
*
* Copyright (c) 2008 Marvell Semiconductor
*
- * Copyright (c) 2016 Vivien Didelot <[email protected]>
+ * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
+ * Vivien Didelot <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -12,30 +12,32 @@
* (at your option) any later version.
*/
+#include <linux/bitfield.h>
+#include <linux/interrupt.h>
#include <linux/irqdomain.h>
-#include "mv88e6xxx.h"
-#include "global2.h"
-#define ADDR_GLOBAL2 0x1c
+#include "chip.h"
+#include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */
+#include "global2.h"
static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
{
- return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val);
+ return mv88e6xxx_read(chip, MV88E6XXX_G2, reg, val);
}
static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
{
- return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val);
+ return mv88e6xxx_write(chip, MV88E6XXX_G2, reg, val);
}
static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
{
- return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update);
+ return mv88e6xxx_update(chip, MV88E6XXX_G2, reg, update);
}
static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
{
- return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask);
+ return mv88e6xxx_wait(chip, MV88E6XXX_G2, reg, mask);
}
/* Offset 0x02: Management Enable 2x */
@@ -49,7 +51,7 @@ int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
* addresses matching 01:80:c2:00:00:2x as MGMT.
*/
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
- err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff);
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_2X, 0xffff);
if (err)
return err;
}
@@ -58,7 +60,8 @@ int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
* addresses matching 01:80:c2:00:00:0x as MGMT.
*/
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X))
- return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff);
+ return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_0X,
+ 0xffff);
return 0;
}
@@ -70,7 +73,7 @@ static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
{
u16 val = (target << 8) | (port & 0xf);
- return mv88e6xxx_g2_update(chip, GLOBAL2_DEVICE_MAPPING, val);
+ return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_DEVICE_MAPPING, val);
}
static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
@@ -99,15 +102,14 @@ static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
/* Offset 0x07: Trunk Mask Table register */
static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
- bool hask, u16 mask)
+ bool hash, u16 mask)
{
- const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
- u16 val = (num << 12) | (mask & port_mask);
+ u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip));
- if (hask)
- val |= GLOBAL2_TRUNK_MASK_HASK;
+ if (hash)
+ val |= MV88E6XXX_G2_TRUNK_MASK_HASH;
- return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MASK, val);
+ return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_TRUNK_MASK, val);
}
/* Offset 0x08: Trunk Mapping Table register */
@@ -118,7 +120,7 @@ static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
u16 val = (id << 11) | (map & port_mask);
- return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MAPPING, val);
+ return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_TRUNK_MAPPING, val);
}
static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
@@ -147,27 +149,82 @@ static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
* Offset 0x0A: Ingress Rate Data register
*/
-static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
+static int mv88e6xxx_g2_irl_wait(struct mv88e6xxx_chip *chip)
{
- int port, err;
+ return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_IRL_CMD,
+ MV88E6XXX_G2_IRL_CMD_BUSY);
+}
- /* Init all Ingress Rate Limit resources of all ports */
- for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
- /* XXX newer chips (like 88E6390) have different 2-bit ops */
- err = mv88e6xxx_g2_write(chip, GLOBAL2_IRL_CMD,
- GLOBAL2_IRL_CMD_OP_INIT_ALL |
- (port << 8));
- if (err)
- break;
+static int mv88e6xxx_g2_irl_op(struct mv88e6xxx_chip *chip, u16 op, int port,
+ int res, int reg)
+{
+ int err;
- /* Wait for the operation to complete */
- err = mv88e6xxx_g2_wait(chip, GLOBAL2_IRL_CMD,
- GLOBAL2_IRL_CMD_BUSY);
- if (err)
- break;
- }
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_IRL_CMD,
+ MV88E6XXX_G2_IRL_CMD_BUSY | op | (port << 8) |
+ (res << 5) | reg);
+ if (err)
+ return err;
- return err;
+ return mv88e6xxx_g2_irl_wait(chip);
+}
+
+int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port)
+{
+ return mv88e6xxx_g2_irl_op(chip, MV88E6352_G2_IRL_CMD_OP_INIT_ALL, port,
+ 0, 0);
+}
+
+int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port)
+{
+ return mv88e6xxx_g2_irl_op(chip, MV88E6390_G2_IRL_CMD_OP_INIT_ALL, port,
+ 0, 0);
+}
+
+/* Offset 0x0B: Cross-chip Port VLAN (Addr) Register
+ * Offset 0x0C: Cross-chip Port VLAN Data Register
+ */
+
+static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip)
+{
+ return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_PVT_ADDR,
+ MV88E6XXX_G2_PVT_ADDR_BUSY);
+}
+
+static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
+ int src_port, u16 op)
+{
+ int err;
+
+ /* 9-bit Cross-chip PVT pointer: with MV88E6XXX_G2_MISC_5_BIT_PORT
+ * cleared, source device is 5-bit, source port is 4-bit.
+ */
+ op |= MV88E6XXX_G2_PVT_ADDR_BUSY;
+ op |= (src_dev & 0x1f) << 4;
+ op |= (src_port & 0xf);
+
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_ADDR, op);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_pvt_op_wait(chip);
+}
+
+int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
+ int src_port, u16 data)
+{
+ int err;
+
+ err = mv88e6xxx_g2_pvt_op_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_DATA, data);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
+ MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN);
}
/* Offset 0x0D: Switch MAC/WoL/WoF register */
@@ -177,7 +234,7 @@ static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
{
u16 val = (pointer << 8) | data;
- return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val);
+ return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_SWITCH_MAC, val);
}
int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
@@ -200,7 +257,7 @@ static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
{
u16 val = (pointer << 8) | (data & 0x7);
- return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val);
+ return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_PRIO_OVERRIDE, val);
}
static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
@@ -218,31 +275,78 @@ static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
}
/* Offset 0x14: EEPROM Command
- * Offset 0x15: EEPROM Data
+ * Offset 0x15: EEPROM Data (for 16-bit data access)
+ * Offset 0x15: EEPROM Addr (for 8-bit data access)
*/
static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
{
- return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD,
- GLOBAL2_EEPROM_CMD_BUSY |
- GLOBAL2_EEPROM_CMD_RUNNING);
+ return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_EEPROM_CMD,
+ MV88E6XXX_G2_EEPROM_CMD_BUSY |
+ MV88E6XXX_G2_EEPROM_CMD_RUNNING);
}
static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
{
int err;
- err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd);
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_EEPROM_CMD,
+ MV88E6XXX_G2_EEPROM_CMD_BUSY | cmd);
if (err)
return err;
return mv88e6xxx_g2_eeprom_wait(chip);
}
+static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
+ u16 addr, u8 *data)
+{
+ u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ;
+ int err;
+
+ err = mv88e6xxx_g2_eeprom_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &cmd);
+ if (err)
+ return err;
+
+ *data = cmd & 0xff;
+
+ return 0;
+}
+
+static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
+ u16 addr, u8 data)
+{
+ u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE |
+ MV88E6XXX_G2_EEPROM_CMD_WRITE_EN;
+ int err;
+
+ err = mv88e6xxx_g2_eeprom_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
+}
+
static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
u8 addr, u16 *data)
{
- u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
+ u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ | addr;
int err;
err = mv88e6xxx_g2_eeprom_wait(chip);
@@ -253,26 +357,72 @@ static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
if (err)
return err;
- return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data);
+ return mv88e6xxx_g2_read(chip, MV88E6352_G2_EEPROM_DATA, data);
}
static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
u8 addr, u16 data)
{
- u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
+ u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | addr;
int err;
err = mv88e6xxx_g2_eeprom_wait(chip);
if (err)
return err;
- err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data);
+ err = mv88e6xxx_g2_write(chip, MV88E6352_G2_EEPROM_DATA, data);
if (err)
return err;
return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
}
+int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ unsigned int offset = eeprom->offset;
+ unsigned int len = eeprom->len;
+ int err;
+
+ eeprom->len = 0;
+
+ while (len) {
+ err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
+ if (err)
+ return err;
+
+ eeprom->len++;
+ offset++;
+ data++;
+ len--;
+ }
+
+ return 0;
+}
+
+int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ unsigned int offset = eeprom->offset;
+ unsigned int len = eeprom->len;
+ int err;
+
+ eeprom->len = 0;
+
+ while (len) {
+ err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
+ if (err)
+ return err;
+
+ eeprom->len++;
+ offset++;
+ data++;
+ len--;
+ }
+
+ return 0;
+}
+
int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
struct ethtool_eeprom *eeprom, u8 *data)
{
@@ -332,11 +482,11 @@ int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
int err;
/* Ensure the RO WriteEn bit is set */
- err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val);
+ err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &val);
if (err)
return err;
- if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
+ if (!(val & MV88E6XXX_G2_EEPROM_CMD_WRITE_EN))
return -EROFS;
eeprom->len = 0;
@@ -395,53 +545,366 @@ int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
{
- return mv88e6xxx_g2_wait(chip, GLOBAL2_SMI_PHY_CMD,
- GLOBAL2_SMI_PHY_CMD_BUSY);
+ return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_SMI_PHY_CMD,
+ MV88E6XXX_G2_SMI_PHY_CMD_BUSY);
}
static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
{
int err;
- err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_CMD, cmd);
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD,
+ MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd);
if (err)
return err;
return mv88e6xxx_g2_smi_phy_wait(chip);
}
-int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, int addr, int reg,
- u16 *val)
+static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip,
+ bool external, bool c45, u16 op, int dev,
+ int reg)
+{
+ u16 cmd = op;
+
+ if (external)
+ cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL;
+ else
+ cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */
+
+ if (c45)
+ cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */
+ else
+ cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22;
+
+ dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK);
+ cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK;
+ cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK;
+
+ return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
+}
+
+static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip,
+ bool external, u16 op, int dev,
+ int reg)
{
- u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg;
+ return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg);
+}
+
+/* IEEE 802.3 Clause 22 Read Data Register */
+static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip,
+ bool external, int dev, int reg,
+ u16 *data)
+{
+ u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA;
int err;
err = mv88e6xxx_g2_smi_phy_wait(chip);
if (err)
return err;
- err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
+ err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
if (err)
return err;
- return mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val);
+ return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
}
-int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int addr, int reg,
- u16 val)
+/* IEEE 802.3 Clause 22 Write Data Register */
+static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip,
+ bool external, int dev, int reg,
+ u16 data)
{
- u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg;
+ u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA;
int err;
err = mv88e6xxx_g2_smi_phy_wait(chip);
if (err)
return err;
- err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val);
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
if (err)
return err;
- return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
+ return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
+}
+
+static int mv88e6xxx_g2_smi_phy_access_c45(struct mv88e6xxx_chip *chip,
+ bool external, u16 op, int port,
+ int dev)
+{
+ return mv88e6xxx_g2_smi_phy_access(chip, external, true, op, port, dev);
+}
+
+/* IEEE 802.3 Clause 45 Write Address Register */
+static int mv88e6xxx_g2_smi_phy_write_addr_c45(struct mv88e6xxx_chip *chip,
+ bool external, int port, int dev,
+ int addr)
+{
+ u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR;
+ int err;
+
+ err = mv88e6xxx_g2_smi_phy_wait(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, addr);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
+}
+
+/* IEEE 802.3 Clause 45 Read Data Register */
+static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip,
+ bool external, int port, int dev,
+ u16 *data)
+{
+ u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA;
+ int err;
+
+ err = mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
+}
+
+static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
+ bool external, int port, int reg,
+ u16 *data)
+{
+ int dev = (reg >> 16) & 0x1f;
+ int addr = reg & 0xffff;
+ int err;
+
+ err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
+ addr);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev,
+ data);
+}
+
+/* IEEE 802.3 Clause 45 Write Data Register */
+static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip,
+ bool external, int port, int dev,
+ u16 data)
+{
+ u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA;
+ int err;
+
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
+}
+
+static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
+ bool external, int port, int reg,
+ u16 data)
+{
+ int dev = (reg >> 16) & 0x1f;
+ int addr = reg & 0xffff;
+ int err;
+
+ err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
+ addr);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev,
+ data);
+}
+
+int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 *val)
+{
+ struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
+ bool external = mdio_bus->external;
+
+ if (reg & MII_ADDR_C45)
+ return mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, reg,
+ val);
+
+ return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg,
+ val);
+}
+
+int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 val)
+{
+ struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
+ bool external = mdio_bus->external;
+
+ if (reg & MII_ADDR_C45)
+ return mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, reg,
+ val);
+
+ return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg,
+ val);
+}
+
+static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
+{
+ u16 reg;
+
+ mv88e6xxx_g2_read(chip, MV88E6352_G2_WDOG_CTL, &reg);
+
+ dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
+
+ return IRQ_HANDLED;
+}
+
+static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
+{
+ u16 reg;
+
+ mv88e6xxx_g2_read(chip, MV88E6352_G2_WDOG_CTL, &reg);
+
+ reg &= ~(MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE |
+ MV88E6352_G2_WDOG_CTL_QC_ENABLE);
+
+ mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL, reg);
+}
+
+static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
+{
+ return mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL,
+ MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE |
+ MV88E6352_G2_WDOG_CTL_QC_ENABLE |
+ MV88E6352_G2_WDOG_CTL_SWRESET);
+}
+
+const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
+ .irq_action = mv88e6097_watchdog_action,
+ .irq_setup = mv88e6097_watchdog_setup,
+ .irq_free = mv88e6097_watchdog_free,
+};
+
+static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
+{
+ return mv88e6xxx_g2_update(chip, MV88E6390_G2_WDOG_CTL,
+ MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE |
+ MV88E6390_G2_WDOG_CTL_CUT_THROUGH |
+ MV88E6390_G2_WDOG_CTL_QUEUE_CONTROLLER |
+ MV88E6390_G2_WDOG_CTL_EGRESS |
+ MV88E6390_G2_WDOG_CTL_FORCE_IRQ);
+}
+
+static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
+{
+ int err;
+ u16 reg;
+
+ mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
+ MV88E6390_G2_WDOG_CTL_PTR_EVENT);
+ err = mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, &reg);
+
+ dev_info(chip->dev, "Watchdog event: 0x%04x",
+ reg & MV88E6390_G2_WDOG_CTL_DATA_MASK);
+
+ mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
+ MV88E6390_G2_WDOG_CTL_PTR_HISTORY);
+ err = mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, &reg);
+
+ dev_info(chip->dev, "Watchdog history: 0x%04x",
+ reg & MV88E6390_G2_WDOG_CTL_DATA_MASK);
+
+ /* Trigger a software reset to try to recover the switch */
+ if (chip->info->ops->reset)
+ chip->info->ops->reset(chip);
+
+ mv88e6390_watchdog_setup(chip);
+
+ return IRQ_HANDLED;
+}
+
+static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
+{
+ mv88e6xxx_g2_update(chip, MV88E6390_G2_WDOG_CTL,
+ MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE);
+}
+
+const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
+ .irq_action = mv88e6390_watchdog_action,
+ .irq_setup = mv88e6390_watchdog_setup,
+ .irq_free = mv88e6390_watchdog_free,
+};
+
+static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
+{
+ struct mv88e6xxx_chip *chip = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+
+ mutex_lock(&chip->reg_lock);
+ if (chip->info->ops->watchdog_ops->irq_action)
+ ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
+ mutex_unlock(&chip->reg_lock);
+
+ return ret;
+}
+
+static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
+{
+ mutex_lock(&chip->reg_lock);
+ if (chip->info->ops->watchdog_ops->irq_free)
+ chip->info->ops->watchdog_ops->irq_free(chip);
+ mutex_unlock(&chip->reg_lock);
+
+ free_irq(chip->watchdog_irq, chip);
+ irq_dispose_mapping(chip->watchdog_irq);
+}
+
+static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
+{
+ int err;
+
+ chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
+ MV88E6XXX_G2_INT_SOURCE_WATCHDOG);
+ if (chip->watchdog_irq < 0)
+ return chip->watchdog_irq;
+
+ err = request_threaded_irq(chip->watchdog_irq, NULL,
+ mv88e6xxx_g2_watchdog_thread_fn,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ "mv88e6xxx-watchdog", chip);
+ if (err)
+ return err;
+
+ mutex_lock(&chip->reg_lock);
+ if (chip->info->ops->watchdog_ops->irq_setup)
+ err = chip->info->ops->watchdog_ops->irq_setup(chip);
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+/* Offset 0x1D: Misc Register */
+
+static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
+ bool port_5_bit)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_MISC, &val);
+ if (err)
+ return err;
+
+ if (port_5_bit)
+ val |= MV88E6XXX_G2_MISC_5_BIT_PORT;
+ else
+ val &= ~MV88E6XXX_G2_MISC_5_BIT_PORT;
+
+ return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MISC, val);
+}
+
+int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
+{
+ return mv88e6xxx_g2_misc_5_bit_port(chip, false);
}
static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
@@ -470,7 +933,7 @@ static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
u16 reg;
mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, &reg);
+ err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_INT_SOURCE, &reg);
mutex_unlock(&chip->reg_lock);
if (err)
goto out;
@@ -497,7 +960,7 @@ static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
{
struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
- mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked);
+ mv88e6xxx_g2_write(chip, MV88E6XXX_G2_INT_MASK, ~chip->g2_irq.masked);
mutex_unlock(&chip->reg_lock);
}
@@ -532,6 +995,8 @@ void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
{
int irq, virq;
+ mv88e6xxx_g2_watchdog_free(chip);
+
free_irq(chip->device_irq, chip);
irq_dispose_mapping(chip->device_irq);
@@ -562,7 +1027,7 @@ int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
chip->g2_irq.masked = ~0;
chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
- GLOBAL_STATUS_IRQ_DEVICE);
+ MV88E6XXX_G1_STS_IRQ_DEVICE);
if (chip->device_irq < 0) {
err = chip->device_irq;
goto out;
@@ -574,7 +1039,7 @@ int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
if (err)
goto out;
- return 0;
+ return mv88e6xxx_g2_watchdog_setup(chip);
out:
for (irq = 0; irq < 16; irq++) {
@@ -597,11 +1062,11 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
* highest, and send all special multicast frames to the CPU
* port at the highest priority.
*/
- reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
+ reg = MV88E6XXX_G2_SWITCH_MGMT_FORCE_FLOW_CTL_PRI | (0x7 << 4);
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
- reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
- err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg);
+ reg |= MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU | 0x7;
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MGMT, reg);
if (err)
return err;
@@ -615,23 +1080,6 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
if (err)
return err;
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
- /* Disable ingress rate limiting by resetting all per port
- * ingress rate limit resources to their initial state.
- */
- err = mv88e6xxx_g2_clear_irl(chip);
- if (err)
- return err;
- }
-
- if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) {
- /* Initialize Cross-chip Port VLAN Table to reset defaults */
- err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR,
- GLOBAL2_PVT_ADDR_OP_INIT_ONES);
- if (err)
- return err;
- }
-
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
/* Clear the priority override table. */
err = mv88e6xxx_g2_clear_pot(chip);
diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h
index 9aefb7d8b0ad..317ffd8f323d 100644
--- a/drivers/net/dsa/mv88e6xxx/global2.h
+++ b/drivers/net/dsa/mv88e6xxx/global2.h
@@ -1,9 +1,10 @@
/*
- * Marvell 88E6xxx Switch Global 2 Registers support (device address 0x1C)
+ * Marvell 88E6xxx Switch Global 2 Registers support
*
* Copyright (c) 2008 Marvell Semiconductor
*
- * Copyright (c) 2016 Vivien Didelot <[email protected]>
+ * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
+ * Vivien Didelot <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +15,200 @@
#ifndef _MV88E6XXX_GLOBAL2_H
#define _MV88E6XXX_GLOBAL2_H
-#include "mv88e6xxx.h"
+#include "chip.h"
+
+#define MV88E6XXX_G2 0x1c
+
+/* Offset 0x00: Interrupt Source Register */
+#define MV88E6XXX_G2_INT_SOURCE 0x00
+#define MV88E6XXX_G2_INT_SOURCE_WATCHDOG 15
+
+/* Offset 0x01: Interrupt Mask Register */
+#define MV88E6XXX_G2_INT_MASK 0x01
+
+/* Offset 0x02: MGMT Enable Register 2x */
+#define MV88E6XXX_G2_MGMT_EN_2X 0x02
+
+/* Offset 0x03: MGMT Enable Register 0x */
+#define MV88E6XXX_G2_MGMT_EN_0X 0x03
+
+/* Offset 0x04: Flow Control Delay Register */
+#define MV88E6XXX_G2_FLOW_CTL 0x04
+
+/* Offset 0x05: Switch Management Register */
+#define MV88E6XXX_G2_SWITCH_MGMT 0x05
+#define MV88E6XXX_G2_SWITCH_MGMT_USE_DOUBLE_TAG_DATA 0x8000
+#define MV88E6XXX_G2_SWITCH_MGMT_PREVENT_LOOPS 0x4000
+#define MV88E6XXX_G2_SWITCH_MGMT_FLOW_CTL_MSG 0x2000
+#define MV88E6XXX_G2_SWITCH_MGMT_FORCE_FLOW_CTL_PRI 0x0080
+#define MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU 0x0008
+
+/* Offset 0x06: Device Mapping Table Register */
+#define MV88E6XXX_G2_DEVICE_MAPPING 0x06
+#define MV88E6XXX_G2_DEVICE_MAPPING_UPDATE 0x8000
+#define MV88E6XXX_G2_DEVICE_MAPPING_DEV_MASK 0x1f00
+#define MV88E6XXX_G2_DEVICE_MAPPING_PORT_MASK 0x000f
+
+/* Offset 0x07: Trunk Mask Table Register */
+#define MV88E6XXX_G2_TRUNK_MASK 0x07
+#define MV88E6XXX_G2_TRUNK_MASK_UPDATE 0x8000
+#define MV88E6XXX_G2_TRUNK_MASK_NUM_MASK 0x7000
+#define MV88E6XXX_G2_TRUNK_MASK_HASH 0x0800
+
+/* Offset 0x08: Trunk Mapping Table Register */
+#define MV88E6XXX_G2_TRUNK_MAPPING 0x08
+#define MV88E6XXX_G2_TRUNK_MAPPING_UPDATE 0x8000
+#define MV88E6XXX_G2_TRUNK_MAPPING_ID_MASK 0x7800
+
+/* Offset 0x09: Ingress Rate Command Register */
+#define MV88E6XXX_G2_IRL_CMD 0x09
+#define MV88E6XXX_G2_IRL_CMD_BUSY 0x8000
+#define MV88E6352_G2_IRL_CMD_OP_MASK 0x7000
+#define MV88E6352_G2_IRL_CMD_OP_NOOP 0x0000
+#define MV88E6352_G2_IRL_CMD_OP_INIT_ALL 0x1000
+#define MV88E6352_G2_IRL_CMD_OP_INIT_RES 0x2000
+#define MV88E6352_G2_IRL_CMD_OP_WRITE_REG 0x3000
+#define MV88E6352_G2_IRL_CMD_OP_READ_REG 0x4000
+#define MV88E6390_G2_IRL_CMD_OP_MASK 0x6000
+#define MV88E6390_G2_IRL_CMD_OP_READ_REG 0x0000
+#define MV88E6390_G2_IRL_CMD_OP_INIT_ALL 0x2000
+#define MV88E6390_G2_IRL_CMD_OP_INIT_RES 0x4000
+#define MV88E6390_G2_IRL_CMD_OP_WRITE_REG 0x6000
+#define MV88E6352_G2_IRL_CMD_PORT_MASK 0x0f00
+#define MV88E6390_G2_IRL_CMD_PORT_MASK 0x1f00
+#define MV88E6XXX_G2_IRL_CMD_RES_MASK 0x00e0
+#define MV88E6XXX_G2_IRL_CMD_REG_MASK 0x000f
+
+/* Offset 0x0A: Ingress Rate Data Register */
+#define MV88E6XXX_G2_IRL_DATA 0x0a
+#define MV88E6XXX_G2_IRL_DATA_MASK 0xffff
+
+/* Offset 0x0B: Cross-chip Port VLAN Register */
+#define MV88E6XXX_G2_PVT_ADDR 0x0b
+#define MV88E6XXX_G2_PVT_ADDR_BUSY 0x8000
+#define MV88E6XXX_G2_PVT_ADDR_OP_MASK 0x7000
+#define MV88E6XXX_G2_PVT_ADDR_OP_INIT_ONES 0x1000
+#define MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN 0x3000
+#define MV88E6XXX_G2_PVT_ADDR_OP_READ 0x4000
+#define MV88E6XXX_G2_PVT_ADDR_PTR_MASK 0x01ff
+
+/* Offset 0x0C: Cross-chip Port VLAN Data Register */
+#define MV88E6XXX_G2_PVT_DATA 0x0c
+#define MV88E6XXX_G2_PVT_DATA_MASK 0x7f
+
+/* Offset 0x0D: Switch MAC/WoL/WoF Register */
+#define MV88E6XXX_G2_SWITCH_MAC 0x0d
+#define MV88E6XXX_G2_SWITCH_MAC_UPDATE 0x8000
+#define MV88E6XXX_G2_SWITCH_MAC_PTR_MASK 0x1f00
+#define MV88E6XXX_G2_SWITCH_MAC_DATA_MASK 0x00ff
+
+/* Offset 0x0E: ATU Stats Register */
+#define MV88E6XXX_G2_ATU_STATS 0x0e
+
+/* Offset 0x0F: Priority Override Table */
+#define MV88E6XXX_G2_PRIO_OVERRIDE 0x0f
+#define MV88E6XXX_G2_PRIO_OVERRIDE_UPDATE 0x8000
+#define MV88E6XXX_G2_PRIO_OVERRIDE_FPRISET 0x1000
+#define MV88E6XXX_G2_PRIO_OVERRIDE_PTR_MASK 0x0f00
+#define MV88E6352_G2_PRIO_OVERRIDE_QPRIAVBEN 0x0080
+#define MV88E6352_G2_PRIO_OVERRIDE_DATAAVB_MASK 0x0030
+#define MV88E6XXX_G2_PRIO_OVERRIDE_QFPRIEN 0x0008
+#define MV88E6XXX_G2_PRIO_OVERRIDE_DATA_MASK 0x0007
+
+/* Offset 0x14: EEPROM Command */
+#define MV88E6XXX_G2_EEPROM_CMD 0x14
+#define MV88E6XXX_G2_EEPROM_CMD_BUSY 0x8000
+#define MV88E6XXX_G2_EEPROM_CMD_OP_MASK 0x7000
+#define MV88E6XXX_G2_EEPROM_CMD_OP_WRITE 0x3000
+#define MV88E6XXX_G2_EEPROM_CMD_OP_READ 0x4000
+#define MV88E6XXX_G2_EEPROM_CMD_OP_LOAD 0x6000
+#define MV88E6XXX_G2_EEPROM_CMD_RUNNING 0x0800
+#define MV88E6XXX_G2_EEPROM_CMD_WRITE_EN 0x0400
+#define MV88E6352_G2_EEPROM_CMD_ADDR_MASK 0x00ff
+#define MV88E6390_G2_EEPROM_CMD_DATA_MASK 0x00ff
+
+/* Offset 0x15: EEPROM Data */
+#define MV88E6352_G2_EEPROM_DATA 0x15
+#define MV88E6352_G2_EEPROM_DATA_MASK 0xffff
+
+/* Offset 0x15: EEPROM Addr */
+#define MV88E6390_G2_EEPROM_ADDR 0x15
+#define MV88E6390_G2_EEPROM_ADDR_MASK 0xffff
+
+/* Offset 0x16: AVB Command Register */
+#define MV88E6352_G2_AVB_CMD 0x16
+
+/* Offset 0x17: AVB Data Register */
+#define MV88E6352_G2_AVB_DATA 0x17
+
+/* Offset 0x18: SMI PHY Command Register */
+#define MV88E6XXX_G2_SMI_PHY_CMD 0x18
+#define MV88E6XXX_G2_SMI_PHY_CMD_BUSY 0x8000
+#define MV88E6390_G2_SMI_PHY_CMD_FUNC_MASK 0x6000
+#define MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL 0x0000
+#define MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL 0x2000
+#define MV88E6390_G2_SMI_PHY_CMD_FUNC_SETUP 0x4000
+#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_MASK 0x1000
+#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_45 0x0000
+#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_22 0x1000
+#define MV88E6XXX_G2_SMI_PHY_CMD_OP_MASK 0x0c00
+#define MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA 0x0400
+#define MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA 0x0800
+#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR 0x0000
+#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA 0x0400
+#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA_INC 0x0800
+#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA 0x0c00
+#define MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK 0x03e0
+#define MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK 0x001f
+#define MV88E6XXX_G2_SMI_PHY_CMD_SETUP_PTR_MASK 0x03ff
+
+/* Offset 0x19: SMI PHY Data Register */
+#define MV88E6XXX_G2_SMI_PHY_DATA 0x19
+
+/* Offset 0x1A: Scratch and Misc. Register */
+#define MV88E6XXX_G2_SCRATCH_MISC_MISC 0x1a
+#define MV88E6XXX_G2_SCRATCH_MISC_UPDATE 0x8000
+#define MV88E6XXX_G2_SCRATCH_MISC_PTR_MASK 0x7f00
+#define MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK 0x00ff
+
+/* Offset 0x1B: Watch Dog Control Register */
+#define MV88E6352_G2_WDOG_CTL 0x1b
+#define MV88E6352_G2_WDOG_CTL_EGRESS_EVENT 0x0080
+#define MV88E6352_G2_WDOG_CTL_RMU_TIMEOUT 0x0040
+#define MV88E6352_G2_WDOG_CTL_QC_ENABLE 0x0020
+#define MV88E6352_G2_WDOG_CTL_EGRESS_HISTORY 0x0010
+#define MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE 0x0008
+#define MV88E6352_G2_WDOG_CTL_FORCE_IRQ 0x0004
+#define MV88E6352_G2_WDOG_CTL_HISTORY 0x0002
+#define MV88E6352_G2_WDOG_CTL_SWRESET 0x0001
+
+/* Offset 0x1B: Watch Dog Control Register */
+#define MV88E6390_G2_WDOG_CTL 0x1b
+#define MV88E6390_G2_WDOG_CTL_UPDATE 0x8000
+#define MV88E6390_G2_WDOG_CTL_PTR_MASK 0x7f00
+#define MV88E6390_G2_WDOG_CTL_PTR_INT_SOURCE 0x0000
+#define MV88E6390_G2_WDOG_CTL_PTR_INT_STS 0x1000
+#define MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE 0x1100
+#define MV88E6390_G2_WDOG_CTL_PTR_EVENT 0x1200
+#define MV88E6390_G2_WDOG_CTL_PTR_HISTORY 0x1300
+#define MV88E6390_G2_WDOG_CTL_DATA_MASK 0x00ff
+#define MV88E6390_G2_WDOG_CTL_CUT_THROUGH 0x0008
+#define MV88E6390_G2_WDOG_CTL_QUEUE_CONTROLLER 0x0004
+#define MV88E6390_G2_WDOG_CTL_EGRESS 0x0002
+#define MV88E6390_G2_WDOG_CTL_FORCE_IRQ 0x0001
+
+/* Offset 0x1C: QoS Weights Register */
+#define MV88E6XXX_G2_QOS_WEIGHTS 0x1c
+#define MV88E6XXX_G2_QOS_WEIGHTS_UPDATE 0x8000
+#define MV88E6352_G2_QOS_WEIGHTS_PTR_MASK 0x3f00
+#define MV88E6390_G2_QOS_WEIGHTS_PTR_MASK 0x7f00
+#define MV88E6XXX_G2_QOS_WEIGHTS_DATA_MASK 0x00ff
+
+/* Offset 0x1D: Misc Register */
+#define MV88E6XXX_G2_MISC 0x1d
+#define MV88E6XXX_G2_MISC_5_BIT_PORT 0x4000
+#define MV88E6352_G2_NOEGR_POLICY 0x2000
+#define MV88E6390_G2_LAG_ID_4 0x2000
#ifdef CONFIG_NET_DSA_MV88E6XXX_GLOBAL2
@@ -23,20 +217,39 @@ static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
return 0;
}
-int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, int addr, int reg,
- u16 *val);
-int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int addr, int reg,
- u16 val);
+int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port);
+int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port);
+
+int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus,
+ int addr, int reg, u16 *val);
+int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus,
+ int addr, int reg, u16 val);
int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr);
+
+int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom, u8 *data);
+int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom, u8 *data);
+
int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
struct ethtool_eeprom *eeprom, u8 *data);
int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
struct ethtool_eeprom *eeprom, u8 *data);
+
+int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
+ int src_port, u16 data);
+int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip);
+
int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip);
int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip);
void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip);
int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
+extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops;
+extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops;
+
#else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */
static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
@@ -49,13 +262,27 @@ static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
return 0;
}
+static inline int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip,
+ int port)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip,
+ int port)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus,
int addr, int reg, u16 *val)
{
return -EOPNOTSUPP;
}
static inline int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus,
int addr, int reg, u16 val)
{
return -EOPNOTSUPP;
@@ -67,6 +294,20 @@ static inline int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip,
return -EOPNOTSUPP;
}
+static inline int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom,
+ u8 *data)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
+ struct ethtool_eeprom *eeprom,
+ u8 *data)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
struct ethtool_eeprom *eeprom,
u8 *data)
@@ -81,6 +322,17 @@ static inline int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
return -EOPNOTSUPP;
}
+static inline int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip,
+ int src_dev, int src_port, u16 data)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
{
return -EOPNOTSUPP;
@@ -100,6 +352,9 @@ static inline int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
return -EOPNOTSUPP;
}
+static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {};
+static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {};
+
#endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */
#endif /* _MV88E6XXX_GLOBAL2_H */
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
deleted file mode 100644
index af54baea47cf..000000000000
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ /dev/null
@@ -1,890 +0,0 @@
-/*
- * Marvell 88e6xxx common definitions
- *
- * Copyright (c) 2008 Marvell Semiconductor
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __MV88E6XXX_H
-#define __MV88E6XXX_H
-
-#include <linux/if_vlan.h>
-#include <linux/irq.h>
-#include <linux/gpio/consumer.h>
-
-#ifndef UINT64_MAX
-#define UINT64_MAX (u64)(~((u64)0))
-#endif
-
-#define SMI_CMD 0x00
-#define SMI_CMD_BUSY BIT(15)
-#define SMI_CMD_CLAUSE_22 BIT(12)
-#define SMI_CMD_OP_22_WRITE ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
-#define SMI_CMD_OP_22_READ ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
-#define SMI_CMD_OP_45_WRITE_ADDR ((0 << 10) | SMI_CMD_BUSY)
-#define SMI_CMD_OP_45_WRITE_DATA ((1 << 10) | SMI_CMD_BUSY)
-#define SMI_CMD_OP_45_READ_DATA ((2 << 10) | SMI_CMD_BUSY)
-#define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY)
-#define SMI_DATA 0x01
-
-/* PHY Registers */
-#define PHY_PAGE 0x16
-#define PHY_PAGE_COPPER 0x00
-
-#define ADDR_SERDES 0x0f
-#define SERDES_PAGE_FIBER 0x01
-
-#define PORT_STATUS 0x00
-#define PORT_STATUS_PAUSE_EN BIT(15)
-#define PORT_STATUS_MY_PAUSE BIT(14)
-#define PORT_STATUS_HD_FLOW BIT(13)
-#define PORT_STATUS_PHY_DETECT BIT(12)
-#define PORT_STATUS_LINK BIT(11)
-#define PORT_STATUS_DUPLEX BIT(10)
-#define PORT_STATUS_SPEED_MASK 0x0300
-#define PORT_STATUS_SPEED_10 0x0000
-#define PORT_STATUS_SPEED_100 0x0100
-#define PORT_STATUS_SPEED_1000 0x0200
-#define PORT_STATUS_EEE BIT(6) /* 6352 */
-#define PORT_STATUS_AM_DIS BIT(6) /* 6165 */
-#define PORT_STATUS_MGMII BIT(6) /* 6185 */
-#define PORT_STATUS_TX_PAUSED BIT(5)
-#define PORT_STATUS_FLOW_CTRL BIT(4)
-#define PORT_STATUS_CMODE_MASK 0x0f
-#define PORT_STATUS_CMODE_100BASE_X 0x8
-#define PORT_STATUS_CMODE_1000BASE_X 0x9
-#define PORT_STATUS_CMODE_SGMII 0xa
-#define PORT_PCS_CTRL 0x01
-#define PORT_PCS_CTRL_RGMII_DELAY_RXCLK BIT(15)
-#define PORT_PCS_CTRL_RGMII_DELAY_TXCLK BIT(14)
-#define PORT_PCS_CTRL_FORCE_SPEED BIT(13) /* 6390 */
-#define PORT_PCS_CTRL_ALTSPEED BIT(12) /* 6390 */
-#define PORT_PCS_CTRL_200BASE BIT(12) /* 6352 */
-#define PORT_PCS_CTRL_FC BIT(7)
-#define PORT_PCS_CTRL_FORCE_FC BIT(6)
-#define PORT_PCS_CTRL_LINK_UP BIT(5)
-#define PORT_PCS_CTRL_FORCE_LINK BIT(4)
-#define PORT_PCS_CTRL_DUPLEX_FULL BIT(3)
-#define PORT_PCS_CTRL_FORCE_DUPLEX BIT(2)
-#define PORT_PCS_CTRL_SPEED_MASK (0x03)
-#define PORT_PCS_CTRL_SPEED_10 (0x00)
-#define PORT_PCS_CTRL_SPEED_100 (0x01)
-#define PORT_PCS_CTRL_SPEED_200 (0x02) /* 6065 and non Gb chips */
-#define PORT_PCS_CTRL_SPEED_1000 (0x02)
-#define PORT_PCS_CTRL_SPEED_10000 (0x03) /* 6390X */
-#define PORT_PCS_CTRL_SPEED_UNFORCED (0x03)
-#define PORT_PAUSE_CTRL 0x02
-#define PORT_FLOW_CTRL_LIMIT_IN ((0x00 << 8) | BIT(15))
-#define PORT_FLOW_CTRL_LIMIT_OUT ((0x01 << 8) | BIT(15))
-#define PORT_SWITCH_ID 0x03
-#define PORT_SWITCH_ID_PROD_NUM_6085 0x04a
-#define PORT_SWITCH_ID_PROD_NUM_6095 0x095
-#define PORT_SWITCH_ID_PROD_NUM_6097 0x099
-#define PORT_SWITCH_ID_PROD_NUM_6131 0x106
-#define PORT_SWITCH_ID_PROD_NUM_6320 0x115
-#define PORT_SWITCH_ID_PROD_NUM_6123 0x121
-#define PORT_SWITCH_ID_PROD_NUM_6161 0x161
-#define PORT_SWITCH_ID_PROD_NUM_6165 0x165
-#define PORT_SWITCH_ID_PROD_NUM_6171 0x171
-#define PORT_SWITCH_ID_PROD_NUM_6172 0x172
-#define PORT_SWITCH_ID_PROD_NUM_6175 0x175
-#define PORT_SWITCH_ID_PROD_NUM_6176 0x176
-#define PORT_SWITCH_ID_PROD_NUM_6185 0x1a7
-#define PORT_SWITCH_ID_PROD_NUM_6190 0x190
-#define PORT_SWITCH_ID_PROD_NUM_6190X 0x0a0
-#define PORT_SWITCH_ID_PROD_NUM_6191 0x191
-#define PORT_SWITCH_ID_PROD_NUM_6240 0x240
-#define PORT_SWITCH_ID_PROD_NUM_6290 0x290
-#define PORT_SWITCH_ID_PROD_NUM_6321 0x310
-#define PORT_SWITCH_ID_PROD_NUM_6352 0x352
-#define PORT_SWITCH_ID_PROD_NUM_6350 0x371
-#define PORT_SWITCH_ID_PROD_NUM_6351 0x375
-#define PORT_SWITCH_ID_PROD_NUM_6390 0x390
-#define PORT_SWITCH_ID_PROD_NUM_6390X 0x0a1
-#define PORT_CONTROL 0x04
-#define PORT_CONTROL_USE_CORE_TAG BIT(15)
-#define PORT_CONTROL_DROP_ON_LOCK BIT(14)
-#define PORT_CONTROL_EGRESS_UNMODIFIED (0x0 << 12)
-#define PORT_CONTROL_EGRESS_UNTAGGED (0x1 << 12)
-#define PORT_CONTROL_EGRESS_TAGGED (0x2 << 12)
-#define PORT_CONTROL_EGRESS_ADD_TAG (0x3 << 12)
-#define PORT_CONTROL_EGRESS_MASK (0x3 << 12)
-#define PORT_CONTROL_HEADER BIT(11)
-#define PORT_CONTROL_IGMP_MLD_SNOOP BIT(10)
-#define PORT_CONTROL_DOUBLE_TAG BIT(9)
-#define PORT_CONTROL_FRAME_MODE_NORMAL (0x0 << 8)
-#define PORT_CONTROL_FRAME_MODE_DSA (0x1 << 8)
-#define PORT_CONTROL_FRAME_MODE_PROVIDER (0x2 << 8)
-#define PORT_CONTROL_FRAME_ETHER_TYPE_DSA (0x3 << 8)
-#define PORT_CONTROL_FRAME_MASK (0x3 << 8)
-#define PORT_CONTROL_DSA_TAG BIT(8)
-#define PORT_CONTROL_VLAN_TUNNEL BIT(7)
-#define PORT_CONTROL_TAG_IF_BOTH BIT(6)
-#define PORT_CONTROL_USE_IP BIT(5)
-#define PORT_CONTROL_USE_TAG BIT(4)
-#define PORT_CONTROL_FORWARD_UNKNOWN_MC BIT(3)
-#define PORT_CONTROL_FORWARD_UNKNOWN BIT(2)
-#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_DA (0x0 << 2)
-#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_MULTICAST_DA (0x1 << 2)
-#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_UNITCAST_DA (0x2 << 2)
-#define PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA (0x3 << 2)
-#define PORT_CONTROL_STATE_MASK 0x03
-#define PORT_CONTROL_STATE_DISABLED 0x00
-#define PORT_CONTROL_STATE_BLOCKING 0x01
-#define PORT_CONTROL_STATE_LEARNING 0x02
-#define PORT_CONTROL_STATE_FORWARDING 0x03
-#define PORT_CONTROL_1 0x05
-#define PORT_CONTROL_1_FID_11_4_MASK (0xff << 0)
-#define PORT_BASE_VLAN 0x06
-#define PORT_BASE_VLAN_FID_3_0_MASK (0xf << 12)
-#define PORT_DEFAULT_VLAN 0x07
-#define PORT_DEFAULT_VLAN_MASK 0xfff
-#define PORT_CONTROL_2 0x08
-#define PORT_CONTROL_2_IGNORE_FCS BIT(15)
-#define PORT_CONTROL_2_VTU_PRI_OVERRIDE BIT(14)
-#define PORT_CONTROL_2_SA_PRIO_OVERRIDE BIT(13)
-#define PORT_CONTROL_2_DA_PRIO_OVERRIDE BIT(12)
-#define PORT_CONTROL_2_JUMBO_1522 (0x00 << 12)
-#define PORT_CONTROL_2_JUMBO_2048 (0x01 << 12)
-#define PORT_CONTROL_2_JUMBO_10240 (0x02 << 12)
-#define PORT_CONTROL_2_8021Q_MASK (0x03 << 10)
-#define PORT_CONTROL_2_8021Q_DISABLED (0x00 << 10)
-#define PORT_CONTROL_2_8021Q_FALLBACK (0x01 << 10)
-#define PORT_CONTROL_2_8021Q_CHECK (0x02 << 10)
-#define PORT_CONTROL_2_8021Q_SECURE (0x03 << 10)
-#define PORT_CONTROL_2_DISCARD_TAGGED BIT(9)
-#define PORT_CONTROL_2_DISCARD_UNTAGGED BIT(8)
-#define PORT_CONTROL_2_MAP_DA BIT(7)
-#define PORT_CONTROL_2_DEFAULT_FORWARD BIT(6)
-#define PORT_CONTROL_2_FORWARD_UNKNOWN BIT(6)
-#define PORT_CONTROL_2_EGRESS_MONITOR BIT(5)
-#define PORT_CONTROL_2_INGRESS_MONITOR BIT(4)
-#define PORT_RATE_CONTROL 0x09
-#define PORT_RATE_CONTROL_2 0x0a
-#define PORT_ASSOC_VECTOR 0x0b
-#define PORT_ASSOC_VECTOR_HOLD_AT_1 BIT(15)
-#define PORT_ASSOC_VECTOR_INT_AGE_OUT BIT(14)
-#define PORT_ASSOC_VECTOR_LOCKED_PORT BIT(13)
-#define PORT_ASSOC_VECTOR_IGNORE_WRONG BIT(12)
-#define PORT_ASSOC_VECTOR_REFRESH_LOCKED BIT(11)
-#define PORT_ATU_CONTROL 0x0c
-#define PORT_PRI_OVERRIDE 0x0d
-#define PORT_ETH_TYPE 0x0f
-#define PORT_IN_DISCARD_LO 0x10
-#define PORT_IN_DISCARD_HI 0x11
-#define PORT_IN_FILTERED 0x12
-#define PORT_OUT_FILTERED 0x13
-#define PORT_TAG_REGMAP_0123 0x18
-#define PORT_TAG_REGMAP_4567 0x19
-#define PORT_IEEE_PRIO_MAP_TABLE 0x18 /* 6390 */
-#define PORT_IEEE_PRIO_MAP_TABLE_UPDATE BIT(15)
-#define PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP (0x0 << 12)
-#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP (0x1 << 12)
-#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP (0x2 << 12)
-#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP (0x3 << 12)
-#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_DSCP (0x5 << 12)
-#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_DSCP (0x6 << 12)
-#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_DSCP (0x7 << 12)
-#define PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT 9
-
-#define GLOBAL_STATUS 0x00
-#define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */
-#define GLOBAL_STATUS_PPU_STATE_MASK (0x3 << 14) /* 6165 6185 */
-#define GLOBAL_STATUS_PPU_STATE_DISABLED_RST (0x0 << 14)
-#define GLOBAL_STATUS_PPU_STATE_INITIALIZING (0x1 << 14)
-#define GLOBAL_STATUS_PPU_STATE_DISABLED (0x2 << 14)
-#define GLOBAL_STATUS_PPU_STATE_POLLING (0x3 << 14)
-#define GLOBAL_STATUS_INIT_READY BIT(11)
-#define GLOBAL_STATUS_IRQ_AVB 8
-#define GLOBAL_STATUS_IRQ_DEVICE 7
-#define GLOBAL_STATUS_IRQ_STATS 6
-#define GLOBAL_STATUS_IRQ_VTU_PROBLEM 5
-#define GLOBAL_STATUS_IRQ_VTU_DONE 4
-#define GLOBAL_STATUS_IRQ_ATU_PROBLEM 3
-#define GLOBAL_STATUS_IRQ_ATU_DONE 2
-#define GLOBAL_STATUS_IRQ_TCAM_DONE 1
-#define GLOBAL_STATUS_IRQ_EEPROM_DONE 0
-#define GLOBAL_MAC_01 0x01
-#define GLOBAL_MAC_23 0x02
-#define GLOBAL_MAC_45 0x03
-#define GLOBAL_ATU_FID 0x01
-#define GLOBAL_VTU_FID 0x02
-#define GLOBAL_VTU_FID_MASK 0xfff
-#define GLOBAL_VTU_SID 0x03 /* 6097 6165 6351 6352 */
-#define GLOBAL_VTU_SID_MASK 0x3f
-#define GLOBAL_CONTROL 0x04
-#define GLOBAL_CONTROL_SW_RESET BIT(15)
-#define GLOBAL_CONTROL_PPU_ENABLE BIT(14)
-#define GLOBAL_CONTROL_DISCARD_EXCESS BIT(13) /* 6352 */
-#define GLOBAL_CONTROL_SCHED_PRIO BIT(11) /* 6152 */
-#define GLOBAL_CONTROL_MAX_FRAME_1632 BIT(10) /* 6152 */
-#define GLOBAL_CONTROL_RELOAD_EEPROM BIT(9) /* 6152 */
-#define GLOBAL_CONTROL_DEVICE_EN BIT(7)
-#define GLOBAL_CONTROL_STATS_DONE_EN BIT(6)
-#define GLOBAL_CONTROL_VTU_PROBLEM_EN BIT(5)
-#define GLOBAL_CONTROL_VTU_DONE_EN BIT(4)
-#define GLOBAL_CONTROL_ATU_PROBLEM_EN BIT(3)
-#define GLOBAL_CONTROL_ATU_DONE_EN BIT(2)
-#define GLOBAL_CONTROL_TCAM_EN BIT(1)
-#define GLOBAL_CONTROL_EEPROM_DONE_EN BIT(0)
-#define GLOBAL_VTU_OP 0x05
-#define GLOBAL_VTU_OP_BUSY BIT(15)
-#define GLOBAL_VTU_OP_FLUSH_ALL ((0x01 << 12) | GLOBAL_VTU_OP_BUSY)
-#define GLOBAL_VTU_OP_VTU_LOAD_PURGE ((0x03 << 12) | GLOBAL_VTU_OP_BUSY)
-#define GLOBAL_VTU_OP_VTU_GET_NEXT ((0x04 << 12) | GLOBAL_VTU_OP_BUSY)
-#define GLOBAL_VTU_OP_STU_LOAD_PURGE ((0x05 << 12) | GLOBAL_VTU_OP_BUSY)
-#define GLOBAL_VTU_OP_STU_GET_NEXT ((0x06 << 12) | GLOBAL_VTU_OP_BUSY)
-#define GLOBAL_VTU_VID 0x06
-#define GLOBAL_VTU_VID_MASK 0xfff
-#define GLOBAL_VTU_VID_VALID BIT(12)
-#define GLOBAL_VTU_DATA_0_3 0x07
-#define GLOBAL_VTU_DATA_4_7 0x08
-#define GLOBAL_VTU_DATA_8_11 0x09
-#define GLOBAL_VTU_STU_DATA_MASK 0x03
-#define GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED 0x00
-#define GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED 0x01
-#define GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED 0x02
-#define GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER 0x03
-#define GLOBAL_STU_DATA_PORT_STATE_DISABLED 0x00
-#define GLOBAL_STU_DATA_PORT_STATE_BLOCKING 0x01
-#define GLOBAL_STU_DATA_PORT_STATE_LEARNING 0x02
-#define GLOBAL_STU_DATA_PORT_STATE_FORWARDING 0x03
-#define GLOBAL_ATU_CONTROL 0x0a
-#define GLOBAL_ATU_CONTROL_LEARN2ALL BIT(3)
-#define GLOBAL_ATU_OP 0x0b
-#define GLOBAL_ATU_OP_BUSY BIT(15)
-#define GLOBAL_ATU_OP_NOP (0 << 12)
-#define GLOBAL_ATU_OP_FLUSH_MOVE_ALL ((1 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC ((2 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_LOAD_DB ((3 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_GET_NEXT_DB ((4 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB ((5 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_GET_CLR_VIOLATION ((7 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_DATA 0x0c
-#define GLOBAL_ATU_DATA_TRUNK BIT(15)
-#define GLOBAL_ATU_DATA_TRUNK_ID_MASK 0x00f0
-#define GLOBAL_ATU_DATA_TRUNK_ID_SHIFT 4
-#define GLOBAL_ATU_DATA_PORT_VECTOR_MASK 0x3ff0
-#define GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT 4
-#define GLOBAL_ATU_DATA_STATE_MASK 0x0f
-#define GLOBAL_ATU_DATA_STATE_UNUSED 0x00
-#define GLOBAL_ATU_DATA_STATE_UC_MGMT 0x0d
-#define GLOBAL_ATU_DATA_STATE_UC_STATIC 0x0e
-#define GLOBAL_ATU_DATA_STATE_UC_PRIO_OVER 0x0f
-#define GLOBAL_ATU_DATA_STATE_MC_NONE_RATE 0x05
-#define GLOBAL_ATU_DATA_STATE_MC_STATIC 0x07
-#define GLOBAL_ATU_DATA_STATE_MC_MGMT 0x0e
-#define GLOBAL_ATU_DATA_STATE_MC_PRIO_OVER 0x0f
-#define GLOBAL_ATU_MAC_01 0x0d
-#define GLOBAL_ATU_MAC_23 0x0e
-#define GLOBAL_ATU_MAC_45 0x0f
-#define GLOBAL_IP_PRI_0 0x10
-#define GLOBAL_IP_PRI_1 0x11
-#define GLOBAL_IP_PRI_2 0x12
-#define GLOBAL_IP_PRI_3 0x13
-#define GLOBAL_IP_PRI_4 0x14
-#define GLOBAL_IP_PRI_5 0x15
-#define GLOBAL_IP_PRI_6 0x16
-#define GLOBAL_IP_PRI_7 0x17
-#define GLOBAL_IEEE_PRI 0x18
-#define GLOBAL_CORE_TAG_TYPE 0x19
-#define GLOBAL_MONITOR_CONTROL 0x1a
-#define GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT 12
-#define GLOBAL_MONITOR_CONTROL_INGRESS_MASK (0xf << 12)
-#define GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT 8
-#define GLOBAL_MONITOR_CONTROL_EGRESS_MASK (0xf << 8)
-#define GLOBAL_MONITOR_CONTROL_ARP_SHIFT 4
-#define GLOBAL_MONITOR_CONTROL_ARP_MASK (0xf << 4)
-#define GLOBAL_MONITOR_CONTROL_MIRROR_SHIFT 0
-#define GLOBAL_MONITOR_CONTROL_ARP_DISABLED (0xf0)
-#define GLOBAL_MONITOR_CONTROL_UPDATE BIT(15)
-#define GLOBAL_MONITOR_CONTROL_0180C280000000XLO (0x00 << 8)
-#define GLOBAL_MONITOR_CONTROL_0180C280000000XHI (0x01 << 8)
-#define GLOBAL_MONITOR_CONTROL_0180C280000002XLO (0x02 << 8)
-#define GLOBAL_MONITOR_CONTROL_0180C280000002XHI (0x03 << 8)
-#define GLOBAL_MONITOR_CONTROL_INGRESS (0x20 << 8)
-#define GLOBAL_MONITOR_CONTROL_EGRESS (0x21 << 8)
-#define GLOBAL_MONITOR_CONTROL_CPU_DEST (0x30 << 8)
-#define GLOBAL_CONTROL_2 0x1c
-#define GLOBAL_CONTROL_2_NO_CASCADE 0xe000
-#define GLOBAL_CONTROL_2_MULTIPLE_CASCADE 0xf000
-#define GLOBAL_CONTROL_2_HIST_RX (0x1 << 6)
-#define GLOBAL_CONTROL_2_HIST_TX (0x2 << 6)
-#define GLOBAL_CONTROL_2_HIST_RX_TX (0x3 << 6)
-#define GLOBAL_STATS_OP 0x1d
-#define GLOBAL_STATS_OP_BUSY BIT(15)
-#define GLOBAL_STATS_OP_NOP (0 << 12)
-#define GLOBAL_STATS_OP_FLUSH_ALL ((1 << 12) | GLOBAL_STATS_OP_BUSY)
-#define GLOBAL_STATS_OP_FLUSH_PORT ((2 << 12) | GLOBAL_STATS_OP_BUSY)
-#define GLOBAL_STATS_OP_READ_CAPTURED ((4 << 12) | GLOBAL_STATS_OP_BUSY)
-#define GLOBAL_STATS_OP_CAPTURE_PORT ((5 << 12) | GLOBAL_STATS_OP_BUSY)
-#define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY)
-#define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY)
-#define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY)
-#define GLOBAL_STATS_OP_BANK_1_BIT_9 BIT(9)
-#define GLOBAL_STATS_OP_BANK_1_BIT_10 BIT(10)
-#define GLOBAL_STATS_COUNTER_32 0x1e
-#define GLOBAL_STATS_COUNTER_01 0x1f
-
-#define GLOBAL2_INT_SOURCE 0x00
-#define GLOBAL2_INT_MASK 0x01
-#define GLOBAL2_MGMT_EN_2X 0x02
-#define GLOBAL2_MGMT_EN_0X 0x03
-#define GLOBAL2_FLOW_CONTROL 0x04
-#define GLOBAL2_SWITCH_MGMT 0x05
-#define GLOBAL2_SWITCH_MGMT_USE_DOUBLE_TAG_DATA BIT(15)
-#define GLOBAL2_SWITCH_MGMT_PREVENT_LOOPS BIT(14)
-#define GLOBAL2_SWITCH_MGMT_FLOW_CONTROL_MSG BIT(13)
-#define GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI BIT(7)
-#define GLOBAL2_SWITCH_MGMT_RSVD2CPU BIT(3)
-#define GLOBAL2_DEVICE_MAPPING 0x06
-#define GLOBAL2_DEVICE_MAPPING_UPDATE BIT(15)
-#define GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT 8
-#define GLOBAL2_DEVICE_MAPPING_PORT_MASK 0x0f
-#define GLOBAL2_TRUNK_MASK 0x07
-#define GLOBAL2_TRUNK_MASK_UPDATE BIT(15)
-#define GLOBAL2_TRUNK_MASK_NUM_SHIFT 12
-#define GLOBAL2_TRUNK_MASK_HASK BIT(11)
-#define GLOBAL2_TRUNK_MAPPING 0x08
-#define GLOBAL2_TRUNK_MAPPING_UPDATE BIT(15)
-#define GLOBAL2_TRUNK_MAPPING_ID_SHIFT 11
-#define GLOBAL2_IRL_CMD 0x09
-#define GLOBAL2_IRL_CMD_BUSY BIT(15)
-#define GLOBAL2_IRL_CMD_OP_INIT_ALL ((0x001 << 12) | GLOBAL2_IRL_CMD_BUSY)
-#define GLOBAL2_IRL_CMD_OP_INIT_SEL ((0x010 << 12) | GLOBAL2_IRL_CMD_BUSY)
-#define GLOBAL2_IRL_CMD_OP_WRITE_SEL ((0x011 << 12) | GLOBAL2_IRL_CMD_BUSY)
-#define GLOBAL2_IRL_CMD_OP_READ_SEL ((0x100 << 12) | GLOBAL2_IRL_CMD_BUSY)
-#define GLOBAL2_IRL_DATA 0x0a
-#define GLOBAL2_PVT_ADDR 0x0b
-#define GLOBAL2_PVT_ADDR_BUSY BIT(15)
-#define GLOBAL2_PVT_ADDR_OP_INIT_ONES ((0x01 << 12) | GLOBAL2_PVT_ADDR_BUSY)
-#define GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN ((0x03 << 12) | GLOBAL2_PVT_ADDR_BUSY)
-#define GLOBAL2_PVT_ADDR_OP_READ ((0x04 << 12) | GLOBAL2_PVT_ADDR_BUSY)
-#define GLOBAL2_PVT_DATA 0x0c
-#define GLOBAL2_SWITCH_MAC 0x0d
-#define GLOBAL2_ATU_STATS 0x0e
-#define GLOBAL2_PRIO_OVERRIDE 0x0f
-#define GLOBAL2_PRIO_OVERRIDE_FORCE_SNOOP BIT(7)
-#define GLOBAL2_PRIO_OVERRIDE_SNOOP_SHIFT 4
-#define GLOBAL2_PRIO_OVERRIDE_FORCE_ARP BIT(3)
-#define GLOBAL2_PRIO_OVERRIDE_ARP_SHIFT 0
-#define GLOBAL2_EEPROM_CMD 0x14
-#define GLOBAL2_EEPROM_CMD_BUSY BIT(15)
-#define GLOBAL2_EEPROM_CMD_OP_WRITE ((0x3 << 12) | GLOBAL2_EEPROM_CMD_BUSY)
-#define GLOBAL2_EEPROM_CMD_OP_READ ((0x4 << 12) | GLOBAL2_EEPROM_CMD_BUSY)
-#define GLOBAL2_EEPROM_CMD_OP_LOAD ((0x6 << 12) | GLOBAL2_EEPROM_CMD_BUSY)
-#define GLOBAL2_EEPROM_CMD_RUNNING BIT(11)
-#define GLOBAL2_EEPROM_CMD_WRITE_EN BIT(10)
-#define GLOBAL2_EEPROM_CMD_ADDR_MASK 0xff
-#define GLOBAL2_EEPROM_DATA 0x15
-#define GLOBAL2_PTP_AVB_OP 0x16
-#define GLOBAL2_PTP_AVB_DATA 0x17
-#define GLOBAL2_SMI_PHY_CMD 0x18
-#define GLOBAL2_SMI_PHY_CMD_BUSY BIT(15)
-#define GLOBAL2_SMI_PHY_CMD_MODE_22 BIT(12)
-#define GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA ((0x1 << 10) | \
- GLOBAL2_SMI_PHY_CMD_MODE_22 | \
- GLOBAL2_SMI_PHY_CMD_BUSY)
-#define GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA ((0x2 << 10) | \
- GLOBAL2_SMI_PHY_CMD_MODE_22 | \
- GLOBAL2_SMI_PHY_CMD_BUSY)
-#define GLOBAL2_SMI_PHY_DATA 0x19
-#define GLOBAL2_SCRATCH_MISC 0x1a
-#define GLOBAL2_SCRATCH_BUSY BIT(15)
-#define GLOBAL2_SCRATCH_REGISTER_SHIFT 8
-#define GLOBAL2_SCRATCH_VALUE_MASK 0xff
-#define GLOBAL2_WDOG_CONTROL 0x1b
-#define GLOBAL2_QOS_WEIGHT 0x1c
-#define GLOBAL2_MISC 0x1d
-
-#define MV88E6XXX_N_FID 4096
-
-enum mv88e6xxx_frame_mode {
- MV88E6XXX_FRAME_MODE_NORMAL,
- MV88E6XXX_FRAME_MODE_DSA,
- MV88E6XXX_FRAME_MODE_PROVIDER,
- MV88E6XXX_FRAME_MODE_ETHERTYPE,
-};
-
-/* List of supported models */
-enum mv88e6xxx_model {
- MV88E6085,
- MV88E6095,
- MV88E6097,
- MV88E6123,
- MV88E6131,
- MV88E6161,
- MV88E6165,
- MV88E6171,
- MV88E6172,
- MV88E6175,
- MV88E6176,
- MV88E6185,
- MV88E6190,
- MV88E6190X,
- MV88E6191,
- MV88E6240,
- MV88E6290,
- MV88E6320,
- MV88E6321,
- MV88E6350,
- MV88E6351,
- MV88E6352,
- MV88E6390,
- MV88E6390X,
-};
-
-enum mv88e6xxx_family {
- MV88E6XXX_FAMILY_NONE,
- MV88E6XXX_FAMILY_6065, /* 6031 6035 6061 6065 */
- MV88E6XXX_FAMILY_6095, /* 6092 6095 */
- MV88E6XXX_FAMILY_6097, /* 6046 6085 6096 6097 */
- MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */
- MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */
- MV88E6XXX_FAMILY_6320, /* 6320 6321 */
- MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */
- MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */
- MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */
-};
-
-enum mv88e6xxx_cap {
- /* Energy Efficient Ethernet.
- */
- MV88E6XXX_CAP_EEE,
-
- /* Multi-chip Addressing Mode.
- * Some chips respond to only 2 registers of its own SMI device address
- * when it is non-zero, and use indirect access to internal registers.
- */
- MV88E6XXX_CAP_SMI_CMD, /* (0x00) SMI Command */
- MV88E6XXX_CAP_SMI_DATA, /* (0x01) SMI Data */
-
- /* PHY Registers.
- */
- MV88E6XXX_CAP_PHY_PAGE, /* (0x16) Page Register */
-
- /* Fiber/SERDES Registers (SMI address F).
- */
- MV88E6XXX_CAP_SERDES,
-
- /* Switch Global (1) Registers.
- */
- MV88E6XXX_CAP_G1_ATU_FID, /* (0x01) ATU FID Register */
- MV88E6XXX_CAP_G1_VTU_FID, /* (0x02) VTU FID Register */
-
- /* Switch Global 2 Registers.
- * The device contains a second set of global 16-bit registers.
- */
- MV88E6XXX_CAP_GLOBAL2,
- MV88E6XXX_CAP_G2_INT, /* (0x00) Interrupt Status */
- MV88E6XXX_CAP_G2_MGMT_EN_2X, /* (0x02) MGMT Enable Register 2x */
- MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */
- MV88E6XXX_CAP_G2_IRL_CMD, /* (0x09) Ingress Rate Command */
- MV88E6XXX_CAP_G2_IRL_DATA, /* (0x0a) Ingress Rate Data */
- MV88E6XXX_CAP_G2_PVT_ADDR, /* (0x0b) Cross Chip Port VLAN Addr */
- MV88E6XXX_CAP_G2_PVT_DATA, /* (0x0c) Cross Chip Port VLAN Data */
- MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */
-
- /* Per VLAN Spanning Tree Unit (STU).
- * The Port State database, if present, is accessed through VTU
- * operations and dedicated SID registers. See GLOBAL_VTU_SID.
- */
- MV88E6XXX_CAP_STU,
-
- /* Internal temperature sensor.
- * Available from any enabled port's PHY register 26, page 6.
- */
- MV88E6XXX_CAP_TEMP,
- MV88E6XXX_CAP_TEMP_LIMIT,
-
- /* VLAN Table Unit.
- * The VTU is used to program 802.1Q VLANs. See GLOBAL_VTU_OP.
- */
- MV88E6XXX_CAP_VTU,
-};
-
-/* Bitmask of capabilities */
-#define MV88E6XXX_FLAG_EEE BIT_ULL(MV88E6XXX_CAP_EEE)
-
-#define MV88E6XXX_FLAG_SMI_CMD BIT_ULL(MV88E6XXX_CAP_SMI_CMD)
-#define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA)
-
-#define MV88E6XXX_FLAG_PHY_PAGE BIT_ULL(MV88E6XXX_CAP_PHY_PAGE)
-
-#define MV88E6XXX_FLAG_SERDES BIT_ULL(MV88E6XXX_CAP_SERDES)
-
-#define MV88E6XXX_FLAG_G1_ATU_FID BIT_ULL(MV88E6XXX_CAP_G1_ATU_FID)
-#define MV88E6XXX_FLAG_G1_VTU_FID BIT_ULL(MV88E6XXX_CAP_G1_VTU_FID)
-
-#define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2)
-#define MV88E6XXX_FLAG_G2_INT BIT_ULL(MV88E6XXX_CAP_G2_INT)
-#define MV88E6XXX_FLAG_G2_MGMT_EN_2X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_2X)
-#define MV88E6XXX_FLAG_G2_MGMT_EN_0X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_0X)
-#define MV88E6XXX_FLAG_G2_IRL_CMD BIT_ULL(MV88E6XXX_CAP_G2_IRL_CMD)
-#define MV88E6XXX_FLAG_G2_IRL_DATA BIT_ULL(MV88E6XXX_CAP_G2_IRL_DATA)
-#define MV88E6XXX_FLAG_G2_PVT_ADDR BIT_ULL(MV88E6XXX_CAP_G2_PVT_ADDR)
-#define MV88E6XXX_FLAG_G2_PVT_DATA BIT_ULL(MV88E6XXX_CAP_G2_PVT_DATA)
-#define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT)
-
-#define MV88E6XXX_FLAG_STU BIT_ULL(MV88E6XXX_CAP_STU)
-#define MV88E6XXX_FLAG_TEMP BIT_ULL(MV88E6XXX_CAP_TEMP)
-#define MV88E6XXX_FLAG_TEMP_LIMIT BIT_ULL(MV88E6XXX_CAP_TEMP_LIMIT)
-#define MV88E6XXX_FLAG_VTU BIT_ULL(MV88E6XXX_CAP_VTU)
-
-/* Ingress Rate Limit unit */
-#define MV88E6XXX_FLAGS_IRL \
- (MV88E6XXX_FLAG_G2_IRL_CMD | \
- MV88E6XXX_FLAG_G2_IRL_DATA)
-
-/* Multi-chip Addressing Mode */
-#define MV88E6XXX_FLAGS_MULTI_CHIP \
- (MV88E6XXX_FLAG_SMI_CMD | \
- MV88E6XXX_FLAG_SMI_DATA)
-
-/* Cross-chip Port VLAN Table */
-#define MV88E6XXX_FLAGS_PVT \
- (MV88E6XXX_FLAG_G2_PVT_ADDR | \
- MV88E6XXX_FLAG_G2_PVT_DATA)
-
-/* Fiber/SERDES Registers at SMI address F, page 1 */
-#define MV88E6XXX_FLAGS_SERDES \
- (MV88E6XXX_FLAG_PHY_PAGE | \
- MV88E6XXX_FLAG_SERDES)
-
-#define MV88E6XXX_FLAGS_FAMILY_6095 \
- (MV88E6XXX_FLAG_GLOBAL2 | \
- MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
- MV88E6XXX_FLAG_VTU | \
- MV88E6XXX_FLAGS_MULTI_CHIP)
-
-#define MV88E6XXX_FLAGS_FAMILY_6097 \
- (MV88E6XXX_FLAG_G1_ATU_FID | \
- MV88E6XXX_FLAG_G1_VTU_FID | \
- MV88E6XXX_FLAG_GLOBAL2 | \
- MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
- MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
- MV88E6XXX_FLAG_G2_POT | \
- MV88E6XXX_FLAG_STU | \
- MV88E6XXX_FLAG_VTU | \
- MV88E6XXX_FLAGS_IRL | \
- MV88E6XXX_FLAGS_MULTI_CHIP | \
- MV88E6XXX_FLAGS_PVT)
-
-#define MV88E6XXX_FLAGS_FAMILY_6165 \
- (MV88E6XXX_FLAG_G1_ATU_FID | \
- MV88E6XXX_FLAG_G1_VTU_FID | \
- MV88E6XXX_FLAG_GLOBAL2 | \
- MV88E6XXX_FLAG_G2_INT | \
- MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
- MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
- MV88E6XXX_FLAG_G2_POT | \
- MV88E6XXX_FLAG_STU | \
- MV88E6XXX_FLAG_TEMP | \
- MV88E6XXX_FLAG_VTU | \
- MV88E6XXX_FLAGS_IRL | \
- MV88E6XXX_FLAGS_MULTI_CHIP | \
- MV88E6XXX_FLAGS_PVT)
-
-#define MV88E6XXX_FLAGS_FAMILY_6185 \
- (MV88E6XXX_FLAG_GLOBAL2 | \
- MV88E6XXX_FLAG_G2_INT | \
- MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
- MV88E6XXX_FLAGS_MULTI_CHIP | \
- MV88E6XXX_FLAG_VTU)
-
-#define MV88E6XXX_FLAGS_FAMILY_6320 \
- (MV88E6XXX_FLAG_EEE | \
- MV88E6XXX_FLAG_GLOBAL2 | \
- MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
- MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
- MV88E6XXX_FLAG_G2_POT | \
- MV88E6XXX_FLAG_TEMP | \
- MV88E6XXX_FLAG_TEMP_LIMIT | \
- MV88E6XXX_FLAG_VTU | \
- MV88E6XXX_FLAGS_IRL | \
- MV88E6XXX_FLAGS_MULTI_CHIP | \
- MV88E6XXX_FLAGS_PVT)
-
-#define MV88E6XXX_FLAGS_FAMILY_6351 \
- (MV88E6XXX_FLAG_G1_ATU_FID | \
- MV88E6XXX_FLAG_G1_VTU_FID | \
- MV88E6XXX_FLAG_GLOBAL2 | \
- MV88E6XXX_FLAG_G2_INT | \
- MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
- MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
- MV88E6XXX_FLAG_G2_POT | \
- MV88E6XXX_FLAG_STU | \
- MV88E6XXX_FLAG_TEMP | \
- MV88E6XXX_FLAG_VTU | \
- MV88E6XXX_FLAGS_IRL | \
- MV88E6XXX_FLAGS_MULTI_CHIP | \
- MV88E6XXX_FLAGS_PVT)
-
-#define MV88E6XXX_FLAGS_FAMILY_6352 \
- (MV88E6XXX_FLAG_EEE | \
- MV88E6XXX_FLAG_G1_ATU_FID | \
- MV88E6XXX_FLAG_G1_VTU_FID | \
- MV88E6XXX_FLAG_GLOBAL2 | \
- MV88E6XXX_FLAG_G2_INT | \
- MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
- MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
- MV88E6XXX_FLAG_G2_POT | \
- MV88E6XXX_FLAG_STU | \
- MV88E6XXX_FLAG_TEMP | \
- MV88E6XXX_FLAG_TEMP_LIMIT | \
- MV88E6XXX_FLAG_VTU | \
- MV88E6XXX_FLAGS_IRL | \
- MV88E6XXX_FLAGS_MULTI_CHIP | \
- MV88E6XXX_FLAGS_PVT | \
- MV88E6XXX_FLAGS_SERDES)
-
-struct mv88e6xxx_ops;
-
-#define MV88E6XXX_FLAGS_FAMILY_6390 \
- (MV88E6XXX_FLAG_EEE | \
- MV88E6XXX_FLAG_GLOBAL2 | \
- MV88E6XXX_FLAG_STU | \
- MV88E6XXX_FLAG_TEMP | \
- MV88E6XXX_FLAG_TEMP_LIMIT | \
- MV88E6XXX_FLAG_VTU | \
- MV88E6XXX_FLAGS_IRL | \
- MV88E6XXX_FLAGS_MULTI_CHIP | \
- MV88E6XXX_FLAGS_PVT)
-
-struct mv88e6xxx_info {
- enum mv88e6xxx_family family;
- u16 prod_num;
- const char *name;
- unsigned int num_databases;
- unsigned int num_ports;
- unsigned int port_base_addr;
- unsigned int global1_addr;
- unsigned int age_time_coeff;
- unsigned int g1_irqs;
- enum dsa_tag_protocol tag_protocol;
- unsigned long long flags;
- const struct mv88e6xxx_ops *ops;
-};
-
-struct mv88e6xxx_atu_entry {
- u16 fid;
- u8 state;
- bool trunk;
- u16 portv_trunkid;
- u8 mac[ETH_ALEN];
-};
-
-struct mv88e6xxx_vtu_entry {
- u16 vid;
- u16 fid;
- u8 sid;
- bool valid;
- u8 data[DSA_MAX_PORTS];
-};
-
-struct mv88e6xxx_bus_ops;
-
-struct mv88e6xxx_priv_port {
- struct net_device *bridge_dev;
-};
-
-struct mv88e6xxx_irq {
- u16 masked;
- struct irq_chip chip;
- struct irq_domain *domain;
- unsigned int nirqs;
-};
-
-struct mv88e6xxx_chip {
- const struct mv88e6xxx_info *info;
-
- /* The dsa_switch this private structure is related to */
- struct dsa_switch *ds;
-
- /* The device this structure is associated to */
- struct device *dev;
-
- /* This mutex protects the access to the switch registers */
- struct mutex reg_lock;
-
- /* The MII bus and the address on the bus that is used to
- * communication with the switch
- */
- const struct mv88e6xxx_bus_ops *smi_ops;
- struct mii_bus *bus;
- int sw_addr;
-
- /* Handles automatic disabling and re-enabling of the PHY
- * polling unit.
- */
- const struct mv88e6xxx_bus_ops *phy_ops;
- struct mutex ppu_mutex;
- int ppu_disabled;
- struct work_struct ppu_work;
- struct timer_list ppu_timer;
-
- /* This mutex serialises access to the statistics unit.
- * Hold this mutex over snapshot + dump sequences.
- */
- struct mutex stats_mutex;
-
- struct mv88e6xxx_priv_port ports[DSA_MAX_PORTS];
-
- /* A switch may have a GPIO line tied to its reset pin. Parse
- * this from the device tree, and use it before performing
- * switch soft reset.
- */
- struct gpio_desc *reset;
-
- /* set to size of eeprom if supported by the switch */
- int eeprom_len;
-
- /* Device node for the MDIO bus */
- struct device_node *mdio_np;
-
- /* And the MDIO bus itself */
- struct mii_bus *mdio_bus;
-
- /* There can be two interrupt controllers, which are chained
- * off a GPIO as interrupt source
- */
- struct mv88e6xxx_irq g1_irq;
- struct mv88e6xxx_irq g2_irq;
- int irq;
- int device_irq;
-};
-
-struct mv88e6xxx_bus_ops {
- int (*read)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val);
- int (*write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val);
-};
-
-struct mv88e6xxx_ops {
- int (*get_eeprom)(struct mv88e6xxx_chip *chip,
- struct ethtool_eeprom *eeprom, u8 *data);
- int (*set_eeprom)(struct mv88e6xxx_chip *chip,
- struct ethtool_eeprom *eeprom, u8 *data);
-
- int (*set_switch_mac)(struct mv88e6xxx_chip *chip, u8 *addr);
-
- int (*phy_read)(struct mv88e6xxx_chip *chip, int addr, int reg,
- u16 *val);
- int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg,
- u16 val);
-
- /* PHY Polling Unit (PPU) operations */
- int (*ppu_enable)(struct mv88e6xxx_chip *chip);
- int (*ppu_disable)(struct mv88e6xxx_chip *chip);
-
- /* Switch Software Reset */
- int (*reset)(struct mv88e6xxx_chip *chip);
-
- /* RGMII Receive/Transmit Timing Control
- * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise.
- */
- int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port,
- phy_interface_t mode);
-
-#define LINK_FORCED_DOWN 0
-#define LINK_FORCED_UP 1
-#define LINK_UNFORCED -2
-
- /* Port's MAC link state
- * Use LINK_FORCED_UP or LINK_FORCED_DOWN to force link up or down,
- * or LINK_UNFORCED for normal link detection.
- */
- int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
-
-#define DUPLEX_UNFORCED -2
-
- /* Port's MAC duplex mode
- *
- * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex,
- * or DUPLEX_UNFORCED for normal duplex detection.
- */
- int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);
-
-#define SPEED_MAX INT_MAX
-#define SPEED_UNFORCED -2
-
- /* Port's MAC speed (in Mbps)
- *
- * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid.
- * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value.
- */
- int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed);
-
- int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port);
-
- int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port,
- enum mv88e6xxx_frame_mode mode);
- int (*port_set_egress_unknowns)(struct mv88e6xxx_chip *chip, int port,
- bool on);
- int (*port_set_ether_type)(struct mv88e6xxx_chip *chip, int port,
- u16 etype);
- int (*port_jumbo_config)(struct mv88e6xxx_chip *chip, int port);
-
- int (*port_egress_rate_limiting)(struct mv88e6xxx_chip *chip, int port);
- int (*port_pause_config)(struct mv88e6xxx_chip *chip, int port);
-
- /* Snapshot the statistics for a port. The statistics can then
- * be read back a leisure but still with a consistent view.
- */
- int (*stats_snapshot)(struct mv88e6xxx_chip *chip, int port);
-
- /* Set the histogram mode for statistics, when the control registers
- * are separated out of the STATS_OP register.
- */
- int (*stats_set_histogram)(struct mv88e6xxx_chip *chip);
-
- /* Return the number of strings describing statistics */
- int (*stats_get_sset_count)(struct mv88e6xxx_chip *chip);
- void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data);
- void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port,
- uint64_t *data);
- int (*g1_set_cpu_port)(struct mv88e6xxx_chip *chip, int port);
- int (*g1_set_egress_port)(struct mv88e6xxx_chip *chip, int port);
-
- /* Can be either in g1 or g2, so don't use a prefix */
- int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip);
-};
-
-#define STATS_TYPE_PORT BIT(0)
-#define STATS_TYPE_BANK0 BIT(1)
-#define STATS_TYPE_BANK1 BIT(2)
-
-struct mv88e6xxx_hw_stat {
- char string[ETH_GSTRING_LEN];
- int sizeof_stat;
- int reg;
- int type;
-};
-
-static inline bool mv88e6xxx_has(struct mv88e6xxx_chip *chip,
- unsigned long flags)
-{
- return (chip->info->flags & flags) == flags;
-}
-
-static inline unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip)
-{
- return chip->info->num_databases;
-}
-
-static inline unsigned int mv88e6xxx_num_ports(struct mv88e6xxx_chip *chip)
-{
- return chip->info->num_ports;
-}
-
-int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val);
-int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val);
-int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
- u16 update);
-int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask);
-
-#endif
diff --git a/drivers/net/dsa/mv88e6xxx/phy.c b/drivers/net/dsa/mv88e6xxx/phy.c
new file mode 100644
index 000000000000..3500ac0ea848
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/phy.c
@@ -0,0 +1,249 @@
+/*
+ * Marvell 88e6xxx Ethernet switch PHY and PPU support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2017 Andrew Lunn <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <net/dsa.h>
+
+#include "chip.h"
+#include "phy.h"
+
+int mv88e6165_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 *val)
+{
+ return mv88e6xxx_read(chip, addr, reg, val);
+}
+
+int mv88e6165_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 val)
+{
+ return mv88e6xxx_write(chip, addr, reg, val);
+}
+
+int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, int reg, u16 *val)
+{
+ int addr = phy; /* PHY devices addresses start at 0x0 */
+ struct mii_bus *bus;
+
+ bus = mv88e6xxx_default_mdio_bus(chip);
+ if (!bus)
+ return -EOPNOTSUPP;
+
+ if (!chip->info->ops->phy_read)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->phy_read(chip, bus, addr, reg, val);
+}
+
+int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, int reg, u16 val)
+{
+ int addr = phy; /* PHY devices addresses start at 0x0 */
+ struct mii_bus *bus;
+
+ bus = mv88e6xxx_default_mdio_bus(chip);
+ if (!bus)
+ return -EOPNOTSUPP;
+
+ if (!chip->info->ops->phy_write)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->phy_write(chip, bus, addr, reg, val);
+}
+
+static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
+{
+ return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
+}
+
+static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
+{
+ int err;
+
+ /* Restore PHY page Copper 0x0 for access via the registered
+ * MDIO bus
+ */
+ err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE,
+ MV88E6XXX_PHY_PAGE_COPPER);
+ if (unlikely(err)) {
+ dev_err(chip->dev,
+ "failed to restore PHY %d page Copper (%d)\n",
+ phy, err);
+ }
+}
+
+int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
+ u8 page, int reg, u16 *val)
+{
+ int err;
+
+ /* There is no paging for registers 22 */
+ if (reg == MV88E6XXX_PHY_PAGE)
+ return -EINVAL;
+
+ err = mv88e6xxx_phy_page_get(chip, phy, page);
+ if (!err) {
+ err = mv88e6xxx_phy_read(chip, phy, reg, val);
+ mv88e6xxx_phy_page_put(chip, phy);
+ }
+
+ return err;
+}
+
+int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
+ u8 page, int reg, u16 val)
+{
+ int err;
+
+ /* There is no paging for registers 22 */
+ if (reg == MV88E6XXX_PHY_PAGE)
+ return -EINVAL;
+
+ err = mv88e6xxx_phy_page_get(chip, phy, page);
+ if (!err) {
+ err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
+ mv88e6xxx_phy_page_put(chip, phy);
+ }
+
+ return err;
+}
+
+static int mv88e6xxx_phy_ppu_disable(struct mv88e6xxx_chip *chip)
+{
+ if (!chip->info->ops->ppu_disable)
+ return 0;
+
+ return chip->info->ops->ppu_disable(chip);
+}
+
+static int mv88e6xxx_phy_ppu_enable(struct mv88e6xxx_chip *chip)
+{
+ if (!chip->info->ops->ppu_enable)
+ return 0;
+
+ return chip->info->ops->ppu_enable(chip);
+}
+
+static void mv88e6xxx_phy_ppu_reenable_work(struct work_struct *ugly)
+{
+ struct mv88e6xxx_chip *chip;
+
+ chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
+
+ mutex_lock(&chip->reg_lock);
+
+ if (mutex_trylock(&chip->ppu_mutex)) {
+ if (mv88e6xxx_phy_ppu_enable(chip) == 0)
+ chip->ppu_disabled = 0;
+ mutex_unlock(&chip->ppu_mutex);
+ }
+
+ mutex_unlock(&chip->reg_lock);
+}
+
+static void mv88e6xxx_phy_ppu_reenable_timer(unsigned long _ps)
+{
+ struct mv88e6xxx_chip *chip = (void *)_ps;
+
+ schedule_work(&chip->ppu_work);
+}
+
+static int mv88e6xxx_phy_ppu_access_get(struct mv88e6xxx_chip *chip)
+{
+ int ret;
+
+ mutex_lock(&chip->ppu_mutex);
+
+ /* If the PHY polling unit is enabled, disable it so that
+ * we can access the PHY registers. If it was already
+ * disabled, cancel the timer that is going to re-enable
+ * it.
+ */
+ if (!chip->ppu_disabled) {
+ ret = mv88e6xxx_phy_ppu_disable(chip);
+ if (ret < 0) {
+ mutex_unlock(&chip->ppu_mutex);
+ return ret;
+ }
+ chip->ppu_disabled = 1;
+ } else {
+ del_timer(&chip->ppu_timer);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void mv88e6xxx_phy_ppu_access_put(struct mv88e6xxx_chip *chip)
+{
+ /* Schedule a timer to re-enable the PHY polling unit. */
+ mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
+ mutex_unlock(&chip->ppu_mutex);
+}
+
+static void mv88e6xxx_phy_ppu_state_init(struct mv88e6xxx_chip *chip)
+{
+ mutex_init(&chip->ppu_mutex);
+ INIT_WORK(&chip->ppu_work, mv88e6xxx_phy_ppu_reenable_work);
+ setup_timer(&chip->ppu_timer, mv88e6xxx_phy_ppu_reenable_timer,
+ (unsigned long)chip);
+}
+
+static void mv88e6xxx_phy_ppu_state_destroy(struct mv88e6xxx_chip *chip)
+{
+ del_timer_sync(&chip->ppu_timer);
+}
+
+int mv88e6185_phy_ppu_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 *val)
+{
+ int err;
+
+ err = mv88e6xxx_phy_ppu_access_get(chip);
+ if (!err) {
+ err = mv88e6xxx_read(chip, addr, reg, val);
+ mv88e6xxx_phy_ppu_access_put(chip);
+ }
+
+ return err;
+}
+
+int mv88e6185_phy_ppu_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 val)
+{
+ int err;
+
+ err = mv88e6xxx_phy_ppu_access_get(chip);
+ if (!err) {
+ err = mv88e6xxx_write(chip, addr, reg, val);
+ mv88e6xxx_phy_ppu_access_put(chip);
+ }
+
+ return err;
+}
+
+void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
+{
+ if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
+ mv88e6xxx_phy_ppu_state_init(chip);
+}
+
+void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
+{
+ if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
+ mv88e6xxx_phy_ppu_state_destroy(chip);
+}
+
+int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip)
+{
+ return mv88e6xxx_phy_ppu_enable(chip);
+}
diff --git a/drivers/net/dsa/mv88e6xxx/phy.h b/drivers/net/dsa/mv88e6xxx/phy.h
new file mode 100644
index 000000000000..556b74a0502a
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/phy.h
@@ -0,0 +1,43 @@
+/*
+ * Marvell 88E6xxx PHY access
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2017 Andrew Lunn <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _MV88E6XXX_PHY_H
+#define _MV88E6XXX_PHY_H
+
+#define MV88E6XXX_PHY_PAGE 0x16
+#define MV88E6XXX_PHY_PAGE_COPPER 0x00
+
+/* PHY Registers accesses implementations */
+int mv88e6165_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 *val);
+int mv88e6165_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 val);
+int mv88e6185_phy_ppu_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 *val);
+int mv88e6185_phy_ppu_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
+ int addr, int reg, u16 val);
+
+/* Generic PHY operations */
+int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
+ int reg, u16 *val);
+int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
+ int reg, u16 val);
+int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
+ u8 page, int reg, u16 *val);
+int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
+ u8 page, int reg, u16 val);
+void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip);
+void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip);
+int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip);
+
+#endif /*_MV88E6XXX_PHY_H */
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 0db7fa0373ae..a7801f6668a5 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -3,7 +3,8 @@
*
* Copyright (c) 2008 Marvell Semiconductor
*
- * Copyright (c) 2016 Vivien Didelot <[email protected]>
+ * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
+ * Vivien Didelot <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -11,7 +12,11 @@
* (at your option) any later version.
*/
-#include "mv88e6xxx.h"
+#include <linux/bitfield.h>
+#include <linux/if_bridge.h>
+#include <linux/phy.h>
+
+#include "chip.h"
#include "port.h"
int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
@@ -45,23 +50,23 @@ static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
if (err)
return err;
- reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
- PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
+ reg &= ~(MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
+ MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK);
switch (mode) {
case PHY_INTERFACE_MODE_RGMII_RXID:
- reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
+ reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
- reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
+ reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
break;
case PHY_INTERFACE_MODE_RGMII_ID:
- reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
- PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
+ reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
+ MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
break;
case PHY_INTERFACE_MODE_RGMII:
break;
@@ -69,13 +74,13 @@ static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
return 0;
}
- err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
if (err)
return err;
- netdev_dbg(chip->ds->ports[port].netdev, "delay RXCLK %s, TXCLK %s\n",
- reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no",
- reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no");
+ dev_dbg(chip->dev, "p%d: delay RXCLK %s, TXCLK %s\n", port,
+ reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK ? "yes" : "no",
+ reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK ? "yes" : "no");
return 0;
}
@@ -103,18 +108,20 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
if (err)
return err;
- reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP);
+ reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
+ MV88E6XXX_PORT_MAC_CTL_LINK_UP);
switch (link) {
case LINK_FORCED_DOWN:
- reg |= PORT_PCS_CTRL_FORCE_LINK;
+ reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK;
break;
case LINK_FORCED_UP:
- reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP;
+ reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
+ MV88E6XXX_PORT_MAC_CTL_LINK_UP;
break;
case LINK_UNFORCED:
/* normal link detection */
@@ -123,13 +130,13 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
return -EINVAL;
}
- err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
if (err)
return err;
- netdev_dbg(chip->ds->ports[port].netdev, "%s link %s\n",
- reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce",
- reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down");
+ dev_dbg(chip->dev, "p%d: %s link %s\n", port,
+ reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK ? "Force" : "Unforce",
+ reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP ? "up" : "down");
return 0;
}
@@ -139,18 +146,20 @@ int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
if (err)
return err;
- reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL);
+ reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
+ MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL);
switch (dup) {
case DUPLEX_HALF:
- reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
+ reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
break;
case DUPLEX_FULL:
- reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL;
+ reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
+ MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
break;
case DUPLEX_UNFORCED:
/* normal duplex detection */
@@ -159,13 +168,13 @@ int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
return -EINVAL;
}
- err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
if (err)
return err;
- netdev_dbg(chip->ds->ports[port].netdev, "%s %s duplex\n",
- reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce",
- reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half");
+ dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
+ reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
+ reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
return 0;
}
@@ -178,55 +187,56 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
switch (speed) {
case 10:
- ctrl = PORT_PCS_CTRL_SPEED_10;
+ ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
break;
case 100:
- ctrl = PORT_PCS_CTRL_SPEED_100;
+ ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
break;
case 200:
if (alt_bit)
- ctrl = PORT_PCS_CTRL_SPEED_100 | PORT_PCS_CTRL_ALTSPEED;
+ ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
+ MV88E6390_PORT_MAC_CTL_ALTSPEED;
else
- ctrl = PORT_PCS_CTRL_SPEED_200;
+ ctrl = MV88E6065_PORT_MAC_CTL_SPEED_200;
break;
case 1000:
- ctrl = PORT_PCS_CTRL_SPEED_1000;
+ ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
break;
case 2500:
- ctrl = PORT_PCS_CTRL_SPEED_1000 | PORT_PCS_CTRL_ALTSPEED;
+ ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
+ MV88E6390_PORT_MAC_CTL_ALTSPEED;
break;
case 10000:
/* all bits set, fall through... */
case SPEED_UNFORCED:
- ctrl = PORT_PCS_CTRL_SPEED_UNFORCED;
+ ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
break;
default:
return -EOPNOTSUPP;
}
- err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
if (err)
return err;
- reg &= ~PORT_PCS_CTRL_SPEED_MASK;
+ reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK;
if (alt_bit)
- reg &= ~PORT_PCS_CTRL_ALTSPEED;
+ reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED;
if (force_bit) {
- reg &= ~PORT_PCS_CTRL_FORCE_SPEED;
+ reg &= ~MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
if (speed != SPEED_UNFORCED)
- ctrl |= PORT_PCS_CTRL_FORCE_SPEED;
+ ctrl |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
}
reg |= ctrl;
- err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
if (err)
return err;
if (speed)
- netdev_dbg(chip->ds->ports[port].netdev,
- "Speed set to %d Mbps\n", speed);
+ dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
else
- netdev_dbg(chip->ds->ports[port].netdev, "Speed unforced\n");
+ dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
return 0;
}
@@ -304,37 +314,105 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
}
-/* Offset 0x02: Pause Control
+int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode)
+{
+ u16 reg;
+ u16 cmode;
+ int err;
+
+ if (mode == PHY_INTERFACE_MODE_NA)
+ return 0;
+
+ if (port != 9 && port != 10)
+ return -EOPNOTSUPP;
+
+ switch (mode) {
+ case PHY_INTERFACE_MODE_1000BASEX:
+ cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X;
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ cmode = MV88E6XXX_PORT_STS_CMODE_SGMII;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX;
+ break;
+ case PHY_INTERFACE_MODE_XGMII:
+ cmode = MV88E6XXX_PORT_STS_CMODE_XAUI;
+ break;
+ case PHY_INTERFACE_MODE_RXAUI:
+ cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI;
+ break;
+ default:
+ cmode = 0;
+ }
+
+ if (cmode) {
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
+ if (err)
+ return err;
+
+ reg &= ~MV88E6XXX_PORT_STS_CMODE_MASK;
+ reg |= cmode;
+
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
+{
+ int err;
+ u16 reg;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
+ if (err)
+ return err;
+
+ *cmode = reg & MV88E6XXX_PORT_STS_CMODE_MASK;
+
+ return 0;
+}
+
+/* Offset 0x02: Jamming Control
*
* Do not limit the period of time that this port can be paused for by
* the remote end or the period of time that this port can pause the
* remote end.
*/
-int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port)
+int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
+ u8 out)
{
- return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000);
+ return mv88e6xxx_port_write(chip, port, MV88E6097_PORT_JAM_CTL,
+ out << 8 | in);
}
-int mv88e6390_port_pause_config(struct mv88e6xxx_chip *chip, int port)
+int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
+ u8 out)
{
int err;
- err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL,
- PORT_FLOW_CTRL_LIMIT_IN | 0);
+ err = mv88e6xxx_port_write(chip, port, MV88E6390_PORT_FLOW_CTL,
+ MV88E6390_PORT_FLOW_CTL_UPDATE |
+ MV88E6390_PORT_FLOW_CTL_LIMIT_IN | in);
if (err)
return err;
- return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL,
- PORT_FLOW_CTRL_LIMIT_OUT | 0);
+ return mv88e6xxx_port_write(chip, port, MV88E6390_PORT_FLOW_CTL,
+ MV88E6390_PORT_FLOW_CTL_UPDATE |
+ MV88E6390_PORT_FLOW_CTL_LIMIT_OUT | out);
}
/* Offset 0x04: Port Control Register */
static const char * const mv88e6xxx_port_state_names[] = {
- [PORT_CONTROL_STATE_DISABLED] = "Disabled",
- [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
- [PORT_CONTROL_STATE_LEARNING] = "Learning",
- [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
+ [MV88E6XXX_PORT_CTL0_STATE_DISABLED] = "Disabled",
+ [MV88E6XXX_PORT_CTL0_STATE_BLOCKING] = "Blocking/Listening",
+ [MV88E6XXX_PORT_CTL0_STATE_LEARNING] = "Learning",
+ [MV88E6XXX_PORT_CTL0_STATE_FORWARDING] = "Forwarding",
};
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
@@ -342,37 +420,72 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
if (err)
return err;
- reg &= ~PORT_CONTROL_STATE_MASK;
+ reg &= ~MV88E6XXX_PORT_CTL0_STATE_MASK;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ state = MV88E6XXX_PORT_CTL0_STATE_DISABLED;
+ break;
+ case BR_STATE_BLOCKING:
+ case BR_STATE_LISTENING:
+ state = MV88E6XXX_PORT_CTL0_STATE_BLOCKING;
+ break;
+ case BR_STATE_LEARNING:
+ state = MV88E6XXX_PORT_CTL0_STATE_LEARNING;
+ break;
+ case BR_STATE_FORWARDING:
+ state = MV88E6XXX_PORT_CTL0_STATE_FORWARDING;
+ break;
+ default:
+ return -EINVAL;
+ }
+
reg |= state;
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
if (err)
return err;
- netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n",
- mv88e6xxx_port_state_names[state]);
+ dev_dbg(chip->dev, "p%d: PortState set to %s\n", port,
+ mv88e6xxx_port_state_names[state]);
return 0;
}
int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port,
- u16 mode)
+ enum mv88e6xxx_egress_mode mode)
{
int err;
u16 reg;
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
if (err)
return err;
- reg &= ~PORT_CONTROL_EGRESS_MASK;
- reg |= mode;
+ reg &= ~MV88E6XXX_PORT_CTL0_EGRESS_MODE_MASK;
+
+ switch (mode) {
+ case MV88E6XXX_EGRESS_MODE_UNMODIFIED:
+ reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNMODIFIED;
+ break;
+ case MV88E6XXX_EGRESS_MODE_UNTAGGED:
+ reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNTAGGED;
+ break;
+ case MV88E6XXX_EGRESS_MODE_TAGGED:
+ reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_TAGGED;
+ break;
+ case MV88E6XXX_EGRESS_MODE_ETHERTYPE:
+ reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_ETHER_TYPE_DSA;
+ break;
+ default:
+ return -EINVAL;
+ }
- return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
}
int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
@@ -381,24 +494,24 @@ int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
int err;
u16 reg;
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
if (err)
return err;
- reg &= ~PORT_CONTROL_FRAME_MODE_DSA;
+ reg &= ~MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK;
switch (mode) {
case MV88E6XXX_FRAME_MODE_NORMAL:
- reg |= PORT_CONTROL_FRAME_MODE_NORMAL;
+ reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL;
break;
case MV88E6XXX_FRAME_MODE_DSA:
- reg |= PORT_CONTROL_FRAME_MODE_DSA;
+ reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA;
break;
default:
return -EINVAL;
}
- return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
}
int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
@@ -407,91 +520,114 @@ int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
int err;
u16 reg;
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
if (err)
return err;
- reg &= ~PORT_CONTROL_FRAME_MASK;
+ reg &= ~MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK;
switch (mode) {
case MV88E6XXX_FRAME_MODE_NORMAL:
- reg |= PORT_CONTROL_FRAME_MODE_NORMAL;
+ reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL;
break;
case MV88E6XXX_FRAME_MODE_DSA:
- reg |= PORT_CONTROL_FRAME_MODE_DSA;
+ reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA;
break;
case MV88E6XXX_FRAME_MODE_PROVIDER:
- reg |= PORT_CONTROL_FRAME_MODE_PROVIDER;
+ reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_PROVIDER;
break;
case MV88E6XXX_FRAME_MODE_ETHERTYPE:
- reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
+ reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_ETHER_TYPE_DSA;
break;
default:
return -EINVAL;
}
- return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
}
-int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port,
- bool on)
+static int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip,
+ int port, bool unicast)
{
int err;
u16 reg;
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
if (err)
return err;
- if (on)
- reg |= PORT_CONTROL_FORWARD_UNKNOWN;
+ if (unicast)
+ reg |= MV88E6185_PORT_CTL0_FORWARD_UNKNOWN;
else
- reg &= ~PORT_CONTROL_FORWARD_UNKNOWN;
+ reg &= ~MV88E6185_PORT_CTL0_FORWARD_UNKNOWN;
- return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
}
-int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port,
- bool on)
+int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
+ bool unicast, bool multicast)
{
int err;
u16 reg;
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
if (err)
return err;
- if (on)
- reg |= PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA;
+ reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_MASK;
+
+ if (unicast && multicast)
+ reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_ALL_UNKNOWN_DA;
+ else if (unicast)
+ reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_MC_DA;
+ else if (multicast)
+ reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_UC_DA;
else
- reg &= ~PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA;
+ reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_DA;
- return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
}
/* Offset 0x05: Port Control 1 */
+int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
+ bool message_port)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val);
+ if (err)
+ return err;
+
+ if (message_port)
+ val |= MV88E6XXX_PORT_CTL1_MESSAGE_PORT;
+ else
+ val &= ~MV88E6XXX_PORT_CTL1_MESSAGE_PORT;
+
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
+}
+
/* Offset 0x06: Port Based VLAN Map */
int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
{
- const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
+ const u16 mask = mv88e6xxx_port_mask(chip);
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
if (err)
return err;
reg &= ~mask;
reg |= map & mask;
- err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN, reg);
if (err)
return err;
- netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n",
- map);
+ dev_dbg(chip->dev, "p%d: VLANTable set to %.3x\n", port, map);
return 0;
}
@@ -503,7 +639,7 @@ int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
int err;
/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
- err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
if (err)
return err;
@@ -511,7 +647,8 @@ int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
/* Port's default FID upper bits are located in reg 0x05, offset 0 */
if (upper_mask) {
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1,
+ &reg);
if (err)
return err;
@@ -531,32 +668,34 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
return -EINVAL;
/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
- err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
if (err)
return err;
reg &= 0x0fff;
reg |= (fid & 0x000f) << 12;
- err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN, reg);
if (err)
return err;
/* Port's default FID upper bits are located in reg 0x05, offset 0 */
if (upper_mask) {
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1,
+ &reg);
if (err)
return err;
reg &= ~upper_mask;
reg |= (fid >> 4) & upper_mask;
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1,
+ reg);
if (err)
return err;
}
- netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid);
+ dev_dbg(chip->dev, "p%d: FID set to %u\n", port, fid);
return 0;
}
@@ -568,11 +707,12 @@ int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
+ &reg);
if (err)
return err;
- *pvid = reg & PORT_DEFAULT_VLAN_MASK;
+ *pvid = reg & MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
return 0;
}
@@ -582,19 +722,20 @@ int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
+ &reg);
if (err)
return err;
- reg &= ~PORT_DEFAULT_VLAN_MASK;
- reg |= pvid & PORT_DEFAULT_VLAN_MASK;
+ reg &= ~MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
+ reg |= pvid & MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
- err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
+ reg);
if (err)
return err;
- netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n",
- pvid);
+ dev_dbg(chip->dev, "p%d: DefaultVID set to %u\n", port, pvid);
return 0;
}
@@ -602,59 +743,145 @@ int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
/* Offset 0x08: Port Control 2 Register */
static const char * const mv88e6xxx_port_8021q_mode_names[] = {
- [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
- [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
- [PORT_CONTROL_2_8021Q_CHECK] = "Check",
- [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED] = "Disabled",
+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_FALLBACK] = "Fallback",
+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_CHECK] = "Check",
+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure",
};
+static int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip,
+ int port, bool multicast)
+{
+ int err;
+ u16 reg;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
+ if (err)
+ return err;
+
+ if (multicast)
+ reg |= MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD;
+ else
+ reg &= ~MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD;
+
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
+}
+
+int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
+ bool unicast, bool multicast)
+{
+ int err;
+
+ err = mv88e6185_port_set_forward_unknown(chip, port, unicast);
+ if (err)
+ return err;
+
+ return mv88e6185_port_set_default_forward(chip, port, multicast);
+}
+
+int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
+ int upstream_port)
+{
+ int err;
+ u16 reg;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
+ if (err)
+ return err;
+
+ reg &= ~MV88E6095_PORT_CTL2_CPU_PORT_MASK;
+ reg |= upstream_port;
+
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
+}
+
int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
u16 mode)
{
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
if (err)
return err;
- reg &= ~PORT_CONTROL_2_8021Q_MASK;
- reg |= mode & PORT_CONTROL_2_8021Q_MASK;
+ reg &= ~MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
+ reg |= mode & MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
if (err)
return err;
- netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n",
- mv88e6xxx_port_8021q_mode_names[mode]);
+ dev_dbg(chip->dev, "p%d: 802.1QMode set to %s\n", port,
+ mv88e6xxx_port_8021q_mode_names[mode]);
return 0;
}
-int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port)
+int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)
{
u16 reg;
int err;
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
if (err)
return err;
- reg |= PORT_CONTROL_2_JUMBO_10240;
+ reg |= MV88E6XXX_PORT_CTL2_MAP_DA;
- return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
+}
+
+int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
+ size_t size)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
+ if (err)
+ return err;
+
+ reg &= ~MV88E6XXX_PORT_CTL2_JUMBO_MODE_MASK;
+
+ if (size <= 1522)
+ reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_1522;
+ else if (size <= 2048)
+ reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_2048;
+ else if (size <= 10240)
+ reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_10240;
+ else
+ return -ERANGE;
+
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
}
/* Offset 0x09: Port Rate Control */
int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
{
- return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0000);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL1,
+ 0x0000);
}
int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
{
- return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0001);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL1,
+ 0x0001);
+}
+
+/* Offset 0x0C: Port ATU Control */
+
+int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port)
+{
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ATU_CTL, 0);
+}
+
+/* Offset 0x0D: (Priority) Override Register */
+
+int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port)
+{
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0);
}
/* Offset 0x0f: Port Ether type */
@@ -662,7 +889,7 @@ int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
u16 etype)
{
- return mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, etype);
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ETH_TYPE, etype);
}
/* Offset 0x18: Port IEEE Priority Remapping Registers [0-3]
@@ -674,53 +901,54 @@ int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
int err;
/* Use a direct priority mapping for all IEEE tagged frames */
- err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, 0x3210);
+ err = mv88e6xxx_port_write(chip, port,
+ MV88E6095_PORT_IEEE_PRIO_REMAP_0123,
+ 0x3210);
if (err)
return err;
- return mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, 0x7654);
+ return mv88e6xxx_port_write(chip, port,
+ MV88E6095_PORT_IEEE_PRIO_REMAP_4567,
+ 0x7654);
}
static int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip,
- int port, u16 table,
- u8 pointer, u16 data)
+ int port, u16 table, u8 ptr, u16 data)
{
u16 reg;
- reg = PORT_IEEE_PRIO_MAP_TABLE_UPDATE |
- table |
- (pointer << PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT) |
- data;
+ reg = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE | table |
+ (ptr << __bf_shf(MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_PTR_MASK)) |
+ (data & MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_DATA_MASK);
- return mv88e6xxx_port_write(chip, port, PORT_IEEE_PRIO_MAP_TABLE, reg);
+ return mv88e6xxx_port_write(chip, port,
+ MV88E6390_PORT_IEEE_PRIO_MAP_TABLE, reg);
}
int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
{
int err, i;
+ u16 table;
for (i = 0; i <= 7; i++) {
- err = mv88e6xxx_port_ieeepmt_write(
- chip, port, PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP,
- i, (i | i << 4));
+ table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP;
+ err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i,
+ (i | i << 4));
if (err)
return err;
- err = mv88e6xxx_port_ieeepmt_write(
- chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP,
- i, i);
+ table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP;
+ err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
if (err)
return err;
- err = mv88e6xxx_port_ieeepmt_write(
- chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP,
- i, i);
+ table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP;
+ err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
if (err)
return err;
- err = mv88e6xxx_port_ieeepmt_write(
- chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP,
- i, i);
+ table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP;
+ err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
if (err)
return err;
}
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 7b3bacaacbfe..8f3991bf1851 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -3,7 +3,8 @@
*
* Copyright (c) 2008 Marvell Semiconductor
*
- * Copyright (c) 2016 Vivien Didelot <[email protected]>
+ * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
+ * Vivien Didelot <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +15,229 @@
#ifndef _MV88E6XXX_PORT_H
#define _MV88E6XXX_PORT_H
-#include "mv88e6xxx.h"
+#include "chip.h"
+
+/* Offset 0x00: Port Status Register */
+#define MV88E6XXX_PORT_STS 0x00
+#define MV88E6XXX_PORT_STS_PAUSE_EN 0x8000
+#define MV88E6XXX_PORT_STS_MY_PAUSE 0x4000
+#define MV88E6XXX_PORT_STS_HD_FLOW 0x2000
+#define MV88E6XXX_PORT_STS_PHY_DETECT 0x1000
+#define MV88E6XXX_PORT_STS_LINK 0x0800
+#define MV88E6XXX_PORT_STS_DUPLEX 0x0400
+#define MV88E6XXX_PORT_STS_SPEED_MASK 0x0300
+#define MV88E6XXX_PORT_STS_SPEED_10 0x0000
+#define MV88E6XXX_PORT_STS_SPEED_100 0x0100
+#define MV88E6XXX_PORT_STS_SPEED_1000 0x0200
+#define MV88E6352_PORT_STS_EEE 0x0040
+#define MV88E6165_PORT_STS_AM_DIS 0x0040
+#define MV88E6185_PORT_STS_MGMII 0x0040
+#define MV88E6XXX_PORT_STS_TX_PAUSED 0x0020
+#define MV88E6XXX_PORT_STS_FLOW_CTL 0x0010
+#define MV88E6XXX_PORT_STS_CMODE_MASK 0x000f
+#define MV88E6XXX_PORT_STS_CMODE_100BASE_X 0x0008
+#define MV88E6XXX_PORT_STS_CMODE_1000BASE_X 0x0009
+#define MV88E6XXX_PORT_STS_CMODE_SGMII 0x000a
+#define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b
+#define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c
+#define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d
+
+/* Offset 0x01: MAC (or PCS or Physical) Control Register */
+#define MV88E6XXX_PORT_MAC_CTL 0x01
+#define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK 0x8000
+#define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK 0x4000
+#define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000
+#define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000
+#define MV88E6352_PORT_MAC_CTL_200BASE 0x1000
+#define MV88E6XXX_PORT_MAC_CTL_FC 0x0080
+#define MV88E6XXX_PORT_MAC_CTL_FORCE_FC 0x0040
+#define MV88E6XXX_PORT_MAC_CTL_LINK_UP 0x0020
+#define MV88E6XXX_PORT_MAC_CTL_FORCE_LINK 0x0010
+#define MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL 0x0008
+#define MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX 0x0004
+#define MV88E6XXX_PORT_MAC_CTL_SPEED_MASK 0x0003
+#define MV88E6XXX_PORT_MAC_CTL_SPEED_10 0x0000
+#define MV88E6XXX_PORT_MAC_CTL_SPEED_100 0x0001
+#define MV88E6065_PORT_MAC_CTL_SPEED_200 0x0002
+#define MV88E6XXX_PORT_MAC_CTL_SPEED_1000 0x0002
+#define MV88E6390_PORT_MAC_CTL_SPEED_10000 0x0003
+#define MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED 0x0003
+
+/* Offset 0x02: Jamming Control Register */
+#define MV88E6097_PORT_JAM_CTL 0x02
+#define MV88E6097_PORT_JAM_CTL_LIMIT_OUT_MASK 0xff00
+#define MV88E6097_PORT_JAM_CTL_LIMIT_IN_MASK 0x00ff
+
+/* Offset 0x02: Flow Control Register */
+#define MV88E6390_PORT_FLOW_CTL 0x02
+#define MV88E6390_PORT_FLOW_CTL_UPDATE 0x8000
+#define MV88E6390_PORT_FLOW_CTL_PTR_MASK 0x7f00
+#define MV88E6390_PORT_FLOW_CTL_LIMIT_IN 0x0000
+#define MV88E6390_PORT_FLOW_CTL_LIMIT_OUT 0x0100
+#define MV88E6390_PORT_FLOW_CTL_DATA_MASK 0x00ff
+
+/* Offset 0x03: Switch Identifier Register */
+#define MV88E6XXX_PORT_SWITCH_ID 0x03
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_MASK 0xfff0
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6085 0x04a0
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6095 0x0950
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6097 0x0990
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6190X 0x0a00
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6390X 0x0a10
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6131 0x1060
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6320 0x1150
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6123 0x1210
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6161 0x1610
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6165 0x1650
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6171 0x1710
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6172 0x1720
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6175 0x1750
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6176 0x1760
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6190 0x1900
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6341 0x3410
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6352 0x3520
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6350 0x3710
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6351 0x3750
+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6390 0x3900
+#define MV88E6XXX_PORT_SWITCH_ID_REV_MASK 0x000f
+
+/* Offset 0x04: Port Control Register */
+#define MV88E6XXX_PORT_CTL0 0x04
+#define MV88E6XXX_PORT_CTL0_USE_CORE_TAG 0x8000
+#define MV88E6XXX_PORT_CTL0_DROP_ON_LOCK 0x4000
+#define MV88E6XXX_PORT_CTL0_EGRESS_MODE_MASK 0x3000
+#define MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNMODIFIED 0x0000
+#define MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNTAGGED 0x1000
+#define MV88E6XXX_PORT_CTL0_EGRESS_MODE_TAGGED 0x2000
+#define MV88E6XXX_PORT_CTL0_EGRESS_MODE_ETHER_TYPE_DSA 0x3000
+#define MV88E6XXX_PORT_CTL0_HEADER 0x0800
+#define MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP 0x0400
+#define MV88E6XXX_PORT_CTL0_DOUBLE_TAG 0x0200
+#define MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK 0x0300
+#define MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL 0x0000
+#define MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA 0x0100
+#define MV88E6XXX_PORT_CTL0_FRAME_MODE_PROVIDER 0x0200
+#define MV88E6XXX_PORT_CTL0_FRAME_MODE_ETHER_TYPE_DSA 0x0300
+#define MV88E6XXX_PORT_CTL0_DSA_TAG 0x0100
+#define MV88E6XXX_PORT_CTL0_VLAN_TUNNEL 0x0080
+#define MV88E6XXX_PORT_CTL0_TAG_IF_BOTH 0x0040
+#define MV88E6185_PORT_CTL0_USE_IP 0x0020
+#define MV88E6185_PORT_CTL0_USE_TAG 0x0010
+#define MV88E6185_PORT_CTL0_FORWARD_UNKNOWN 0x0004
+#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_MASK 0x000c
+#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_DA 0x0000
+#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_MC_DA 0x0004
+#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_UC_DA 0x0008
+#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_ALL_UNKNOWN_DA 0x000c
+#define MV88E6XXX_PORT_CTL0_STATE_MASK 0x0003
+#define MV88E6XXX_PORT_CTL0_STATE_DISABLED 0x0000
+#define MV88E6XXX_PORT_CTL0_STATE_BLOCKING 0x0001
+#define MV88E6XXX_PORT_CTL0_STATE_LEARNING 0x0002
+#define MV88E6XXX_PORT_CTL0_STATE_FORWARDING 0x0003
+
+/* Offset 0x05: Port Control 1 */
+#define MV88E6XXX_PORT_CTL1 0x05
+#define MV88E6XXX_PORT_CTL1_MESSAGE_PORT 0x8000
+#define MV88E6XXX_PORT_CTL1_FID_11_4_MASK 0x00ff
+
+/* Offset 0x06: Port Based VLAN Map */
+#define MV88E6XXX_PORT_BASE_VLAN 0x06
+#define MV88E6XXX_PORT_BASE_VLAN_FID_3_0_MASK 0xf000
+
+/* Offset 0x07: Default Port VLAN ID & Priority */
+#define MV88E6XXX_PORT_DEFAULT_VLAN 0x07
+#define MV88E6XXX_PORT_DEFAULT_VLAN_MASK 0x0fff
+
+/* Offset 0x08: Port Control 2 Register */
+#define MV88E6XXX_PORT_CTL2 0x08
+#define MV88E6XXX_PORT_CTL2_IGNORE_FCS 0x8000
+#define MV88E6XXX_PORT_CTL2_VTU_PRI_OVERRIDE 0x4000
+#define MV88E6XXX_PORT_CTL2_SA_PRIO_OVERRIDE 0x2000
+#define MV88E6XXX_PORT_CTL2_DA_PRIO_OVERRIDE 0x1000
+#define MV88E6XXX_PORT_CTL2_JUMBO_MODE_MASK 0x3000
+#define MV88E6XXX_PORT_CTL2_JUMBO_MODE_1522 0x0000
+#define MV88E6XXX_PORT_CTL2_JUMBO_MODE_2048 0x1000
+#define MV88E6XXX_PORT_CTL2_JUMBO_MODE_10240 0x2000
+#define MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK 0x0c00
+#define MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED 0x0000
+#define MV88E6XXX_PORT_CTL2_8021Q_MODE_FALLBACK 0x0400
+#define MV88E6XXX_PORT_CTL2_8021Q_MODE_CHECK 0x0800
+#define MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE 0x0c00
+#define MV88E6XXX_PORT_CTL2_DISCARD_TAGGED 0x0200
+#define MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED 0x0100
+#define MV88E6XXX_PORT_CTL2_MAP_DA 0x0080
+#define MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD 0x0040
+#define MV88E6XXX_PORT_CTL2_EGRESS_MONITOR 0x0020
+#define MV88E6XXX_PORT_CTL2_INGRESS_MONITOR 0x0010
+#define MV88E6095_PORT_CTL2_CPU_PORT_MASK 0x000f
+
+/* Offset 0x09: Egress Rate Control */
+#define MV88E6XXX_PORT_EGRESS_RATE_CTL1 0x09
+
+/* Offset 0x0A: Egress Rate Control 2 */
+#define MV88E6XXX_PORT_EGRESS_RATE_CTL2 0x0a
+
+/* Offset 0x0B: Port Association Vector */
+#define MV88E6XXX_PORT_ASSOC_VECTOR 0x0b
+#define MV88E6XXX_PORT_ASSOC_VECTOR_HOLD_AT_1 0x8000
+#define MV88E6XXX_PORT_ASSOC_VECTOR_INT_AGE_OUT 0x4000
+#define MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT 0x2000
+#define MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG 0x1000
+#define MV88E6XXX_PORT_ASSOC_VECTOR_REFRESH_LOCKED 0x0800
+
+/* Offset 0x0C: Port ATU Control */
+#define MV88E6XXX_PORT_ATU_CTL 0x0c
+
+/* Offset 0x0D: Priority Override Register */
+#define MV88E6XXX_PORT_PRI_OVERRIDE 0x0d
+
+/* Offset 0x0E: Policy Control Register */
+#define MV88E6XXX_PORT_POLICY_CTL 0x0e
+
+/* Offset 0x0F: Port Special Ether Type */
+#define MV88E6XXX_PORT_ETH_TYPE 0x0f
+#define MV88E6XXX_PORT_ETH_TYPE_DEFAULT 0x9100
+
+/* Offset 0x10: InDiscards Low Counter */
+#define MV88E6XXX_PORT_IN_DISCARD_LO 0x10
+
+/* Offset 0x11: InDiscards High Counter */
+#define MV88E6XXX_PORT_IN_DISCARD_HI 0x11
+
+/* Offset 0x12: InFiltered Counter */
+#define MV88E6XXX_PORT_IN_FILTERED 0x12
+
+/* Offset 0x13: OutFiltered Counter */
+#define MV88E6XXX_PORT_OUT_FILTERED 0x13
+
+/* Offset 0x16: LED Control */
+#define MV88E6XXX_PORT_LED_CONTROL 0x16
+
+/* Offset 0x18: IEEE Priority Mapping Table */
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_MASK 0x7000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP 0x0000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP 0x1000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP 0x2000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP 0x3000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_DSCP 0x5000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_DSCP 0x6000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_DSCP 0x7000
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_PTR_MASK 0x0e00
+#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_DATA_MASK 0x01ff
+
+/* Offset 0x18: Port IEEE Priority Remapping Registers (0-3) */
+#define MV88E6095_PORT_IEEE_PRIO_REMAP_0123 0x18
+
+/* Offset 0x19: Port IEEE Priority Remapping Registers (4-7) */
+#define MV88E6095_PORT_IEEE_PRIO_REMAP_4567 0x19
int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
u16 *val);
@@ -51,21 +274,35 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port);
int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port,
- u16 mode);
+ enum mv88e6xxx_egress_mode mode);
int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
enum mv88e6xxx_frame_mode mode);
int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
enum mv88e6xxx_frame_mode mode);
-int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port,
- bool on);
-int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port,
- bool on);
+int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
+ bool unicast, bool multicast);
+int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
+ bool unicast, bool multicast);
int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
u16 etype);
-int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port);
+int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
+ bool message_port);
+int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
+ size_t size);
int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port);
int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port);
-int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port);
-int mv88e6390_port_pause_config(struct mv88e6xxx_chip *chip, int port);
+int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
+ u8 out);
+int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
+ u8 out);
+int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode);
+int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
+int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
+int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
+ int upstream_port);
+
+int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port);
+int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port);
#endif /* _MV88E6XXX_PORT_H */
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
new file mode 100644
index 000000000000..f3c01119b3d1
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/serdes.c
@@ -0,0 +1,229 @@
+/*
+ * Marvell 88E6xxx SERDES manipulation, via SMI bus
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2017 Andrew Lunn <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/mii.h>
+
+#include "chip.h"
+#include "global2.h"
+#include "phy.h"
+#include "port.h"
+#include "serdes.h"
+
+static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg,
+ u16 *val)
+{
+ return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
+ MV88E6352_SERDES_PAGE_FIBER,
+ reg, val);
+}
+
+static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg,
+ u16 val)
+{
+ return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES,
+ MV88E6352_SERDES_PAGE_FIBER,
+ reg, val);
+}
+
+static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
+{
+ u16 val, new_val;
+ int err;
+
+ err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
+ if (err)
+ return err;
+
+ if (on)
+ new_val = val & ~BMCR_PDOWN;
+ else
+ new_val = val | BMCR_PDOWN;
+
+ if (val != new_val)
+ err = mv88e6352_serdes_write(chip, MII_BMCR, new_val);
+
+ return err;
+}
+
+int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
+{
+ int err;
+ u8 cmode;
+
+ err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
+ if (err)
+ return err;
+
+ if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
+ (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
+ (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) {
+ err = mv88e6352_serdes_power_set(chip, on);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
+static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on)
+{
+ u16 val, new_val;
+ int reg_c45;
+ int err;
+
+ reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
+ MV88E6390_PCS_CONTROL_1;
+ err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
+ if (err)
+ return err;
+
+ if (on)
+ new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
+ MV88E6390_PCS_CONTROL_1_LOOPBACK |
+ MV88E6390_PCS_CONTROL_1_PDOWN);
+ else
+ new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
+
+ if (val != new_val)
+ err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val);
+
+ return err;
+}
+
+/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
+static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr,
+ bool on)
+{
+ u16 val, new_val;
+ int reg_c45;
+ int err;
+
+ reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
+ MV88E6390_SGMII_CONTROL;
+ err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
+ if (err)
+ return err;
+
+ if (on)
+ new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
+ MV88E6390_SGMII_CONTROL_LOOPBACK |
+ MV88E6390_SGMII_CONTROL_PDOWN);
+ else
+ new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
+
+ if (val != new_val)
+ err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val);
+
+ return err;
+}
+
+static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode,
+ int port_donor, int lane, bool rxaui, bool on)
+{
+ int err;
+ u8 cmode_donor;
+
+ err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor);
+ if (err)
+ return err;
+
+ switch (cmode_donor) {
+ case MV88E6XXX_PORT_STS_CMODE_RXAUI:
+ if (!rxaui)
+ break;
+ /* Fall through */
+ case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
+ case MV88E6XXX_PORT_STS_CMODE_SGMII:
+ case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
+ if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
+ cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)
+ return mv88e6390_serdes_sgmii(chip, lane, on);
+ }
+ return 0;
+}
+
+static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode,
+ bool on)
+{
+ switch (cmode) {
+ case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
+ case MV88E6XXX_PORT_STS_CMODE_SGMII:
+ return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on);
+ case MV88E6XXX_PORT_STS_CMODE_XAUI:
+ case MV88E6XXX_PORT_STS_CMODE_RXAUI:
+ case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
+ return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on);
+ }
+
+ return 0;
+}
+
+static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode,
+ bool on)
+{
+ switch (cmode) {
+ case MV88E6XXX_PORT_STS_CMODE_SGMII:
+ return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on);
+ case MV88E6XXX_PORT_STS_CMODE_XAUI:
+ case MV88E6XXX_PORT_STS_CMODE_RXAUI:
+ case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
+ case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
+ return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on);
+ }
+
+ return 0;
+}
+
+int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
+{
+ u8 cmode;
+ int err;
+
+ err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
+ if (err)
+ return err;
+
+ switch (port) {
+ case 2:
+ return mv88e6390_serdes_lower(chip, cmode, 9,
+ MV88E6390_PORT9_LANE1,
+ false, on);
+ case 3:
+ return mv88e6390_serdes_lower(chip, cmode, 9,
+ MV88E6390_PORT9_LANE2,
+ true, on);
+ case 4:
+ return mv88e6390_serdes_lower(chip, cmode, 9,
+ MV88E6390_PORT9_LANE3,
+ true, on);
+ case 5:
+ return mv88e6390_serdes_lower(chip, cmode, 10,
+ MV88E6390_PORT10_LANE1,
+ false, on);
+ case 6:
+ return mv88e6390_serdes_lower(chip, cmode, 10,
+ MV88E6390_PORT10_LANE2,
+ true, on);
+ case 7:
+ return mv88e6390_serdes_lower(chip, cmode, 10,
+ MV88E6390_PORT10_LANE3,
+ true, on);
+ case 9:
+ return mv88e6390_serdes_port9(chip, cmode, on);
+ case 10:
+ return mv88e6390_serdes_port10(chip, cmode, on);
+ }
+
+ return 0;
+}
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h
new file mode 100644
index 000000000000..5c1cd6d8e9a5
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/serdes.h
@@ -0,0 +1,48 @@
+/*
+ * Marvell 88E6xxx SERDES manipulation, via SMI bus
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2016 Andrew Lunn <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _MV88E6XXX_SERDES_H
+#define _MV88E6XXX_SERDES_H
+
+#include "chip.h"
+
+#define MV88E6352_ADDR_SERDES 0x0f
+#define MV88E6352_SERDES_PAGE_FIBER 0x01
+
+#define MV88E6390_PORT9_LANE0 0x09
+#define MV88E6390_PORT9_LANE1 0x12
+#define MV88E6390_PORT9_LANE2 0x13
+#define MV88E6390_PORT9_LANE3 0x14
+#define MV88E6390_PORT10_LANE0 0x0a
+#define MV88E6390_PORT10_LANE1 0x15
+#define MV88E6390_PORT10_LANE2 0x16
+#define MV88E6390_PORT10_LANE3 0x17
+#define MV88E6390_SERDES_DEVICE (4 << 16)
+
+/* 10GBASE-R and 10GBASE-X4/X2 */
+#define MV88E6390_PCS_CONTROL_1 0x1000
+#define MV88E6390_PCS_CONTROL_1_RESET BIT(15)
+#define MV88E6390_PCS_CONTROL_1_LOOPBACK BIT(14)
+#define MV88E6390_PCS_CONTROL_1_SPEED BIT(13)
+#define MV88E6390_PCS_CONTROL_1_PDOWN BIT(11)
+
+/* 1000BASE-X and SGMII */
+#define MV88E6390_SGMII_CONTROL 0x2000
+#define MV88E6390_SGMII_CONTROL_RESET BIT(15)
+#define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14)
+#define MV88E6390_SGMII_CONTROL_PDOWN BIT(11)
+
+int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
+int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
+
+#endif
diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index b3df70d07ff6..b3bee7eab45f 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -18,7 +18,6 @@
#include <linux/phy.h>
#include <linux/netdevice.h>
#include <net/dsa.h>
-#include <net/switchdev.h>
#include <linux/of_net.h>
#include <linux/of_platform.h>
#include <linux/if_bridge.h>
@@ -507,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->ports[ds->dst->cpu_port].dn);
+ phy_mode = of_get_phy_mode(ds->dst->cpu_dp->dn);
if (phy_mode < 0) {
pr_err("Can't find phy-mode for master device\n");
return phy_mode;
@@ -746,17 +745,14 @@ qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
}
static int
-qca8k_port_bridge_join(struct dsa_switch *ds, int port,
- struct net_device *bridge)
+qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
{
struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
int port_mask = BIT(QCA8K_CPU_PORT);
int i;
- priv->port_sts[port].bridge_dev = bridge;
-
for (i = 1; i < QCA8K_NUM_PORTS; i++) {
- if (priv->port_sts[i].bridge_dev != bridge)
+ if (ds->ports[i].bridge_dev != br)
continue;
/* Add this port to the portvlan mask of the other ports
* in the bridge
@@ -775,14 +771,13 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port,
}
static void
-qca8k_port_bridge_leave(struct dsa_switch *ds, int port)
+qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
{
struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
int i;
for (i = 1; i < QCA8K_NUM_PORTS; i++) {
- if (priv->port_sts[i].bridge_dev !=
- priv->port_sts[port].bridge_dev)
+ if (ds->ports[i].bridge_dev != br)
continue;
/* Remove this port to the portvlan mask of the other ports
* in the bridge
@@ -791,7 +786,7 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port)
QCA8K_PORT_LOOKUP_CTRL(i),
BIT(port));
}
- priv->port_sts[port].bridge_dev = NULL;
+
/* Set the cpu port to be the only one in the portvlan mask of
* this port
*/
@@ -877,7 +872,7 @@ qca8k_port_fdb_del(struct dsa_switch *ds, int port,
static int
qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb,
- int (*cb)(struct switchdev_obj *obj))
+ switchdev_obj_dump_cb_t *cb)
{
struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
struct qca8k_fdb _fdb = { 0 };
@@ -911,7 +906,7 @@ qca8k_get_tag_protocol(struct dsa_switch *ds)
return DSA_TAG_PROTO_QCA;
}
-static struct dsa_switch_ops qca8k_switch_ops = {
+static const struct dsa_switch_ops qca8k_switch_ops = {
.get_tag_protocol = qca8k_get_tag_protocol,
.setup = qca8k_setup,
.get_strings = qca8k_get_strings,
@@ -954,17 +949,16 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
if (id != QCA8K_ID_QCA8337)
return -ENODEV;
- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+ priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS);
if (!priv->ds)
return -ENOMEM;
priv->ds->priv = priv;
- priv->ds->dev = &mdiodev->dev;
priv->ds->ops = &qca8k_switch_ops;
mutex_init(&priv->reg_mutex);
dev_set_drvdata(&mdiodev->dev, priv);
- return dsa_register_switch(priv->ds, priv->ds->dev->of_node);
+ return dsa_register_switch(priv->ds);
}
static void
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index 201464719531..1ed4fac6cd6d 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -157,7 +157,6 @@ enum qca8k_fdb_cmd {
struct ar8xxx_port_status {
struct ethtool_eee eee;
- struct net_device *bridge_dev;
int enabled;
};