diff options
Diffstat (limited to 'drivers/net/dsa')
-rw-r--r-- | drivers/net/dsa/Kconfig | 13 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 155 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2.h | 2 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2_regs.h | 15 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6123_61_65.c | 199 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6131.c | 182 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6171.c | 202 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6352.c | 307 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.c | 982 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.h | 245 |
10 files changed, 1488 insertions, 814 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 48e62a34f7f2..18550c7ebe6f 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -7,7 +7,7 @@ config NET_DSA_MV88E6XXX config NET_DSA_MV88E6060 tristate "Marvell 88E6060 ethernet switch chip support" - select NET_DSA + depends on NET_DSA select NET_DSA_TAG_TRAILER ---help--- This enables support for the Marvell 88E6060 ethernet switch @@ -19,7 +19,7 @@ config NET_DSA_MV88E6XXX_NEED_PPU config NET_DSA_MV88E6131 tristate "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support" - select NET_DSA + depends on NET_DSA select NET_DSA_MV88E6XXX select NET_DSA_MV88E6XXX_NEED_PPU select NET_DSA_TAG_DSA @@ -29,7 +29,7 @@ config NET_DSA_MV88E6131 config NET_DSA_MV88E6123_61_65 tristate "Marvell 88E6123/6161/6165 ethernet switch chip support" - select NET_DSA + depends on NET_DSA select NET_DSA_MV88E6XXX select NET_DSA_TAG_EDSA ---help--- @@ -38,7 +38,7 @@ config NET_DSA_MV88E6123_61_65 config NET_DSA_MV88E6171 tristate "Marvell 88E6171/6172 ethernet switch chip support" - select NET_DSA + depends on NET_DSA select NET_DSA_MV88E6XXX select NET_DSA_TAG_EDSA ---help--- @@ -47,7 +47,7 @@ config NET_DSA_MV88E6171 config NET_DSA_MV88E6352 tristate "Marvell 88E6176/88E6352 ethernet switch chip support" - select NET_DSA + depends on NET_DSA select NET_DSA_MV88E6XXX select NET_DSA_TAG_EDSA ---help--- @@ -56,8 +56,7 @@ config NET_DSA_MV88E6352 config NET_DSA_BCM_SF2 tristate "Broadcom Starfighter 2 Ethernet switch support" - depends on HAS_IOMEM - select NET_DSA + depends on HAS_IOMEM && NET_DSA select NET_DSA_TAG_BRCM select FIXED_PHY select BCM7XXX_PHY diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 4daffb284931..cedb572bf25a 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -23,6 +23,7 @@ #include <linux/of_address.h> #include <net/dsa.h> #include <linux/ethtool.h> +#include <linux/if_bridge.h> #include "bcm_sf2.h" #include "bcm_sf2_regs.h" @@ -299,10 +300,14 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, if (port == 7) intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); - /* Set this port, and only this one to be in the default VLAN */ + /* Set this port, and only this one to be in the default VLAN, + * if member of a bridge, restore its membership prior to + * bringing down this port. + */ reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); reg &= ~PORT_VLAN_CTRL_MASK; reg |= (1 << port); + reg |= priv->port_sts[port].vlan_ctl_mask; core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port)); bcm_sf2_imp_vlan_setup(ds, cpu_port); @@ -400,6 +405,151 @@ static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, return 0; } +/* Fast-ageing of ARL entries for a given port, equivalent to an ARL + * flush for that port. + */ +static int bcm_sf2_sw_fast_age_port(struct dsa_switch *ds, int port) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + unsigned int timeout = 1000; + u32 reg; + + core_writel(priv, port, CORE_FAST_AGE_PORT); + + reg = core_readl(priv, CORE_FAST_AGE_CTRL); + reg |= EN_AGE_PORT | FAST_AGE_STR_DONE; + core_writel(priv, reg, CORE_FAST_AGE_CTRL); + + do { + reg = core_readl(priv, CORE_FAST_AGE_CTRL); + if (!(reg & FAST_AGE_STR_DONE)) + break; + + cpu_relax(); + } while (timeout--); + + if (!timeout) + return -ETIMEDOUT; + + return 0; +} + +static int bcm_sf2_sw_br_join(struct dsa_switch *ds, int port, + u32 br_port_mask) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + unsigned int i; + u32 reg, p_ctl; + + p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); + + for (i = 0; i < priv->hw_params.num_ports; i++) { + if (!((1 << i) & br_port_mask)) + continue; + + /* Add this local port to the remote port VLAN control + * membership and update the remote port bitmask + */ + reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); + reg |= 1 << port; + core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); + priv->port_sts[i].vlan_ctl_mask = reg; + + p_ctl |= 1 << i; + } + + /* Configure the local port VLAN control membership to include + * remote ports and update the local port bitmask + */ + core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port)); + priv->port_sts[port].vlan_ctl_mask = p_ctl; + + return 0; +} + +static int bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port, + u32 br_port_mask) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + unsigned int i; + u32 reg, p_ctl; + + p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); + + for (i = 0; i < priv->hw_params.num_ports; i++) { + /* Don't touch the remaining ports */ + if (!((1 << i) & br_port_mask)) + continue; + + reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); + reg &= ~(1 << port); + core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); + priv->port_sts[port].vlan_ctl_mask = reg; + + /* Prevent self removal to preserve isolation */ + if (port != i) + p_ctl &= ~(1 << i); + } + + core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port)); + priv->port_sts[port].vlan_ctl_mask = p_ctl; + + return 0; +} + +static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, + u8 state) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + u8 hw_state, cur_hw_state; + int ret = 0; + u32 reg; + + reg = core_readl(priv, CORE_G_PCTL_PORT(port)); + cur_hw_state = reg >> G_MISTP_STATE_SHIFT; + + switch (state) { + case BR_STATE_DISABLED: + hw_state = G_MISTP_DIS_STATE; + break; + case BR_STATE_LISTENING: + hw_state = G_MISTP_LISTEN_STATE; + break; + case BR_STATE_LEARNING: + hw_state = G_MISTP_LEARN_STATE; + break; + case BR_STATE_FORWARDING: + hw_state = G_MISTP_FWD_STATE; + break; + case BR_STATE_BLOCKING: + hw_state = G_MISTP_BLOCK_STATE; + break; + default: + pr_err("%s: invalid STP state: %d\n", __func__, state); + return -EINVAL; + } + + /* Fast-age ARL entries if we are moving a port from Learning or + * Forwarding state to Disabled, Blocking or Listening state + */ + if (cur_hw_state != hw_state) { + if (cur_hw_state & 4 && !(hw_state & 4)) { + ret = bcm_sf2_sw_fast_age_port(ds, port); + if (ret) { + pr_err("%s: fast-ageing failed\n", __func__); + return ret; + } + } + } + + reg = core_readl(priv, CORE_G_PCTL_PORT(port)); + reg &= ~(G_MISTP_STATE_MASK << G_MISTP_STATE_SHIFT); + reg |= hw_state; + core_writel(priv, reg, CORE_G_PCTL_PORT(port)); + + return 0; +} + static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) { struct bcm_sf2_priv *priv = dev_id; @@ -916,6 +1066,9 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { .port_disable = bcm_sf2_port_disable, .get_eee = bcm_sf2_sw_get_eee, .set_eee = bcm_sf2_sw_set_eee, + .port_join_bridge = bcm_sf2_sw_br_join, + .port_leave_bridge = bcm_sf2_sw_br_leave, + .port_stp_update = bcm_sf2_sw_br_set_stp_state, }; static int __init bcm_sf2_init(void) diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index 7b7053d3c5fa..22e2ebf31333 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -46,6 +46,8 @@ struct bcm_sf2_port_status { unsigned int link; struct ethtool_eee eee; + + u32 vlan_ctl_mask; }; struct bcm_sf2_priv { diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index cabdfa5e217a..fa4e6e78c9ea 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h @@ -163,6 +163,21 @@ #define EN_CHIP_RST (1 << 6) #define EN_SW_RESET (1 << 4) +#define CORE_FAST_AGE_CTRL 0x00220 +#define EN_FAST_AGE_STATIC (1 << 0) +#define EN_AGE_DYNAMIC (1 << 1) +#define EN_AGE_PORT (1 << 2) +#define EN_AGE_VLAN (1 << 3) +#define EN_AGE_SPT (1 << 4) +#define EN_AGE_MCAST (1 << 5) +#define FAST_AGE_STR_DONE (1 << 7) + +#define CORE_FAST_AGE_PORT 0x00224 +#define AGE_PORT_MASK 0xf + +#define CORE_FAST_AGE_VID 0x00228 +#define AGE_VID_MASK 0x3fff + #define CORE_LNKSTS 0x00400 #define LNK_STS_MASK 0x1ff diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c index e9c736e1cef3..b4af6d5aff7c 100644 --- a/drivers/net/dsa/mv88e6123_61_65.c +++ b/drivers/net/dsa/mv88e6123_61_65.c @@ -25,66 +25,33 @@ static char *mv88e6123_61_65_probe(struct device *host_dev, int sw_addr) if (bus == NULL) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { - if (ret == 0x1212) + if (ret == PORT_SWITCH_ID_6123_A1) return "Marvell 88E6123 (A1)"; - if (ret == 0x1213) + if (ret == PORT_SWITCH_ID_6123_A2) return "Marvell 88E6123 (A2)"; - if ((ret & 0xfff0) == 0x1210) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6123) return "Marvell 88E6123"; - if (ret == 0x1612) + if (ret == PORT_SWITCH_ID_6161_A1) return "Marvell 88E6161 (A1)"; - if (ret == 0x1613) + if (ret == PORT_SWITCH_ID_6161_A2) return "Marvell 88E6161 (A2)"; - if ((ret & 0xfff0) == 0x1610) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6161) return "Marvell 88E6161"; - if (ret == 0x1652) + if (ret == PORT_SWITCH_ID_6165_A1) return "Marvell 88E6165 (A1)"; - if (ret == 0x1653) + if (ret == PORT_SWITCH_ID_6165_A2) return "Marvell 88e6165 (A2)"; - if ((ret & 0xfff0) == 0x1650) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6165) return "Marvell 88E6165"; } return NULL; } -static int mv88e6123_61_65_switch_reset(struct dsa_switch *ds) -{ - int i; - int ret; - unsigned long timeout; - - /* Set all ports to the disabled state. */ - for (i = 0; i < 8; i++) { - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); - } - - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); - - /* Reset the switch. */ - REG_WRITE(REG_GLOBAL, 0x04, 0xc400); - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); - if ((ret & 0xc800) == 0xc800) - break; - - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - return 0; -} - static int mv88e6123_61_65_setup_global(struct dsa_switch *ds) { int ret; @@ -222,28 +189,6 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) val |= 0x000c; REG_WRITE(addr, 0x04, val); - /* Port Control 1: disable trunking. Also, if this is the - * CPU port, enable learn messages to be sent to this port. - */ - REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); - - /* Port based VLAN map: give each port its own address - * database, allow the CPU port to talk to each of the 'real' - * ports, and allow each of the 'real' ports to only talk to - * the upstream port. - */ - val = (p & 0xf) << 12; - if (dsa_is_cpu_port(ds, p)) - val |= ds->phys_port_mask; - else - val |= 1 << dsa_upstream_port(ds); - REG_WRITE(addr, 0x06, val); - - /* Default VLAN ID and priority: don't set a default VLAN - * ID, and set the default packet priority to zero. - */ - REG_WRITE(addr, 0x07, 0x0000); - /* Port Control 2: don't force a good FCS, set the maximum * frame size to 10240 bytes, don't let the switch add or * strip 802.1q tags, don't discard tagged or untagged frames @@ -288,7 +233,7 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, 0x19, 0x7654); - return 0; + return mv88e6xxx_setup_port_common(ds, p); } static int mv88e6123_61_65_setup(struct dsa_switch *ds) @@ -297,11 +242,23 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) int i; int ret; - mutex_init(&ps->smi_mutex); - mutex_init(&ps->stats_mutex); - mutex_init(&ps->phy_mutex); + ret = mv88e6xxx_setup_common(ds); + if (ret < 0) + return ret; + + switch (ps->id) { + case PORT_SWITCH_ID_6123: + ps->num_ports = 3; + break; + case PORT_SWITCH_ID_6161: + case PORT_SWITCH_ID_6165: + ps->num_ports = 6; + break; + default: + return -ENODEV; + } - ret = mv88e6123_61_65_switch_reset(ds); + ret = mv88e6xxx_switch_reset(ds, false); if (ret < 0) return ret; @@ -311,7 +268,7 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) if (ret < 0) return ret; - for (i = 0; i < 6; i++) { + for (i = 0; i < ps->num_ports; i++) { ret = mv88e6123_61_65_setup_port(ds, i); if (ret < 0) return ret; @@ -320,108 +277,18 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) return 0; } -static int mv88e6123_61_65_port_to_phy_addr(int port) -{ - if (port >= 0 && port <= 4) - return port; - return -1; -} - -static int -mv88e6123_61_65_phy_read(struct dsa_switch *ds, int port, int regnum) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6123_61_65_port_to_phy_addr(port); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_read(ds, addr, regnum); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static int -mv88e6123_61_65_phy_write(struct dsa_switch *ds, - int port, int regnum, u16 val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6123_61_65_port_to_phy_addr(port); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write(ds, addr, regnum, val); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static struct mv88e6xxx_hw_stat mv88e6123_61_65_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, - { "sw_in_discards", 4, 0x110, }, - { "sw_in_filtered", 2, 0x112, }, - { "sw_out_filtered", 2, 0x113, }, -}; - -static void -mv88e6123_61_65_get_strings(struct dsa_switch *ds, int port, uint8_t *data) -{ - mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats), - mv88e6123_61_65_hw_stats, port, data); -} - -static void -mv88e6123_61_65_get_ethtool_stats(struct dsa_switch *ds, - int port, uint64_t *data) -{ - mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats), - mv88e6123_61_65_hw_stats, port, data); -} - -static int mv88e6123_61_65_get_sset_count(struct dsa_switch *ds) -{ - return ARRAY_SIZE(mv88e6123_61_65_hw_stats); -} - struct dsa_switch_driver mv88e6123_61_65_switch_driver = { .tag_protocol = DSA_TAG_PROTO_EDSA, .priv_size = sizeof(struct mv88e6xxx_priv_state), .probe = mv88e6123_61_65_probe, .setup = mv88e6123_61_65_setup, .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6123_61_65_phy_read, - .phy_write = mv88e6123_61_65_phy_write, + .phy_read = mv88e6xxx_phy_read, + .phy_write = mv88e6xxx_phy_write, .poll_link = mv88e6xxx_poll_link, - .get_strings = mv88e6123_61_65_get_strings, - .get_ethtool_stats = mv88e6123_61_65_get_ethtool_stats, - .get_sset_count = mv88e6123_61_65_get_sset_count, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, #ifdef CONFIG_NET_DSA_HWMON .get_temp = mv88e6xxx_get_temp, #endif diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c index 2540ef0142af..e54824fa0d95 100644 --- a/drivers/net/dsa/mv88e6131.c +++ b/drivers/net/dsa/mv88e6131.c @@ -17,12 +17,6 @@ #include <net/dsa.h> #include "mv88e6xxx.h" -/* Switch product IDs */ -#define ID_6085 0x04a0 -#define ID_6095 0x0950 -#define ID_6131 0x1060 -#define ID_6131_B2 0x1066 - static char *mv88e6131_probe(struct device *host_dev, int sw_addr) { struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); @@ -31,56 +25,23 @@ static char *mv88e6131_probe(struct device *host_dev, int sw_addr) if (bus == NULL) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { int ret_masked = ret & 0xfff0; - if (ret_masked == ID_6085) + if (ret_masked == PORT_SWITCH_ID_6085) return "Marvell 88E6085"; - if (ret_masked == ID_6095) + if (ret_masked == PORT_SWITCH_ID_6095) return "Marvell 88E6095/88E6095F"; - if (ret == ID_6131_B2) + if (ret == PORT_SWITCH_ID_6131_B2) return "Marvell 88E6131 (B2)"; - if (ret_masked == ID_6131) + if (ret_masked == PORT_SWITCH_ID_6131) return "Marvell 88E6131"; } return NULL; } -static int mv88e6131_switch_reset(struct dsa_switch *ds) -{ - int i; - int ret; - unsigned long timeout; - - /* Set all ports to the disabled state. */ - for (i = 0; i < 11; i++) { - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); - } - - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); - - /* Reset the switch. */ - REG_WRITE(REG_GLOBAL, 0x04, 0xc400); - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); - if ((ret & 0xc800) == 0xc800) - break; - - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - return 0; -} - static int mv88e6131_setup_global(struct dsa_switch *ds) { int ret; @@ -174,7 +135,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) * (100 Mb/s on 6085) full duplex. */ if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p)) - if (ps->id == ID_6085) + if (ps->id == PORT_SWITCH_ID_6085) REG_WRITE(addr, 0x01, 0x003d); /* 100 Mb/s */ else REG_WRITE(addr, 0x01, 0x003e); /* 1000 Mb/s */ @@ -201,35 +162,13 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) /* On 6085, unknown multicast forward is controlled * here rather than in Port Control 2 register. */ - if (ps->id == ID_6085) + if (ps->id == PORT_SWITCH_ID_6085) val |= 0x0008; } if (ds->dsa_port_mask & (1 << p)) val |= 0x0100; REG_WRITE(addr, 0x04, val); - /* Port Control 1: disable trunking. Also, if this is the - * CPU port, enable learn messages to be sent to this port. - */ - REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); - - /* Port based VLAN map: give each port its own address - * database, allow the CPU port to talk to each of the 'real' - * ports, and allow each of the 'real' ports to only talk to - * the upstream port. - */ - val = (p & 0xf) << 12; - if (dsa_is_cpu_port(ds, p)) - val |= ds->phys_port_mask; - else - val |= 1 << dsa_upstream_port(ds); - REG_WRITE(addr, 0x06, val); - - /* Default VLAN ID and priority: don't set a default VLAN - * ID, and set the default packet priority to zero. - */ - REG_WRITE(addr, 0x07, 0x0000); - /* Port Control 2: don't force a good FCS, don't use * VLAN-based, source address-based or destination * address-based priority overrides, don't let the switch @@ -242,7 +181,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) * If this is the upstream port for this switch, enable * forwarding of unknown multicast addresses. */ - if (ps->id == ID_6085) + if (ps->id == PORT_SWITCH_ID_6085) /* on 6085, bits 3:0 are reserved, bit 6 control ARP * mirroring, and multicast forward is handled in * Port Control register. @@ -278,7 +217,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, 0x19, 0x7654); - return 0; + return mv88e6xxx_setup_port_common(ds, p); } static int mv88e6131_setup(struct dsa_switch *ds) @@ -287,13 +226,28 @@ static int mv88e6131_setup(struct dsa_switch *ds) int i; int ret; - mutex_init(&ps->smi_mutex); + ret = mv88e6xxx_setup_common(ds); + if (ret < 0) + return ret; + mv88e6xxx_ppu_state_init(ds); - mutex_init(&ps->stats_mutex); - ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; + switch (ps->id) { + case PORT_SWITCH_ID_6085: + ps->num_ports = 10; + break; + case PORT_SWITCH_ID_6095: + ps->num_ports = 11; + break; + case PORT_SWITCH_ID_6131: + case PORT_SWITCH_ID_6131_B2: + ps->num_ports = 8; + break; + default: + return -ENODEV; + } - ret = mv88e6131_switch_reset(ds); + ret = mv88e6xxx_switch_reset(ds, false); if (ret < 0) return ret; @@ -303,7 +257,7 @@ static int mv88e6131_setup(struct dsa_switch *ds) if (ret < 0) return ret; - for (i = 0; i < 11; i++) { + for (i = 0; i < ps->num_ports; i++) { ret = mv88e6131_setup_port(ds, i); if (ret < 0) return ret; @@ -312,17 +266,24 @@ static int mv88e6131_setup(struct dsa_switch *ds) return 0; } -static int mv88e6131_port_to_phy_addr(int port) +static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port) { - if (port >= 0 && port <= 11) + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (port >= 0 && port < ps->num_ports) return port; - return -1; + + return -EINVAL; } static int mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum) { - int addr = mv88e6131_port_to_phy_addr(port); + int addr = mv88e6131_port_to_phy_addr(ds, port); + + if (addr < 0) + return addr; + return mv88e6xxx_phy_read_ppu(ds, addr, regnum); } @@ -330,61 +291,12 @@ static int mv88e6131_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) { - int addr = mv88e6131_port_to_phy_addr(port); - return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val); -} - -static struct mv88e6xxx_hw_stat mv88e6131_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, -}; - -static void -mv88e6131_get_strings(struct dsa_switch *ds, int port, uint8_t *data) -{ - mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6131_hw_stats), - mv88e6131_hw_stats, port, data); -} + int addr = mv88e6131_port_to_phy_addr(ds, port); -static void -mv88e6131_get_ethtool_stats(struct dsa_switch *ds, - int port, uint64_t *data) -{ - mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6131_hw_stats), - mv88e6131_hw_stats, port, data); -} + if (addr < 0) + return addr; -static int mv88e6131_get_sset_count(struct dsa_switch *ds) -{ - return ARRAY_SIZE(mv88e6131_hw_stats); + return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val); } struct dsa_switch_driver mv88e6131_switch_driver = { @@ -396,9 +308,9 @@ struct dsa_switch_driver mv88e6131_switch_driver = { .phy_read = mv88e6131_phy_read, .phy_write = mv88e6131_phy_write, .poll_link = mv88e6xxx_poll_link, - .get_strings = mv88e6131_get_strings, - .get_ethtool_stats = mv88e6131_get_ethtool_stats, - .get_sset_count = mv88e6131_get_sset_count, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, }; MODULE_ALIAS("platform:mv88e6085"); diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index aa33d16f2e22..9104efea0e3e 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c @@ -25,69 +25,27 @@ static char *mv88e6171_probe(struct device *host_dev, int sw_addr) if (bus == NULL) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { - if ((ret & 0xfff0) == 0x1710) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6171) return "Marvell 88E6171"; - if ((ret & 0xfff0) == 0x1720) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6172) return "Marvell 88E6172"; } return NULL; } -static int mv88e6171_switch_reset(struct dsa_switch *ds) -{ - int i; - int ret; - unsigned long timeout; - - /* Set all ports to the disabled state. */ - for (i = 0; i < 8; i++) { - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); - } - - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); - - /* Reset the switch. */ - REG_WRITE(REG_GLOBAL, 0x04, 0xc400); - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); - if ((ret & 0xc800) == 0xc800) - break; - - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - /* Enable ports not under DSA, e.g. WAN port */ - for (i = 0; i < 8; i++) { - if (dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i)) - continue; - - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret | 0x03); - } - - return 0; -} - static int mv88e6171_setup_global(struct dsa_switch *ds) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; int i; - /* Disable the PHY polling unit (since there won't be any - * external PHYs to poll), don't discard packets with - * excessive collisions, and mask all interrupt sources. + /* Discard packets with excessive collisions, mask all + * interrupt sources, enable PPU. */ - REG_WRITE(REG_GLOBAL, 0x04, 0x0000); + REG_WRITE(REG_GLOBAL, 0x04, 0x6000); /* Set the default address aging time to 5 minutes, and * enable address learn messages to be sent to all message @@ -145,7 +103,7 @@ static int mv88e6171_setup_global(struct dsa_switch *ds) } /* Clear all trunk masks. */ - for (i = 0; i < 8; i++) + for (i = 0; i < ps->num_ports; i++) REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff); /* Clear all trunk mappings. */ @@ -219,28 +177,6 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p) val |= 0x000c; REG_WRITE(addr, 0x04, val); - /* Port Control 1: disable trunking. Also, if this is the - * CPU port, enable learn messages to be sent to this port. - */ - REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); - - /* Port based VLAN map: give each port its own address - * database, allow the CPU port to talk to each of the 'real' - * ports, and allow each of the 'real' ports to only talk to - * the upstream port. - */ - val = (p & 0xf) << 12; - if (dsa_is_cpu_port(ds, p)) - val |= ds->phys_port_mask; - else - val |= 1 << dsa_upstream_port(ds); - REG_WRITE(addr, 0x06, val); - - /* Default VLAN ID and priority: don't set a default VLAN - * ID, and set the default packet priority to zero. - */ - REG_WRITE(addr, 0x07, 0x0000); - /* Port Control 2: don't force a good FCS, set the maximum * frame size to 10240 bytes, don't let the switch add or * strip 802.1q tags, don't discard tagged or untagged frames @@ -285,19 +221,22 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, 0x19, 0x7654); - return 0; + return mv88e6xxx_setup_port_common(ds, p); } static int mv88e6171_setup(struct dsa_switch *ds) { - struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int i; int ret; - mutex_init(&ps->smi_mutex); - mutex_init(&ps->stats_mutex); + ret = mv88e6xxx_setup_common(ds); + if (ret < 0) + return ret; + + ps->num_ports = 7; - ret = mv88e6171_switch_reset(ds); + ret = mv88e6xxx_switch_reset(ds, true); if (ret < 0) return ret; @@ -307,7 +246,7 @@ static int mv88e6171_setup(struct dsa_switch *ds) if (ret < 0) return ret; - for (i = 0; i < 8; i++) { + for (i = 0; i < ps->num_ports; i++) { if (!(dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i))) continue; @@ -316,96 +255,29 @@ static int mv88e6171_setup(struct dsa_switch *ds) return ret; } - mutex_init(&ps->phy_mutex); - return 0; } -static int mv88e6171_port_to_phy_addr(int port) -{ - if (port >= 0 && port <= 4) - return port; - return -1; -} - -static int -mv88e6171_phy_read(struct dsa_switch *ds, int port, int regnum) +static int mv88e6171_get_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6171_port_to_phy_addr(port); - int ret; - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_read(ds, addr, regnum); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static int -mv88e6171_phy_write(struct dsa_switch *ds, - int port, int regnum, u16 val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6171_port_to_phy_addr(port); - int ret; + if (ps->id == PORT_SWITCH_ID_6172) + return mv88e6xxx_get_eee(ds, port, e); - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write(ds, addr, regnum, val); - mutex_unlock(&ps->phy_mutex); - return ret; + return -EOPNOTSUPP; } -static struct mv88e6xxx_hw_stat mv88e6171_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, -}; - -static void -mv88e6171_get_strings(struct dsa_switch *ds, int port, uint8_t *data) +static int mv88e6171_set_eee(struct dsa_switch *ds, int port, + struct phy_device *phydev, struct ethtool_eee *e) { - mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6171_hw_stats), - mv88e6171_hw_stats, port, data); -} + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); -static void -mv88e6171_get_ethtool_stats(struct dsa_switch *ds, - int port, uint64_t *data) -{ - mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6171_hw_stats), - mv88e6171_hw_stats, port, data); -} + if (ps->id == PORT_SWITCH_ID_6172) + return mv88e6xxx_set_eee(ds, port, phydev, e); -static int mv88e6171_get_sset_count(struct dsa_switch *ds) -{ - return ARRAY_SIZE(mv88e6171_hw_stats); + return -EOPNOTSUPP; } struct dsa_switch_driver mv88e6171_switch_driver = { @@ -414,17 +286,25 @@ struct dsa_switch_driver mv88e6171_switch_driver = { .probe = mv88e6171_probe, .setup = mv88e6171_setup, .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6171_phy_read, - .phy_write = mv88e6171_phy_write, + .phy_read = mv88e6xxx_phy_read_indirect, + .phy_write = mv88e6xxx_phy_write_indirect, .poll_link = mv88e6xxx_poll_link, - .get_strings = mv88e6171_get_strings, - .get_ethtool_stats = mv88e6171_get_ethtool_stats, - .get_sset_count = mv88e6171_get_sset_count, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, + .set_eee = mv88e6171_set_eee, + .get_eee = mv88e6171_get_eee, #ifdef CONFIG_NET_DSA_HWMON .get_temp = mv88e6xxx_get_temp, #endif .get_regs_len = mv88e6xxx_get_regs_len, .get_regs = mv88e6xxx_get_regs, + .port_join_bridge = mv88e6xxx_join_bridge, + .port_leave_bridge = mv88e6xxx_leave_bridge, + .port_stp_update = mv88e6xxx_port_stp_update, + .fdb_add = mv88e6xxx_port_fdb_add, + .fdb_del = mv88e6xxx_port_fdb_del, + .fdb_getnext = mv88e6xxx_port_fdb_getnext, }; MODULE_ALIAS("platform:mv88e6171"); diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index e13adc7b3dda..126c11b81e75 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c @@ -22,59 +22,6 @@ #include <net/dsa.h> #include "mv88e6xxx.h" -static int mv88e6352_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) -{ - unsigned long timeout = jiffies + HZ / 10; - - while (time_before(jiffies, timeout)) { - int ret; - - ret = REG_READ(reg, offset); - if (!(ret & mask)) - return 0; - - usleep_range(1000, 2000); - } - return -ETIMEDOUT; -} - -static inline int mv88e6352_phy_wait(struct dsa_switch *ds) -{ - return mv88e6352_wait(ds, REG_GLOBAL2, 0x18, 0x8000); -} - -static inline int mv88e6352_eeprom_load_wait(struct dsa_switch *ds) -{ - return mv88e6352_wait(ds, REG_GLOBAL2, 0x14, 0x0800); -} - -static inline int mv88e6352_eeprom_busy_wait(struct dsa_switch *ds) -{ - return mv88e6352_wait(ds, REG_GLOBAL2, 0x14, 0x8000); -} - -static int __mv88e6352_phy_read(struct dsa_switch *ds, int addr, int regnum) -{ - int ret; - - REG_WRITE(REG_GLOBAL2, 0x18, 0x9800 | (addr << 5) | regnum); - - ret = mv88e6352_phy_wait(ds); - if (ret < 0) - return ret; - - return REG_READ(REG_GLOBAL2, 0x19); -} - -static int __mv88e6352_phy_write(struct dsa_switch *ds, int addr, int regnum, - u16 val) -{ - REG_WRITE(REG_GLOBAL2, 0x19, val); - REG_WRITE(REG_GLOBAL2, 0x18, 0x9400 | (addr << 5) | regnum); - - return mv88e6352_phy_wait(ds); -} - static char *mv88e6352_probe(struct device *host_dev, int sw_addr) { struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); @@ -83,58 +30,24 @@ static char *mv88e6352_probe(struct device *host_dev, int sw_addr) if (bus == NULL) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { - if ((ret & 0xfff0) == 0x1760) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6176) return "Marvell 88E6176"; - if (ret == 0x3521) + if (ret == PORT_SWITCH_ID_6352_A0) return "Marvell 88E6352 (A0)"; - if (ret == 0x3522) + if (ret == PORT_SWITCH_ID_6352_A1) return "Marvell 88E6352 (A1)"; - if ((ret & 0xfff0) == 0x3520) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6352) return "Marvell 88E6352"; } return NULL; } -static int mv88e6352_switch_reset(struct dsa_switch *ds) -{ - unsigned long timeout; - int ret; - int i; - - /* Set all ports to the disabled state. */ - for (i = 0; i < 7; i++) { - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); - } - - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); - - /* Reset the switch. Keep PPU active (bit 14, undocumented). - * The PPU needs to be active to support indirect phy register - * accesses through global registers 0x18 and 0x19. - */ - REG_WRITE(REG_GLOBAL, 0x04, 0xc000); - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); - if ((ret & 0x8800) == 0x8800) - break; - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - return 0; -} - static int mv88e6352_setup_global(struct dsa_switch *ds) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; int i; @@ -205,7 +118,7 @@ static int mv88e6352_setup_global(struct dsa_switch *ds) /* Disable ingress rate limiting by resetting all ingress * rate limit registers to their initial state. */ - for (i = 0; i < 7; i++) + for (i = 0; i < ps->num_ports; i++) REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8)); /* Initialise cross-chip port VLAN table to reset defaults. */ @@ -268,28 +181,6 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p) val |= 0x000c; REG_WRITE(addr, 0x04, val); - /* Port Control 1: disable trunking. Also, if this is the - * CPU port, enable learn messages to be sent to this port. - */ - REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); - - /* Port based VLAN map: give each port its own address - * database, allow the CPU port to talk to each of the 'real' - * ports, and allow each of the 'real' ports to only talk to - * the upstream port. - */ - val = (p & 0xf) << 12; - if (dsa_is_cpu_port(ds, p)) - val |= ds->phys_port_mask; - else - val |= 1 << dsa_upstream_port(ds); - REG_WRITE(addr, 0x06, val); - - /* Default VLAN ID and priority: don't set a default VLAN - * ID, and set the default packet priority to zero. - */ - REG_WRITE(addr, 0x07, 0x0000); - /* Port Control 2: don't force a good FCS, set the maximum * frame size to 10240 bytes, don't let the switch add or * strip 802.1q tags, don't discard tagged or untagged frames @@ -334,53 +225,18 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, 0x19, 0x7654); - return 0; + return mv88e6xxx_setup_port_common(ds, p); } #ifdef CONFIG_NET_DSA_HWMON -static int mv88e6352_phy_page_read(struct dsa_switch *ds, - int port, int page, int reg) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = __mv88e6352_phy_write(ds, port, 0x16, page); - if (ret < 0) - goto error; - ret = __mv88e6352_phy_read(ds, port, reg); -error: - __mv88e6352_phy_write(ds, port, 0x16, 0x0); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static int mv88e6352_phy_page_write(struct dsa_switch *ds, - int port, int page, int reg, int val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = __mv88e6352_phy_write(ds, port, 0x16, page); - if (ret < 0) - goto error; - - ret = __mv88e6352_phy_write(ds, port, reg, val); -error: - __mv88e6352_phy_write(ds, port, 0x16, 0x0); - mutex_unlock(&ps->phy_mutex); - return ret; -} - static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp) { int ret; *temp = 0; - ret = mv88e6352_phy_page_read(ds, 0, 6, 27); + ret = mv88e6xxx_phy_page_read(ds, 0, 6, 27); if (ret < 0) return ret; @@ -395,7 +251,7 @@ static int mv88e6352_get_temp_limit(struct dsa_switch *ds, int *temp) *temp = 0; - ret = mv88e6352_phy_page_read(ds, 0, 6, 26); + ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); if (ret < 0) return ret; @@ -408,11 +264,11 @@ static int mv88e6352_set_temp_limit(struct dsa_switch *ds, int temp) { int ret; - ret = mv88e6352_phy_page_read(ds, 0, 6, 26); + ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); if (ret < 0) return ret; temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); - return mv88e6352_phy_page_write(ds, 0, 6, 26, + return mv88e6xxx_phy_page_write(ds, 0, 6, 26, (ret & 0xe0ff) | (temp << 8)); } @@ -422,7 +278,7 @@ static int mv88e6352_get_temp_alarm(struct dsa_switch *ds, bool *alarm) *alarm = false; - ret = mv88e6352_phy_page_read(ds, 0, 6, 26); + ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); if (ret < 0) return ret; @@ -438,14 +294,15 @@ static int mv88e6352_setup(struct dsa_switch *ds) int ret; int i; - mutex_init(&ps->smi_mutex); - mutex_init(&ps->stats_mutex); - mutex_init(&ps->phy_mutex); - mutex_init(&ps->eeprom_mutex); + ret = mv88e6xxx_setup_common(ds); + if (ret < 0) + return ret; - ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; + ps->num_ports = 7; - ret = mv88e6352_switch_reset(ds); + mutex_init(&ps->eeprom_mutex); + + ret = mv88e6xxx_switch_reset(ds, true); if (ret < 0) return ret; @@ -455,7 +312,7 @@ static int mv88e6352_setup(struct dsa_switch *ds) if (ret < 0) return ret; - for (i = 0; i < 7; i++) { + for (i = 0; i < ps->num_ports; i++) { ret = mv88e6352_setup_port(ds, i); if (ret < 0) return ret; @@ -464,83 +321,6 @@ static int mv88e6352_setup(struct dsa_switch *ds) return 0; } -static int mv88e6352_port_to_phy_addr(int port) -{ - if (port >= 0 && port <= 4) - return port; - return -EINVAL; -} - -static int -mv88e6352_phy_read(struct dsa_switch *ds, int port, int regnum) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6352_port_to_phy_addr(port); - int ret; - - if (addr < 0) - return addr; - - mutex_lock(&ps->phy_mutex); - ret = __mv88e6352_phy_read(ds, addr, regnum); - mutex_unlock(&ps->phy_mutex); - - return ret; -} - -static int -mv88e6352_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6352_port_to_phy_addr(port); - int ret; - - if (addr < 0) - return addr; - - mutex_lock(&ps->phy_mutex); - ret = __mv88e6352_phy_write(ds, addr, regnum, val); - mutex_unlock(&ps->phy_mutex); - - return ret; -} - -static struct mv88e6xxx_hw_stat mv88e6352_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, - { "sw_in_discards", 4, 0x110, }, - { "sw_in_filtered", 2, 0x112, }, - { "sw_out_filtered", 2, 0x113, }, -}; - static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); @@ -553,7 +333,7 @@ static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) if (ret < 0) goto error; - ret = mv88e6352_eeprom_busy_wait(ds); + ret = mv88e6xxx_eeprom_busy_wait(ds); if (ret < 0) goto error; @@ -576,7 +356,7 @@ static int mv88e6352_get_eeprom(struct dsa_switch *ds, eeprom->magic = 0xc3ec4951; - ret = mv88e6352_eeprom_load_wait(ds); + ret = mv88e6xxx_eeprom_load_wait(ds); if (ret < 0) return ret; @@ -657,7 +437,7 @@ static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr, if (ret < 0) goto error; - ret = mv88e6352_eeprom_busy_wait(ds); + ret = mv88e6xxx_eeprom_busy_wait(ds); error: mutex_unlock(&ps->eeprom_mutex); return ret; @@ -681,7 +461,7 @@ static int mv88e6352_set_eeprom(struct dsa_switch *ds, len = eeprom->len; eeprom->len = 0; - ret = mv88e6352_eeprom_load_wait(ds); + ret = mv88e6xxx_eeprom_load_wait(ds); if (ret < 0) return ret; @@ -739,37 +519,20 @@ static int mv88e6352_set_eeprom(struct dsa_switch *ds, return 0; } -static void -mv88e6352_get_strings(struct dsa_switch *ds, int port, uint8_t *data) -{ - mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6352_hw_stats), - mv88e6352_hw_stats, port, data); -} - -static void -mv88e6352_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) -{ - mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6352_hw_stats), - mv88e6352_hw_stats, port, data); -} - -static int mv88e6352_get_sset_count(struct dsa_switch *ds) -{ - return ARRAY_SIZE(mv88e6352_hw_stats); -} - struct dsa_switch_driver mv88e6352_switch_driver = { .tag_protocol = DSA_TAG_PROTO_EDSA, .priv_size = sizeof(struct mv88e6xxx_priv_state), .probe = mv88e6352_probe, .setup = mv88e6352_setup, .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6352_phy_read, - .phy_write = mv88e6352_phy_write, + .phy_read = mv88e6xxx_phy_read_indirect, + .phy_write = mv88e6xxx_phy_write_indirect, .poll_link = mv88e6xxx_poll_link, - .get_strings = mv88e6352_get_strings, - .get_ethtool_stats = mv88e6352_get_ethtool_stats, - .get_sset_count = mv88e6352_get_sset_count, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, + .set_eee = mv88e6xxx_set_eee, + .get_eee = mv88e6xxx_get_eee, #ifdef CONFIG_NET_DSA_HWMON .get_temp = mv88e6352_get_temp, .get_temp_limit = mv88e6352_get_temp_limit, @@ -780,6 +543,12 @@ struct dsa_switch_driver mv88e6352_switch_driver = { .set_eeprom = mv88e6352_set_eeprom, .get_regs_len = mv88e6xxx_get_regs_len, .get_regs = mv88e6xxx_get_regs, + .port_join_bridge = mv88e6xxx_join_bridge, + .port_leave_bridge = mv88e6xxx_leave_bridge, + .port_stp_update = mv88e6xxx_port_stp_update, + .fdb_add = mv88e6xxx_port_fdb_add, + .fdb_del = mv88e6xxx_port_fdb_del, + .fdb_getnext = mv88e6xxx_port_fdb_getnext, }; MODULE_ALIAS("platform:mv88e6352"); diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 3e7e31a6abb7..fc8d3b6ffe8e 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -9,6 +9,8 @@ */ #include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/if_bridge.h> #include <linux/jiffies.h> #include <linux/list.h> #include <linux/module.h> @@ -31,11 +33,11 @@ static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr) int i; for (i = 0; i < 16; i++) { - ret = mdiobus_read(bus, sw_addr, 0); + ret = mdiobus_read(bus, sw_addr, SMI_CMD); if (ret < 0) return ret; - if ((ret & 0x8000) == 0) + if ((ret & SMI_CMD_BUSY) == 0) return 0; } @@ -55,7 +57,8 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) return ret; /* Transmit the read command. */ - ret = mdiobus_write(bus, sw_addr, 0, 0x9800 | (addr << 5) | reg); + ret = mdiobus_write(bus, sw_addr, SMI_CMD, + SMI_CMD_OP_22_READ | (addr << 5) | reg); if (ret < 0) return ret; @@ -65,26 +68,23 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) return ret; /* Read the data. */ - ret = mdiobus_read(bus, sw_addr, 1); + ret = mdiobus_read(bus, sw_addr, SMI_DATA); if (ret < 0) return ret; return ret & 0xffff; } -int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) +/* Must be called with SMI mutex held */ +static int _mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); int ret; if (bus == NULL) return -EINVAL; - mutex_lock(&ps->smi_mutex); ret = __mv88e6xxx_reg_read(bus, ds->pd->sw_addr, addr, reg); - mutex_unlock(&ps->smi_mutex); - if (ret < 0) return ret; @@ -94,6 +94,18 @@ int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) return ret; } +int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->smi_mutex); + ret = _mv88e6xxx_reg_read(ds, addr, reg); + mutex_unlock(&ps->smi_mutex); + + return ret; +} + int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, int reg, u16 val) { @@ -108,12 +120,13 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, return ret; /* Transmit the data to write. */ - ret = mdiobus_write(bus, sw_addr, 1, val); + ret = mdiobus_write(bus, sw_addr, SMI_DATA, val); if (ret < 0) return ret; /* Transmit the write command. */ - ret = mdiobus_write(bus, sw_addr, 0, 0x9400 | (addr << 5) | reg); + ret = mdiobus_write(bus, sw_addr, SMI_CMD, + SMI_CMD_OP_22_WRITE | (addr << 5) | reg); if (ret < 0) return ret; @@ -125,11 +138,11 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, return 0; } -int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) +/* Must be called with SMI mutex held */ +static int _mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, + u16 val) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); - int ret; if (bus == NULL) return -EINVAL; @@ -137,8 +150,16 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", addr, reg, val); + return __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val); +} + +int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + mutex_lock(&ps->smi_mutex); - ret = __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val); + ret = _mv88e6xxx_reg_write(ds, addr, reg, val); mutex_unlock(&ps->smi_mutex); return ret; @@ -147,26 +168,26 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) int mv88e6xxx_config_prio(struct dsa_switch *ds) { /* Configure the IP ToS mapping registers. */ - REG_WRITE(REG_GLOBAL, 0x10, 0x0000); - REG_WRITE(REG_GLOBAL, 0x11, 0x0000); - REG_WRITE(REG_GLOBAL, 0x12, 0x5555); - REG_WRITE(REG_GLOBAL, 0x13, 0x5555); - REG_WRITE(REG_GLOBAL, 0x14, 0xaaaa); - REG_WRITE(REG_GLOBAL, 0x15, 0xaaaa); - REG_WRITE(REG_GLOBAL, 0x16, 0xffff); - REG_WRITE(REG_GLOBAL, 0x17, 0xffff); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff); /* Configure the IEEE 802.1p priority mapping register. */ - REG_WRITE(REG_GLOBAL, 0x18, 0xfa41); + REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41); return 0; } int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) { - REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]); - REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]); - REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]); + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]); + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]); + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]); return 0; } @@ -180,12 +201,13 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) int j; /* Write the MAC address byte. */ - REG_WRITE(REG_GLOBAL2, 0x0d, 0x8000 | (i << 8) | addr[i]); + REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MAC, + GLOBAL2_SWITCH_MAC_BUSY | (i << 8) | addr[i]); /* Wait for the write to complete. */ for (j = 0; j < 16; j++) { - ret = REG_READ(REG_GLOBAL2, 0x0d); - if ((ret & 0x8000) == 0) + ret = REG_READ(REG_GLOBAL2, GLOBAL2_SWITCH_MAC); + if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0) break; } if (j == 16) @@ -195,14 +217,17 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) return 0; } -int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum) +/* Must be called with phy mutex held */ +static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum) { if (addr >= 0) return mv88e6xxx_reg_read(ds, addr, regnum); return 0xffff; } -int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val) +/* Must be called with phy mutex held */ +static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, + u16 val) { if (addr >= 0) return mv88e6xxx_reg_write(ds, addr, regnum, val); @@ -215,14 +240,16 @@ static int mv88e6xxx_ppu_disable(struct dsa_switch *ds) int ret; unsigned long timeout; - ret = REG_READ(REG_GLOBAL, 0x04); - REG_WRITE(REG_GLOBAL, 0x04, ret & ~0x4000); + ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL); + REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, + ret & ~GLOBAL_CONTROL_PPU_ENABLE); timeout = jiffies + 1 * HZ; while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); + ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS); usleep_range(1000, 2000); - if ((ret & 0xc000) != 0xc000) + if ((ret & GLOBAL_STATUS_PPU_MASK) != + GLOBAL_STATUS_PPU_POLLING) return 0; } @@ -234,14 +261,15 @@ static int mv88e6xxx_ppu_enable(struct dsa_switch *ds) int ret; unsigned long timeout; - ret = REG_READ(REG_GLOBAL, 0x04); - REG_WRITE(REG_GLOBAL, 0x04, ret | 0x4000); + ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL); + REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, ret | GLOBAL_CONTROL_PPU_ENABLE); timeout = jiffies + 1 * HZ; while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); + ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS); usleep_range(1000, 2000); - if ((ret & 0xc000) == 0xc000) + if ((ret & GLOBAL_STATUS_PPU_MASK) == + GLOBAL_STATUS_PPU_POLLING) return 0; } @@ -362,11 +390,12 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) link = 0; if (dev->flags & IFF_UP) { - port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), 0x00); + port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), + PORT_STATUS); if (port_status < 0) continue; - link = !!(port_status & 0x0800); + link = !!(port_status & PORT_STATUS_LINK); } if (!link) { @@ -377,22 +406,22 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) continue; } - switch (port_status & 0x0300) { - case 0x0000: + switch (port_status & PORT_STATUS_SPEED_MASK) { + case PORT_STATUS_SPEED_10: speed = 10; break; - case 0x0100: + case PORT_STATUS_SPEED_100: speed = 100; break; - case 0x0200: + case PORT_STATUS_SPEED_1000: speed = 1000; break; default: speed = -1; break; } - duplex = (port_status & 0x0400) ? 1 : 0; - fc = (port_status & 0x8000) ? 1 : 0; + duplex = (port_status & PORT_STATUS_DUPLEX) ? 1 : 0; + fc = (port_status & PORT_STATUS_PAUSE_EN) ? 1 : 0; if (!netif_carrier_ok(dev)) { netdev_info(dev, @@ -405,14 +434,27 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) } } +static bool mv88e6xxx_6352_family(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + switch (ps->id) { + case PORT_SWITCH_ID_6352: + case PORT_SWITCH_ID_6172: + case PORT_SWITCH_ID_6176: + return true; + } + return false; +} + static int mv88e6xxx_stats_wait(struct dsa_switch *ds) { int ret; int i; for (i = 0; i < 10; i++) { - ret = REG_READ(REG_GLOBAL, 0x1d); - if ((ret & 0x8000) == 0) + ret = REG_READ(REG_GLOBAL, GLOBAL_STATS_OP); + if ((ret & GLOBAL_STATS_OP_BUSY) == 0) return 0; } @@ -423,8 +465,13 @@ static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port) { int ret; + if (mv88e6xxx_6352_family(ds)) + port = (port + 1) << 5; + /* Snapshot the hardware statistics counters for this port. */ - REG_WRITE(REG_GLOBAL, 0x1d, 0xdc00 | port); + REG_WRITE(REG_GLOBAL, GLOBAL_STATS_OP, + GLOBAL_STATS_OP_CAPTURE_PORT | + GLOBAL_STATS_OP_HIST_RX_TX | port); /* Wait for the snapshotting to complete. */ ret = mv88e6xxx_stats_wait(ds); @@ -441,7 +488,9 @@ static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) *val = 0; - ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1d, 0xcc00 | stat); + ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP, + GLOBAL_STATS_OP_READ_CAPTURED | + GLOBAL_STATS_OP_HIST_RX_TX | stat); if (ret < 0) return; @@ -449,22 +498,77 @@ static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) if (ret < 0) return; - ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1e); + ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32); if (ret < 0) return; _val = ret << 16; - ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1f); + ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01); if (ret < 0) return; *val = _val | ret; } -void mv88e6xxx_get_strings(struct dsa_switch *ds, - int nr_stats, struct mv88e6xxx_hw_stat *stats, - int port, uint8_t *data) +static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { + { "in_good_octets", 8, 0x00, }, + { "in_bad_octets", 4, 0x02, }, + { "in_unicast", 4, 0x04, }, + { "in_broadcasts", 4, 0x06, }, + { "in_multicasts", 4, 0x07, }, + { "in_pause", 4, 0x16, }, + { "in_undersize", 4, 0x18, }, + { "in_fragments", 4, 0x19, }, + { "in_oversize", 4, 0x1a, }, + { "in_jabber", 4, 0x1b, }, + { "in_rx_error", 4, 0x1c, }, + { "in_fcs_error", 4, 0x1d, }, + { "out_octets", 8, 0x0e, }, + { "out_unicast", 4, 0x10, }, + { "out_broadcasts", 4, 0x13, }, + { "out_multicasts", 4, 0x12, }, + { "out_pause", 4, 0x15, }, + { "excessive", 4, 0x11, }, + { "collisions", 4, 0x1e, }, + { "deferred", 4, 0x05, }, + { "single", 4, 0x14, }, + { "multiple", 4, 0x17, }, + { "out_fcs_error", 4, 0x03, }, + { "late", 4, 0x1f, }, + { "hist_64bytes", 4, 0x08, }, + { "hist_65_127bytes", 4, 0x09, }, + { "hist_128_255bytes", 4, 0x0a, }, + { "hist_256_511bytes", 4, 0x0b, }, + { "hist_512_1023bytes", 4, 0x0c, }, + { "hist_1024_max_bytes", 4, 0x0d, }, + /* Not all devices have the following counters */ + { "sw_in_discards", 4, 0x110, }, + { "sw_in_filtered", 2, 0x112, }, + { "sw_out_filtered", 2, 0x113, }, + +}; + +static bool have_sw_in_discards(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + switch (ps->id) { + case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161: + case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171: + case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176: + case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185: + case PORT_SWITCH_ID_6352: + return true; + default: + return false; + } +} + +static void _mv88e6xxx_get_strings(struct dsa_switch *ds, + int nr_stats, + struct mv88e6xxx_hw_stat *stats, + int port, uint8_t *data) { int i; @@ -474,9 +578,10 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds, } } -void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, - int nr_stats, struct mv88e6xxx_hw_stat *stats, - int port, uint64_t *data) +static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, + int nr_stats, + struct mv88e6xxx_hw_stat *stats, + int port, uint64_t *data) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; @@ -524,6 +629,39 @@ error: mutex_unlock(&ps->stats_mutex); } +/* All the statistics in the table */ +void +mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) +{ + if (have_sw_in_discards(ds)) + _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats), + mv88e6xxx_hw_stats, port, data); + else + _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3, + mv88e6xxx_hw_stats, port, data); +} + +int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +{ + if (have_sw_in_discards(ds)) + return ARRAY_SIZE(mv88e6xxx_hw_stats); + return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3; +} + +void +mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, + int port, uint64_t *data) +{ + if (have_sw_in_discards(ds)) + _mv88e6xxx_get_ethtool_stats( + ds, ARRAY_SIZE(mv88e6xxx_hw_stats), + mv88e6xxx_hw_stats, port, data); + else + _mv88e6xxx_get_ethtool_stats( + ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3, + mv88e6xxx_hw_stats, port, data); +} + int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) { return 32 * sizeof(u16); @@ -560,42 +698,754 @@ int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6); + ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6); if (ret < 0) goto error; /* Enable temperature sensor */ - ret = mv88e6xxx_phy_read(ds, 0x0, 0x1a); + ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a); if (ret < 0) goto error; - ret = mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5)); + ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5)); if (ret < 0) goto error; /* Wait for temperature to stabilize */ usleep_range(10000, 12000); - val = mv88e6xxx_phy_read(ds, 0x0, 0x1a); + val = _mv88e6xxx_phy_read(ds, 0x0, 0x1a); if (val < 0) { ret = val; goto error; } /* Disable temperature sensor */ - ret = mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5)); + ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5)); if (ret < 0) goto error; *temp = ((val & 0x1f) - 5) * 5; error: - mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0); + _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0); mutex_unlock(&ps->phy_mutex); return ret; } #endif /* CONFIG_NET_DSA_HWMON */ +static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) +{ + unsigned long timeout = jiffies + HZ / 10; + + while (time_before(jiffies, timeout)) { + int ret; + + ret = REG_READ(reg, offset); + if (!(ret & mask)) + return 0; + + usleep_range(1000, 2000); + } + return -ETIMEDOUT; +} + +int mv88e6xxx_phy_wait(struct dsa_switch *ds) +{ + return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP, + GLOBAL2_SMI_OP_BUSY); +} + +int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) +{ + return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + GLOBAL2_EEPROM_OP_LOAD); +} + +int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) +{ + return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + GLOBAL2_EEPROM_OP_BUSY); +} + +/* Must be called with SMI lock held */ +static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) +{ + unsigned long timeout = jiffies + HZ / 10; + + while (time_before(jiffies, timeout)) { + int ret; + + ret = _mv88e6xxx_reg_read(ds, reg, offset); + if (ret < 0) + return ret; + if (!(ret & mask)) + return 0; + + usleep_range(1000, 2000); + } + return -ETIMEDOUT; +} + +/* Must be called with SMI lock held */ +static int _mv88e6xxx_atu_wait(struct dsa_switch *ds) +{ + return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP, + GLOBAL_ATU_OP_BUSY); +} + +/* Must be called with phy mutex held */ +static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, + int regnum) +{ + int ret; + + REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP, + GLOBAL2_SMI_OP_22_READ | (addr << 5) | regnum); + + ret = mv88e6xxx_phy_wait(ds); + if (ret < 0) + return ret; + + return REG_READ(REG_GLOBAL2, GLOBAL2_SMI_DATA); +} + +/* Must be called with phy mutex held */ +static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, + int regnum, u16 val) +{ + REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_DATA, val); + REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP, + GLOBAL2_SMI_OP_22_WRITE | (addr << 5) | regnum); + + return mv88e6xxx_phy_wait(ds); +} + +int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int reg; + + mutex_lock(&ps->phy_mutex); + + reg = _mv88e6xxx_phy_read_indirect(ds, port, 16); + if (reg < 0) + goto out; + + e->eee_enabled = !!(reg & 0x0200); + e->tx_lpi_enabled = !!(reg & 0x0100); + + reg = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS); + if (reg < 0) + goto out; + + e->eee_active = !!(reg & PORT_STATUS_EEE); + reg = 0; + +out: + mutex_unlock(&ps->phy_mutex); + return reg; +} + +int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, + struct phy_device *phydev, struct ethtool_eee *e) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int reg; + int ret; + + mutex_lock(&ps->phy_mutex); + + ret = _mv88e6xxx_phy_read_indirect(ds, port, 16); + if (ret < 0) + goto out; + + reg = ret & ~0x0300; + if (e->eee_enabled) + reg |= 0x0200; + if (e->tx_lpi_enabled) + reg |= 0x0100; + + ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg); +out: + mutex_unlock(&ps->phy_mutex); + + return ret; +} + +static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd) +{ + int ret; + + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x01, fid); + if (ret < 0) + return ret; + + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd); + if (ret < 0) + return ret; + + return _mv88e6xxx_atu_wait(ds); +} + +static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid) +{ + int ret; + + ret = _mv88e6xxx_atu_wait(ds); + if (ret < 0) + return ret; + + return _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB); +} + +static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int reg, ret; + u8 oldstate; + + mutex_lock(&ps->smi_mutex); + + reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL); + if (reg < 0) + goto abort; + + oldstate = reg & PORT_CONTROL_STATE_MASK; + if (oldstate != state) { + /* Flush forwarding database if we're moving a port + * from Learning or Forwarding state to Disabled or + * Blocking or Listening state. + */ + if (oldstate >= PORT_CONTROL_STATE_LEARNING && + state <= PORT_CONTROL_STATE_BLOCKING) { + ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]); + if (ret) + goto abort; + } + reg = (reg & ~PORT_CONTROL_STATE_MASK) | state; + ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL, + reg); + } + +abort: + mutex_unlock(&ps->smi_mutex); + return ret; +} + +/* Must be called with smi lock held */ +static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + u8 fid = ps->fid[port]; + u16 reg = fid << 12; + + if (dsa_is_cpu_port(ds, port)) + reg |= ds->phys_port_mask; + else + reg |= (ps->bridge_mask[fid] | + (1 << dsa_upstream_port(ds))) & ~(1 << port); + + return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg); +} + +/* Must be called with smi lock held */ +static int _mv88e6xxx_update_bridge_config(struct dsa_switch *ds, int fid) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int port; + u32 mask; + int ret; + + mask = ds->phys_port_mask; + while (mask) { + port = __ffs(mask); + mask &= ~(1 << port); + if (ps->fid[port] != fid) + continue; + + ret = _mv88e6xxx_update_port_config(ds, port); + if (ret) + return ret; + } + + return _mv88e6xxx_flush_fid(ds, fid); +} + +/* Bridge handling functions */ + +int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret = 0; + u32 nmask; + int fid; + + /* If the bridge group is not empty, join that group. + * Otherwise create a new group. + */ + fid = ps->fid[port]; + nmask = br_port_mask & ~(1 << port); + if (nmask) + fid = ps->fid[__ffs(nmask)]; + + nmask = ps->bridge_mask[fid] | (1 << port); + if (nmask != br_port_mask) { + netdev_err(ds->ports[port], + "join: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n", + fid, br_port_mask, nmask); + return -EINVAL; + } + + mutex_lock(&ps->smi_mutex); + + ps->bridge_mask[fid] = br_port_mask; + + if (fid != ps->fid[port]) { + ps->fid_mask |= 1 << ps->fid[port]; + ps->fid[port] = fid; + ret = _mv88e6xxx_update_bridge_config(ds, fid); + } + + mutex_unlock(&ps->smi_mutex); + + return ret; +} + +int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + u8 fid, newfid; + int ret; + + fid = ps->fid[port]; + + if (ps->bridge_mask[fid] != br_port_mask) { + netdev_err(ds->ports[port], + "leave: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n", + fid, br_port_mask, ps->bridge_mask[fid]); + return -EINVAL; + } + + /* If the port was the last port of a bridge, we are done. + * Otherwise assign a new fid to the port, and fix up + * the bridge configuration. + */ + if (br_port_mask == (1 << port)) + return 0; + + mutex_lock(&ps->smi_mutex); + + newfid = __ffs(ps->fid_mask); + ps->fid[port] = newfid; + ps->fid_mask &= (1 << newfid); + ps->bridge_mask[fid] &= ~(1 << port); + ps->bridge_mask[newfid] = 1 << port; + + ret = _mv88e6xxx_update_bridge_config(ds, fid); + if (!ret) + ret = _mv88e6xxx_update_bridge_config(ds, newfid); + + mutex_unlock(&ps->smi_mutex); + + return ret; +} + +int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int stp_state; + + 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; + } + + netdev_dbg(ds->ports[port], "port state %d [%d]\n", state, stp_state); + + /* mv88e6xxx_port_stp_update may be called with softirqs disabled, + * so we can not update the port state directly but need to schedule it. + */ + ps->port_state[port] = stp_state; + set_bit(port, &ps->port_state_update_mask); + schedule_work(&ps->bridge_work); + + return 0; +} + +static int __mv88e6xxx_write_addr(struct dsa_switch *ds, + const unsigned char *addr) +{ + int i, ret; + + for (i = 0; i < 3; i++) { + ret = _mv88e6xxx_reg_write( + ds, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i, + (addr[i * 2] << 8) | addr[i * 2 + 1]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr) +{ + int i, ret; + + for (i = 0; i < 3; i++) { + ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, + GLOBAL_ATU_MAC_01 + i); + if (ret < 0) + return ret; + addr[i * 2] = ret >> 8; + addr[i * 2 + 1] = ret & 0xff; + } + + return 0; +} + +static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port, + const unsigned char *addr, int state) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + u8 fid = ps->fid[port]; + int ret; + + ret = _mv88e6xxx_atu_wait(ds); + if (ret < 0) + return ret; + + ret = __mv88e6xxx_write_addr(ds, addr); + if (ret < 0) + return ret; + + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, + (0x10 << port) | state); + if (ret) + return ret; + + ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_LOAD_DB); + + return ret; +} + +int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + int state = is_multicast_ether_addr(addr) ? + GLOBAL_ATU_DATA_STATE_MC_STATIC : + GLOBAL_ATU_DATA_STATE_UC_STATIC; + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->smi_mutex); + ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, state); + mutex_unlock(&ps->smi_mutex); + + return ret; +} + +int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->smi_mutex); + ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, + GLOBAL_ATU_DATA_STATE_UNUSED); + mutex_unlock(&ps->smi_mutex); + + return ret; +} + +static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port, + unsigned char *addr, bool *is_static) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + u8 fid = ps->fid[port]; + int ret, state; + + ret = _mv88e6xxx_atu_wait(ds); + if (ret < 0) + return ret; + + ret = __mv88e6xxx_write_addr(ds, addr); + if (ret < 0) + return ret; + + do { + ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB); + if (ret < 0) + return ret; + + ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA); + if (ret < 0) + return ret; + state = ret & GLOBAL_ATU_DATA_STATE_MASK; + if (state == GLOBAL_ATU_DATA_STATE_UNUSED) + return -ENOENT; + } while (!(((ret >> 4) & 0xff) & (1 << port))); + + ret = __mv88e6xxx_read_addr(ds, addr); + if (ret < 0) + return ret; + + *is_static = state == (is_multicast_ether_addr(addr) ? + GLOBAL_ATU_DATA_STATE_MC_STATIC : + GLOBAL_ATU_DATA_STATE_UC_STATIC); + + return 0; +} + +/* get next entry for port */ +int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, + unsigned char *addr, bool *is_static) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->smi_mutex); + ret = __mv88e6xxx_port_getnext(ds, port, addr, is_static); + mutex_unlock(&ps->smi_mutex); + + return ret; +} + +static void mv88e6xxx_bridge_work(struct work_struct *work) +{ + struct mv88e6xxx_priv_state *ps; + struct dsa_switch *ds; + int port; + + ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work); + ds = ((struct dsa_switch *)ps) - 1; + + while (ps->port_state_update_mask) { + port = __ffs(ps->port_state_update_mask); + clear_bit(port, &ps->port_state_update_mask); + mv88e6xxx_set_port_state(ds, port, ps->port_state[port]); + } +} + +int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret, fid; + + mutex_lock(&ps->smi_mutex); + + /* Port Control 1: disable trunking, disable sending + * learning messages to this port. + */ + ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN, + 0x0000); + if (ret) + goto abort; + + /* Port based VLAN map: give each port its own address + * database, allow the CPU port to talk to each of the 'real' + * ports, and allow each of the 'real' ports to only talk to + * the upstream port. + */ + fid = __ffs(ps->fid_mask); + ps->fid[port] = fid; + ps->fid_mask &= ~(1 << fid); + + if (!dsa_is_cpu_port(ds, port)) + ps->bridge_mask[fid] = 1 << port; + + ret = _mv88e6xxx_update_port_config(ds, port); + if (ret) + goto abort; + + /* Default VLAN ID and priority: don't set a default VLAN + * ID, and set the default packet priority to zero. + */ + ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x07, 0x0000); +abort: + mutex_unlock(&ps->smi_mutex); + return ret; +} + +int mv88e6xxx_setup_common(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + mutex_init(&ps->smi_mutex); + mutex_init(&ps->stats_mutex); + mutex_init(&ps->phy_mutex); + + ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0; + + ps->fid_mask = (1 << DSA_MAX_PORTS) - 1; + + INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work); + + return 0; +} + +int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + u16 is_reset = (ppu_active ? 0x8800 : 0xc800); + unsigned long timeout; + int ret; + int i; + + /* Set all ports to the disabled state. */ + for (i = 0; i < ps->num_ports; i++) { + ret = REG_READ(REG_PORT(i), PORT_CONTROL); + REG_WRITE(REG_PORT(i), PORT_CONTROL, ret & 0xfffc); + } + + /* Wait for transmit queues to drain. */ + usleep_range(2000, 4000); + + /* Reset the switch. Keep the PPU active if requested. The PPU + * needs to be active to support indirect phy register access + * through global registers 0x18 and 0x19. + */ + if (ppu_active) + REG_WRITE(REG_GLOBAL, 0x04, 0xc000); + else + REG_WRITE(REG_GLOBAL, 0x04, 0xc400); + + /* Wait up to one second for reset to complete. */ + timeout = jiffies + 1 * HZ; + while (time_before(jiffies, timeout)) { + ret = REG_READ(REG_GLOBAL, 0x00); + if ((ret & is_reset) == is_reset) + break; + usleep_range(1000, 2000); + } + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + return 0; +} + +int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); + if (ret < 0) + goto error; + ret = _mv88e6xxx_phy_read_indirect(ds, port, reg); +error: + _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, + int reg, int val) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); + if (ret < 0) + goto error; + + ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val); +error: + _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (port >= 0 && port < ps->num_ports) + return port; + return -EINVAL; +} + +int +mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int addr = mv88e6xxx_port_to_phy_addr(ds, port); + int ret; + + if (addr < 0) + return addr; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_read(ds, addr, regnum); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +int +mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int addr = mv88e6xxx_port_to_phy_addr(ds, port); + int ret; + + if (addr < 0) + return addr; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_write(ds, addr, regnum, val); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +int +mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int addr = mv88e6xxx_port_to_phy_addr(ds, port); + int ret; + + if (addr < 0) + return addr; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +int +mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum, + u16 val) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int addr = mv88e6xxx_port_to_phy_addr(ds, port); + int ret; + + if (addr < 0) + return addr; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val); + mutex_unlock(&ps->phy_mutex); + return ret; +} + static int __init mv88e6xxx_init(void) { #if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 03e397efde36..e045154f3364 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -11,9 +11,199 @@ #ifndef __MV88E6XXX_H #define __MV88E6XXX_H +#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 REG_PORT(p) (0x10 + (p)) +#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_PCS_CTRL 0x01 +#define PORT_SWITCH_ID 0x03 +#define PORT_SWITCH_ID_6085 0x04a0 +#define PORT_SWITCH_ID_6095 0x0950 +#define PORT_SWITCH_ID_6123 0x1210 +#define PORT_SWITCH_ID_6123_A1 0x1212 +#define PORT_SWITCH_ID_6123_A2 0x1213 +#define PORT_SWITCH_ID_6131 0x1060 +#define PORT_SWITCH_ID_6131_B2 0x1066 +#define PORT_SWITCH_ID_6152 0x1a40 +#define PORT_SWITCH_ID_6155 0x1a50 +#define PORT_SWITCH_ID_6161 0x1610 +#define PORT_SWITCH_ID_6161_A1 0x1612 +#define PORT_SWITCH_ID_6161_A2 0x1613 +#define PORT_SWITCH_ID_6165 0x1650 +#define PORT_SWITCH_ID_6165_A1 0x1652 +#define PORT_SWITCH_ID_6165_A2 0x1653 +#define PORT_SWITCH_ID_6171 0x1710 +#define PORT_SWITCH_ID_6172 0x1720 +#define PORT_SWITCH_ID_6176 0x1760 +#define PORT_SWITCH_ID_6182 0x1a60 +#define PORT_SWITCH_ID_6185 0x1a70 +#define PORT_SWITCH_ID_6352 0x3520 +#define PORT_SWITCH_ID_6352_A0 0x3521 +#define PORT_SWITCH_ID_6352_A1 0x3522 +#define PORT_CONTROL 0x04 +#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_BASE_VLAN 0x06 +#define PORT_DEFAULT_VLAN 0x07 +#define PORT_CONTROL_2 0x08 +#define PORT_RATE_CONTROL 0x09 +#define PORT_RATE_CONTROL_2 0x0a +#define PORT_ASSOC_VECTOR 0x0b +#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 0x19 +#define PORT_TAG_REGMAP_4567 0x1a + #define REG_GLOBAL 0x1b +#define GLOBAL_STATUS 0x00 +#define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */ +/* Two bits for 6165, 6185 etc */ +#define GLOBAL_STATUS_PPU_MASK (0x3 << 14) +#define GLOBAL_STATUS_PPU_DISABLED_RST (0x0 << 14) +#define GLOBAL_STATUS_PPU_INITIALIZING (0x1 << 14) +#define GLOBAL_STATUS_PPU_DISABLED (0x2 << 14) +#define GLOBAL_STATUS_PPU_POLLING (0x3 << 14) +#define GLOBAL_MAC_01 0x01 +#define GLOBAL_MAC_23 0x02 +#define GLOBAL_MAC_45 0x03 +#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_VID 0x06 +#define GLOBAL_VTU_DATA_0_3 0x07 +#define GLOBAL_VTU_DATA_4_7 0x08 +#define GLOBAL_VTU_DATA_8_11 0x09 +#define GLOBAL_ATU_CONTROL 0x0a +#define GLOBAL_ATU_OP 0x0b +#define GLOBAL_ATU_OP_BUSY BIT(15) +#define GLOBAL_ATU_OP_NOP (0 << 12) +#define GLOBAL_ATU_OP_FLUSH_ALL ((1 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_FLUSH_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_DB ((5 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_FLUSH_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_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_CONTROL_2 0x1c +#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_COUNTER_32 0x1e +#define GLOBAL_STATS_COUNTER_01 0x1f + #define REG_GLOBAL2 0x1c +#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_DEVICE_MAPPING 0x06 +#define GLOBAL2_TRUNK_MASK 0x07 +#define GLOBAL2_TRUNK_MAPPING 0x08 +#define GLOBAL2_INGRESS_OP 0x09 +#define GLOBAL2_INGRESS_DATA 0x0a +#define GLOBAL2_PVT_ADDR 0x0b +#define GLOBAL2_PVT_DATA 0x0c +#define GLOBAL2_SWITCH_MAC 0x0d +#define GLOBAL2_SWITCH_MAC_BUSY BIT(15) +#define GLOBAL2_ATU_STATS 0x0e +#define GLOBAL2_PRIO_OVERRIDE 0x0f +#define GLOBAL2_EEPROM_OP 0x14 +#define GLOBAL2_EEPROM_OP_BUSY BIT(15) +#define GLOBAL2_EEPROM_OP_LOAD BIT(11) +#define GLOBAL2_EEPROM_DATA 0x15 +#define GLOBAL2_PTP_AVB_OP 0x16 +#define GLOBAL2_PTP_AVB_DATA 0x17 +#define GLOBAL2_SMI_OP 0x18 +#define GLOBAL2_SMI_OP_BUSY BIT(15) +#define GLOBAL2_SMI_OP_CLAUSE_22 BIT(12) +#define GLOBAL2_SMI_OP_22_WRITE ((1 << 10) | GLOBAL2_SMI_OP_BUSY | \ + GLOBAL2_SMI_OP_CLAUSE_22) +#define GLOBAL2_SMI_OP_22_READ ((2 << 10) | GLOBAL2_SMI_OP_BUSY | \ + GLOBAL2_SMI_OP_CLAUSE_22) +#define GLOBAL2_SMI_OP_45_WRITE_ADDR ((0 << 10) | GLOBAL2_SMI_OP_BUSY) +#define GLOBAL2_SMI_OP_45_WRITE_DATA ((1 << 10) | GLOBAL2_SMI_OP_BUSY) +#define GLOBAL2_SMI_OP_45_READ_DATA ((2 << 10) | GLOBAL2_SMI_OP_BUSY) +#define GLOBAL2_SMI_DATA 0x19 +#define GLOBAL2_SCRATCH_MISC 0x1a +#define GLOBAL2_WDOG_CONTROL 0x1b +#define GLOBAL2_QOS_WEIGHT 0x1c +#define GLOBAL2_MISC 0x1d struct mv88e6xxx_priv_state { /* When using multi-chip addressing, this mutex protects @@ -49,6 +239,18 @@ struct mv88e6xxx_priv_state { struct mutex eeprom_mutex; int id; /* switch product id */ + int num_ports; /* number of switch ports */ + + /* hw bridging */ + + u32 fid_mask; + u8 fid[DSA_MAX_PORTS]; + u16 bridge_mask[DSA_MAX_PORTS]; + + unsigned long port_state_update_mask; + u8 port_state[DSA_MAX_PORTS]; + + struct work_struct bridge_work; }; struct mv88e6xxx_hw_stat { @@ -57,6 +259,9 @@ struct mv88e6xxx_hw_stat { int reg; }; +int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active); +int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port); +int mv88e6xxx_setup_common(struct dsa_switch *ds); int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg); int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg); int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, @@ -65,24 +270,46 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val); int mv88e6xxx_config_prio(struct dsa_switch *ds); int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr); int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr); -int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum); -int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val); +int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum); +int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val); +int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum); +int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum, + u16 val); void mv88e6xxx_ppu_state_init(struct dsa_switch *ds); int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum); int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, int regnum, u16 val); void mv88e6xxx_poll_link(struct dsa_switch *ds); -void mv88e6xxx_get_strings(struct dsa_switch *ds, - int nr_stats, struct mv88e6xxx_hw_stat *stats, - int port, uint8_t *data); -void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, - int nr_stats, struct mv88e6xxx_hw_stat *stats, - int port, uint64_t *data); +void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data); +void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data); +int mv88e6xxx_get_sset_count(struct dsa_switch *ds); +int mv88e6xxx_get_sset_count_basic(struct dsa_switch *ds); int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port); void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, struct ethtool_regs *regs, void *_p); int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp); - +int mv88e6xxx_phy_wait(struct dsa_switch *ds); +int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds); +int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds); +int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum); +int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum, + u16 val); +int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); +int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, + struct phy_device *phydev, struct ethtool_eee *e); +int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); +int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); +int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); +int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); +int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); +int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, + unsigned char *addr, bool *is_static); +int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg); +int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, + int reg, int val); extern struct dsa_switch_driver mv88e6131_switch_driver; extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; extern struct dsa_switch_driver mv88e6352_switch_driver; |